7、iOSの基礎となる分析 - ニュースの検索プロセス

フロー法を探します

  1. オブジェクトのテスト方法
    1. オブジェクトのメソッドの例 - 彼らが持っています
    2. オブジェクトのメソッドの例としては、 - なかった - 親クラスの外観を
    3. オブジェクトのメソッドの例 - それはなかったん - 親 - 親クラスの親クラスを見つけるために - NSObjectの
    4. オブジェクトのメソッドの例はありません - それはなかったん - 親 - 親クラスの親クラスを見つけるために - NSObjectの無 - 崩壊
    5. 概要:SelfClass - >スーパークラス - > ...-> NSObjectの - > nilに
  2. テストのクラスメソッド
    1. クラスメソッド - 持っています
    2. クラスメソッド - 私たちはしませんでした - 親クラスを持っています
    3. クラスメソッド - 私はしませんでしたしない - 親 - 親クラスの親 - NSObjectのを
    4. クラスメソッドは - 彼はそうしなかったん - 親 - 親クラスの親クラス - NSObjectの何を - 崩壊
    5. クラスメソッド - 彼はそうしなかったん - 親 - 親クラスの親クラスを - NSObjectのはしませんでした - しかし、オブジェクトメソッド、オブジェクトのメソッド呼び出しがあります
    6. 总结:自己メタクラス - >スーパークラスMeta - > ... - > NSObjectMetaClass-> NSObjectの - > nilに
    7. 例えばLGPersonはNSObjectのインスタンスのメソッドを呼び出すように、この点を有する、方法は、メタクラスの方法でフォームクラスのインスタンス(オブジェクト)であり、これも可能です。
  3. 動的なメソッド解決
  4. メッセージ転送
  5. そうでない場合は、エラークラッシュ
    1. 認識されていないセレクタはインスタンスに送信しました
2020-01-20 21:03:41.414708+0800 LGTest[1213:36305] 
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[LGPerson sayHello]: unrecognized selector sent to instance 0x101848d30'

プロセス分析

objc_msgSendまずキャッシュはすぐにない場合は、低速のルーチンを探し、見つけるために

クイックルックは今のために、メッセージに前回の分析プロセスを送信するプロセスである_class_lookupMethodAndLoadCache3通常の検索プロセス

objc_calss_old.mm 中
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{        
    return lookUpImpOrForward(cls, sel, obj, 
                              YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}

lookUpImpOrForward分析

この方法は非常に重要であり、私たちは忘れてはなりません

  • まず、製剤のシリーズオブジェクトのメソッドやクラスメソッドが見つからない場合ことを確実にするために、または私たちは再帰のシリーズを見つけるために行くように続けることができ、親クラスのメタクラスを持つことができます

素早く見つけるobcj_msgSendでは見られないスロー検索は、のような遅い表情があることが必要です

  • IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
    
        runtimeLock.assertUnlocked();
    
        传进来的cache 是NO 所以不用看这一部分
        接下来是判断是否是非法的类,判断也不用看
    
        if (!cls->isRealized()) {
            realizeClass(cls);
            准备条件。如果类没有实现,需要重现实现。准备类(包括父类和元类)
        }
    
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlock();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.lock();
            // If sel == initialize, _class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
    
        
     retry:    
        runtimeLock.assertLocked();
    
        // Try this class's cache.
    
        imp = cache_getImp(cls, sel);
        if (imp) goto done;
    
    下边加括号的目的是 实现局部作用域,两个作用域内的变量名重复不会冲突
        // Try this class's method lists.
        {
            去当前类的method_list 中查找方法
            Method meth = getMethodNoSuper_nolock(cls, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);
                imp = meth->imp;
                goto done;
            }
        }
    
        // Try superclass caches and method lists.
        {
            当前类的父类中的查找方法
            unsigned attempts = unreasonableClassCount();
            for (Class curClass = cls->superclass;
                 curClass != nil;
                 curClass = curClass->superclass)
            {
                // Halt if there is a cycle in the superclass chain.
                if (--attempts == 0) {
                    _objc_fatal("Memory corruption in class list.");
                }
                
                // Superclass cache.
                imp = cache_getImp(curClass, sel);
                if (imp) {
                    if (imp != (IMP)_objc_msgForward_impcache) {
                        // Found the method in a superclass. Cache it in this class.
                        log_and_fill_cache(cls, imp, sel, inst, curClass);
                        goto done;
                    }
                    else {
                        // Found a forward:: entry in a superclass.
                        // Stop searching, but don't cache yet; call method 
                        // resolver for this class first.
                        break;
                    }
                }
                
                // Superclass method list.
                Method meth = getMethodNoSuper_nolock(curClass, sel);
                if (meth) {
                    log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                    imp = meth->imp;
                    goto done;
                }
            }
        }
    
        // No implementation found. Try method resolver once.
    
        if (resolver  &&  !triedResolver) {
            runtimeLock.unlock();
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.lock();
            // Don't cache the result; we don't hold the lock so it may have 
            // changed already. Re-do the search from scratch instead.
            triedResolver = YES;
            goto retry;
        }
    
        // No implementation found, and method resolver didn't help. 
        // Use forwarding.
    
        imp = (IMP)_objc_msgForward_impcache;
        cache_fill(cls, sel, imp, inst);
    
     done:
        runtimeLock.unlock();
    
        return imp;
    }

クラス(親クラスとメタクラスを含む)を調製し、次いで、親クラスまたはクラスがmethod_list方法をルックアップ。まず、現在のIMPがキャッシュされていないかを判断、への直接の呼び出しがある場合、キャッシュ(この時間はキャッシュに来ている場合があります)

採択に続きgetMethodNoSuper_nolockの見つけるために、メソッドリスト

static Class realizeClass(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;
   。。。。。。
    
    supercls = realizeClass(remapClass(cls->superclass));  递归实现父类
    metacls = realizeClass(remapClass(cls->ISA()));        递归实现元类

#if SUPPORT_NONPOINTER_ISA
。。。。。。
// SUPPORT_NONPOINTER_ISA
#endif
    。。。。。
    // Attach categories
    methodizeClass(cls);

    return cls;
}

getMethodNoSuper_nolock

getMethodNoSuper_nolock内部メソッドによって  メソッド   リストは、現在の検索横断されるSEL対応する方法を、見つからなかった場合、それはゼロを返します

static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    assert(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

で  search_method_list   手続き後、によって  findMethodInSortedMethodList(SEL、mlist)   保証バイナリ検索は、すばやく目的の方法を見つけることができます。

特定のバイナリサーチコード

// 关键二分查找代码
for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)probe->name;
        
        if (keyValue == probeValue) {
            // `probe` is a match.
            // Rewind looking for the *first* occurrence of this value.
            // This is required for correct category overrides.
            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                probe--;
            }
            return (method_t *)probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }

検索method_t次のステップに進み見つからない場合は、リターンを、次に見つけcache_fillをキャッシュします

この方法は、入力し、発見されたlog_and_fill_cache内部で呼び出されるメソッド、cache_fill入力した後、キャッシュ処理には後藤が行われ

のプロセスを続行して下さい

彼がいなかった場合は1)、親クラスを検索します。

2)、親キャッシュ表情がIMP = cache_getImp(CLS、SEL)

その後、親メソッドメタ= getMethodNoSuper_nolock(CLS、SEL)のリストを確認する方法はありません

3)親クラスが見つからない場合、メソッドはクラスの親クラスのメタクラスに行きます

クラスcurClass = CLS - >スーパークラス(親クラスの親クラスのメタクラスはメタクラスです)

同上の検索

4)親のメタクラスのクラスはなく、見つけて、NSObjectのを見つけることで行ってきましたができなかった場合

static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
    if (objcMsgLogEnabled) {
        bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                      cls->nameForLogging(),
                                      implementer->nameForLogging(), 
                                      sel);
        if (!cacheIt) return;
    }
#endif
    cache_fill (cls, sel, imp, receiver);
}

上記処理方法は未だ見出されていない場合は、動的方法解像度実施する  _class_resolveMethodは、プロセスにおいて、システムは、それが既に存在している2つの事前定義されたクラスのメソッドをコールフォールトトレランスのための機会を提供するであろう。ベンの崩壊を見つけることができない方法をここで処理することができますので、あなたがプログラムをさせたくない場合は、エラーメッセージなどを印刷することができます。

// objc源码
_class_resolveInstanceMethod(cls, sel, inst)
_class_resolveClassMethod(cls, sel, inst)

// NSObject内部方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

これらの2つの方法のシステムは何も処理を行う必要はありませんでした場合は、メッセージ転送、グローバルな舞台の検索方法を転送するメッセージの段階に入ります。呼ばれる彼らのコンパイル方法、内__objc_msgForward

imp = (IMP)_objc_msgForward_impcache;
    STATIC_ENTRY __objc_msgForward_impcache

    // No stret specialization.
    b   __objc_msgForward

    END_ENTRY __objc_msgForward_impcache

    ENTRY __objc_msgForward

    adrp    x17, __objc_forward_handler@PAGE
    ldr p17, [x17, __objc_forward_handler@PAGEOFF]
    TailCallFunctionPointer x17
    
    END_ENTRY __objc_msgForward

グローバル検索__objc_forward_handler(あなたがアンダースコアを削除したいC ++でコンパイル)、見つかったobjc_defaultForwardHandlerの   メソッドの実装を 

objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

何も契約した場合、エラーメソッドが崩壊する見つけることができませんでした

2020-01-20 21:03:41.414708+0800 LGTest[1213:36305] 
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[LGPerson sayHello]: unrecognized selector sent to instance 0x101848d30'

概要

  • その後、クラスの従来の二分法と親クラスのメソッドのリストを見て後、ファストキャッシュobjc_msgSendによる方法、最初の発見を探します
  • メソッドが見つける必要がなかった場合は最後に、あなたが最初の動的メソッド解決を入力して、早送りやメッセージ転送ルーチンを入力した後、メッセージが失敗したエラーのクラッシュを処理した後に転送されます

 

拡張:

それが失敗した場合の方法は崩壊し、時には我々はクラッシュしたくないメッセージの転送メカニズムによって処理することができます

ゆっくりとしたプロセスを見つけるための高速な方法へのプロセスフロー

メッセージ転送機構はまた、高速トラック遅いプロセスに分割されています

 

 

 

 

 

 

 

 

 

 

 

公開された83元の記事 ウォン称賛12 ビュー180 000 +

おすすめ

転載: blog.csdn.net/shengdaVolleyball/article/details/104056265