消息動態(tài)決議

前言

前面我們分析了消息的查找流程快速查找流程慢速查找流程爱榔。如果這兩種方式都沒找到方法的實現(xiàn)掉冶,蘋果給了兩個建議

  • 動態(tài)方法決議:慢速查找流程未找到后真竖,會執(zhí)行一次動態(tài)方法決議
  • 消息轉(zhuǎn)發(fā):如果動態(tài)方法決議仍然沒有找到實現(xiàn),則進行消息轉(zhuǎn)發(fā)

如果這兩個建議都沒有找到方法的實現(xiàn)厌小,就會崩潰報錯unrecognized selector sent to instance 0x600000c880d0

定義LGPerson類恢共,其中sayHello實例方法沒有實現(xiàn),main.m文件調(diào)用璧亚,崩潰如下

<!-- LGPerson.h文件 -->
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject
- (void)sayHello;
- (void)sayMaster;
@end

NS_ASSUME_NONNULL_END

<!-- LGPerson.m文件 -->
#import "LGPerson.h"

@implementation LGPerson
- (void)sayMaster {
    NSLog(@"%@ : %s",self,__func__);
}
@end
image.png

方法找不到的報錯底層

根據(jù)慢速查找源碼讨韭,我們發(fā)現(xiàn)其報錯后都是走到__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

匯編實現(xiàn)中查找__objc_forward_handler沒有找到,在源碼中去掉一個下劃線進行全局搜索_objc_forward_handler有如下實現(xiàn)透硝,本質(zhì)是調(diào)用objc_defaultForwardHandler方法

// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
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;

這就是我們?nèi)粘i_發(fā)中最常見的錯誤:沒有實現(xiàn)函數(shù)狰闪,運行程序,崩潰報錯提示濒生。探索流程總結(jié)

  • 由控制臺打印信息unrecognized selector sent to instance為出發(fā)點埋泵,向下深挖方法的響應(yīng)流程
  • 通過對LookUpImpOrForward的分析得出在慢速查找未找到的時候,給imp默認賦值forward_impobjc_msgForward_impcache
  • 通過對objc_msgForward_impcache的查找罪治,發(fā)現(xiàn)在匯編中它只是一個中間函數(shù)丽声,真正指向的是objc_msgForward
  • objc_msgForward的分析得出,方法內(nèi)調(diào)用了objc_forward_handle獲取返回值存到x17寄存器觉义,再由TailCallFunctionPointer方法跳轉(zhuǎn)到x17寄存器內(nèi)真正指向的imp地址
  • _objc_forward_handler會給定默認的實現(xiàn)objc_defaultForwardHandle雁社,當imp最終的查找流程全部走完的時候還未找到imp,那么此時就會進入objc_defaultForwardHandle函數(shù)谁撼,將錯誤信息打印出來歧胁。

為了防止方法未實現(xiàn)的崩潰,下面我們從對象方法動態(tài)決議開始探索

對象方法動態(tài)決議

慢速查找流程未找到方法實現(xiàn)時厉碟,循環(huán)查找結(jié)束喊巍,接下來進入動態(tài)方法決議

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;

    for (unsigned attempts = unreasonableClassCount();;) {

        if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
            //當消息的快速查找、慢速查找流程都走完還找不到的情況下
            //imp會給一個默認值forward_imp箍鼓,進入消息轉(zhuǎn)發(fā)流程
            imp = forward_imp;
            break;
        }
        //找到了sel對應(yīng)的imp
        if (fastpath(imp)) {
            goto done;
        }
    }
    /**
    * 如果遍歷查找的過程找到了崭参,會跳過此步驟,取到done分支款咖,進行后續(xù)操作
    * 如果找不到何暮,會進行下面這個算法,最終進入resolveMethod_locked函數(shù)
    * 此算法真正達到的目的為單例铐殃,保證一個lookUpImpOrForward
    * 只執(zhí)行一次resolveMethod_locked
    */ 
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
    return imp;
}
位運算
  • &:按位與海洼,是雙目運算符。其功能是參與運算的兩數(shù)各對應(yīng)的二進位相與富腊。相同位置數(shù)字都為1時坏逢,結(jié)果位才為1。
  • ^:按位異或赘被,比較的是二進制位是整,相同位置數(shù)字不同為1,相同為0
  • ^=:按位異或民假,比較的是二進制位浮入,相同位置數(shù)字不同為1,相同為0羊异,將結(jié)果賦值為運算符左邊事秀。
單例解讀
.macro MethodTableLookup
    
    SAVE_REGS MSGSEND

    // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
    // receiver and selector already in x0 and x1
    mov x2, x16
    mov x3, #3
    bl  _lookUpImpOrForward

    // IMP in x0
    mov x17, x0

    RESTORE_REGS MSGSEND

.endmacro
  • 前提條件(初始值) behavior = 3 LOOKUP_RESOLVER = 2
  • 初次判斷操作behavior & LOOKUP_RESOLVER = 3 & 2 = 0x11 & 0x10 = 0x10 = 2
  • 重置behaivor behavior = behavior ^ LOOKUP_RESOLVER = 3 ^ 2 = 0x11 ^ 0x10 = 0x01 = 1
  • 再次進入判斷操作(第二次至無限次)behavior & LOOKUP_RESOLVER = 1 & 2 = 0x01 & 0x10 = 0x00 = 0
得出結(jié)論

保證了每一個lookUpImpOrForward函數(shù)最多只能執(zhí)行一次resolveMethod_locked(動態(tài)方法決議)彤断,直到behavior被重新賦值

動態(tài)方法決議源碼

調(diào)用一個方法流程,先進入消息快速查詢流程 -> 然后消息慢速查找流程秽晚,當?shù)讓釉创a已經(jīng)查找了兩遍依然找不到方法的實現(xiàn)瓦糟,此時imp=nil,理論上來講程序應(yīng)該崩潰赴蝇。但在開發(fā)者的角度菩浙,崩潰表示這個框架不穩(wěn)定,系統(tǒng)很不友善句伶。所以此框架決定再給你一次機會劲蜻,為你提供一個返回自定義imp的機會,resolveMethod_locked函數(shù)就是動態(tài)消息轉(zhuǎn)發(fā)的入口考余。

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    //對象 -- 類
    if (! cls->isMetaClass()) { //類不是元類先嬉,調(diào)用對象的解析方法
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {//如果是元類,調(diào)用類的解析方法楚堤, 類 -- 元類
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        //為什么要有這行代碼疫蔓? -- 類方法在元類中是對象方法,所以還是需要查詢元類中對象方法的動態(tài)方法決議
        if (!lookUpImpOrNil(inst, sel, cls)) { //如果沒有找到或者為空身冬,在元類的對象方法解析方法中查找
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    //如果方法解析中將其實現(xiàn)指向其他方法衅胀,則繼續(xù)走方法查找流程
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}

該函數(shù)內(nèi)的三個關(guān)鍵函數(shù)

  • resolveInstanceMethod:實例方法動態(tài)添加imp
  • resolveClassMethod:類方法動態(tài)添加imp
  • lookUpImpOrForwardTryCache,當完成添加之后酥筝,回到之前的慢速查找流程再來一遍
類動態(tài)方法決議會進入resolveClassMethod滚躯,然后根據(jù)判斷有可能會再次進入resolveInstanceMethod為什么?

正常的類對象動態(tài)方法決議會進入resolveClassMethod嘿歌,這點是毋庸置疑的掸掏,但是類方法查找過程是在元類中查找,那么通過isa的指向圖中可以得知宙帝,類方法的查找過程也是有繼承關(guān)系的丧凤,會一直向上找,找到superMetaClass步脓,找到rootMetaClass息裸,最終找到NSObject,到這一層所有的方法對于NSObject來講都是實例方法所以會調(diào)用resolveInstanceMethod沪编。

實例方法源碼

針對實例方法調(diào)用,在快速-慢速查找均沒有找到實例方法的實現(xiàn)時年扩,我們有一次挽救的機會即嘗試一次動態(tài)方法決議蚁廓,由于是實例方法,所以會走到resolveInstanceMethod方法

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);
    
    // look的是 resolveInstanceMethod --相當于是發(fā)送消息前的容錯處理
    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel); //發(fā)送resolve_sel消息

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    //查找say666
    IMP imp = lookUpImpOrNil(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

主要步驟如下

  • 在發(fā)送resolveInstanceMethod消息前厨幻,需要查找cls類中是否有該方法的實現(xiàn)相嵌,即通過lookUpImpOrNil方法又會進入lookUpImpOrForward慢速查找流程查找resolveInstanceMethod方法
    如果沒有直接返回
    如果有則發(fā)送resolveInstanceMethod消息
  • 再次慢速查找實例方法的實現(xiàn)腿时,即通過lookUpImpOrNil方法又會進入lookUpImpOrForward慢速查找流程查找實例方法
崩潰修改

針對實例方法sayHello未實現(xiàn)的報錯,可以通過在類中重寫resolveInstanceMethod類方法饭宾,并將其指向其他方法的實現(xiàn)批糟。即在LGPerson中重寫resolveInstanceMethod類方法,將實例方法sayHello的實現(xiàn)指向sayMaster方法實現(xiàn)看铆,如下所示

// LGPerson.m 文件添加如下resolveInstanceMethod方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(sayHello)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        //獲取sayMaster方法的imp
        IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
        //獲取sayMaster的實例方法
        Method sayMethod  = class_getInstanceMethod(self, @selector(sayMaster));
        //獲取sayMaster的豐富簽名
        const char *type = method_getTypeEncoding(sayMethod);
        //將sel的實現(xiàn)指向sayMaster
        return class_addMethod(self, sel, imp, type);
    }
    return [super resolveInstanceMethod:sel];
}

// 運行工程其打印如下
2021-08-01 17:51:30.226277+0800 DDDDD[5106:762939] sayHello 來了
2021-08-01 17:51:30.226734+0800 DDDDD[5106:762939] <LGPerson: 0x281ecc040> : -[LGPerson sayMaster]

類方法的動態(tài)決議

類方法動態(tài)決議源碼

/*********************************************************
* 解析類方法
* 調(diào)用+resolveClass 方法徽鼎,尋找要添加到類cls 的方法。
* cls 應(yīng)該是一個元類弹惦。
* 不檢查該方法是否已經(jīng)存在否淤。
*********************************************************/
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    //當你為實現(xiàn)resolveClassMethod的時候,此處也不會進入return
    //因為系統(tǒng)給resolveClassMethod函數(shù)默認返回NO棠隐,即默認實現(xiàn)了
    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }
    
    //nonmeta容錯處理
    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    //系統(tǒng)會在此處為你發(fā)送一個消息resolveClassMethod
    //當你的這個類檢測了這個消息石抡,并且做了處理
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    //那么此時系統(tǒng)會重新查找,此函數(shù)最終會觸發(fā)LookUpImpOrForward
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

LGPerson類中添加類方法如下助泽,其中sayHappy方法沒有實現(xiàn)

<!-- LGPerson.h文件 -->
+ (void)sayNB;
+ (void)sayHappy;

<!-- LGPerson.m文件 -->
+ (void)sayNB {
    NSLog(@"%@ : %s",self,__func__);
}

// 添加類方法動態(tài)決議
+ (BOOL)resolveClassMethod:(SEL)sel{
    if (sel == @selector(sayHappy)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        
        IMP imp = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(sayNB));
        Method lgClassMethod  = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(sayNB));
        const char *type = method_getTypeEncoding(lgClassMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return [super resolveClassMethod:sel];
}

// 運行工程其打印如下
2021-08-01 18:46:27.239762+0800 DDDDD[5295:774648] sayHappy 來了
2021-08-01 18:46:27.239814+0800 DDDDD[5295:774648] LGPerson : +[LGPerson sayNB]

注意: resolveClassMethod類方法的重寫傳入的cls不再是類啰扛,而是元類∥撕兀可以通過objc_getMetaClass方法獲取類的元類隐解,原因是類方法在元類中是實例方法

得出結(jié)論
  • 通過手動添加resolveInstanceMethod/resolveClassMethod可以防止方法未實現(xiàn)崩潰
  • 通過判斷當前查找的sel動態(tài)添加一個新的imp
  • 此時sel(sayHello/sayHappy)對應(yīng)實現(xiàn)的imp不再是sayHello/sayHappy,而是變成了我動態(tài)指定的sayMaster/sayNB
優(yōu)化

日常開發(fā)中不可能每個類中都實現(xiàn)一份resolveInstanceMethod/resolveClassMethod暑刃,有沒有更好的解決方案呢厢漩?通過方法慢速查找流程可以發(fā)現(xiàn)其查找路徑有兩條

  • 實例方法:類 -- 父類 -- 根類 -- nil
  • 類方法:元類 -- 根元類 -- 根類 -- nil

它們的共同點是如果前面沒找到,都會來到根類即NSObject中查找岩臣,所以我們可以將上述的兩個方法整合在一起溜嗜。通過NSObject添加分類的方式來統(tǒng)一處理,而且由于類方法的查找在其繼承鏈中查找的也是實例方法架谎,所以可以將實例方法和類方法統(tǒng)一放在resolveInstanceMethod方法中處理炸宵,如下所示

@implementation NSObject (LG)

//為實例對象創(chuàng)建一個IMP
- (void) sayMaster {
    NSLog(@"%@ : %s",self,__func__);
}

//為元類對象創(chuàng)建創(chuàng)建一個IMP
+ (void) sayNB {
    NSLog(@"%@ - %s",self,__func__);
}

#pragma clang diagnostic push
// 讓編譯器忽略錯誤
#pragma clang diagnostic ignored "-Wundeclared-selector"

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(sayHello)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
        Method sayMethod  = class_getInstanceMethod(self, @selector(sayMaster));
        const char *type = method_getTypeEncoding(sayMethod);
        return class_addMethod(self, sel, imp, type);
    } else if (sel == @selector(sayHappy)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        IMP imp = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(sayNB));
        Method lgClassMethod  = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(sayNB));
        const char *type = method_getTypeEncoding(lgClassMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return NO;
}
#pragma clang diagnostic pop
@end

// 運行工程其打印如下
2021-08-01 19:20:31.446141+0800 DDDDD[5405:783621] sayHello 來了
2021-08-01 19:20:31.446775+0800 DDDDD[5405:783621] <LGPerson: 0x2838f8080> : -[NSObject(LG) sayMaster]
2021-08-01 19:20:31.446847+0800 DDDDD[5405:783621] sayHappy 來了
2021-08-01 19:20:31.446881+0800 DDDDD[5405:783621] LGPerson - +[NSObject(LG) sayNB]

這種方式的實現(xiàn),正好與源碼中針對類方法的處理邏輯是一致的谷扣,即完美闡述為什么調(diào)用了類方法動態(tài)方法決議土全,還要調(diào)用對象方法動態(tài)方法決議,其根本原因還是類方法在元類中的實例方法会涎。
當然裹匙,上面這種寫法還是會有其他的問題,比如系統(tǒng)方法也會被更改末秃,針對這一點是可以優(yōu)化的概页,即我們可以針對自定義類中方法統(tǒng)一方法名的前綴,根據(jù)前綴來判斷是否是自定義方法练慕,然后統(tǒng)一處理自定義方法惰匙,例如可以在崩潰前pop到首頁技掏,主要是用于app線上防崩潰的處理,提升用戶的體驗项鬼。

aop和oop總結(jié)

oop面向?qū)ο缶幊?/code>哑梳,不同類做不同事情,分工明確绘盟。
  • 優(yōu)點:耦合度很低
  • 缺點:代碼冗余鸠真,常規(guī)解決辦法是提取,這樣會有一個公共類奥此。所有人對公共類進行集成弧哎,形成強依賴,也就代表著出現(xiàn)了強耦合
aop:面向切面編程稚虎,是oop的延伸
  • 優(yōu)點:對業(yè)務(wù)無侵入撤嫩,通過動態(tài)方式將某些方法進行注入
  • 缺點:做了一些判斷,執(zhí)行了很多無關(guān)代碼蠢终,包括系統(tǒng)方法序攘,造成性能消耗,會打斷apple的方法動態(tài)轉(zhuǎn)發(fā)流程寻拂。

消息轉(zhuǎn)發(fā)流程引入

在慢速查找的流程中程奠,我們了解到如果快速+慢速沒有找到方法實現(xiàn),動態(tài)方法決議也不行祭钉,就使用消息轉(zhuǎn)發(fā)瞄沙。但是我們找遍了源碼也沒有發(fā)現(xiàn)消息轉(zhuǎn)發(fā)的相關(guān)源碼,可以通過以下方式來了解慌核,方法調(diào)用崩潰前都走了哪些方法

  • 通過instrumentObjcMessageSends方式打印發(fā)送消息的日志

objc4-818.2源碼全局搜索instrumentObjcMessageSends實現(xiàn)如下

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable)
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if (objcMsgLogFD != -1)
        fsync (objcMsgLogFD);

    objcMsgLogEnabled = enable;
}

探索instrumentObjcMessageSends流程

  • 通過lookUpImpOrForward --> log_and_fill_cache --> logMessageSend距境,在logMessageSend源碼下方找到instrumentObjcMessageSends的源碼實現(xiàn)
  • main中調(diào)用instrumentObjcMessageSends打印方法調(diào)用的日志信息,有以下兩點準備
  1. 打開objcMsgLogEnabled開關(guān)垮卓,即調(diào)用instrumentObjcMessageSends方法時傳入YES
  2. main中通過extern聲明instrumentObjcMessageSends方法
extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        //使用區(qū)域垫桂,將想要查找的方法用instrumentObjcMessageSends圈起來
        //開始監(jiān)聽傳遞參數(shù)為YES,結(jié)束監(jiān)聽傳遞參數(shù)為NO
        instrumentObjcMessageSends(YES);
        [person sayHello];
        // 這里再次賦值為NO粟按,表示只查看sayHello方法
        instrumentObjcMessageSends(NO);
    }
    return 0;
}
  • 通過logMessageSend源碼了解到消息發(fā)送打印信息存儲在/tmp/msgSends目錄
bool logMessageSend(bool isClassMethod,
                    const char *objectsClass,
                    const char *implementingClass,
                    SEL selector)
{
    char    buf[ 1024 ];

    // Create/open the log file
    if (objcMsgLogFD == (-1))
    {
        snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
        objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
        if (objcMsgLogFD < 0) {
            // no log file - disable logging
            objcMsgLogEnabled = false;
            objcMsgLogFD = -1;
            return true;
        }
    }

    // Make the log entry
    snprintf(buf, sizeof(buf), "%c %s %s %s\n",
            isClassMethod ? '+' : '-',
            objectsClass,
            implementingClass,
            sel_getName(selector));

    objcMsgLogLock.lock();
    write (objcMsgLogFD, buf, strlen(buf));
    objcMsgLogLock.unlock();

    // Tell caller to not cache the method
    return false;
}
  • 運行代碼并前往/tmp/msgSends目錄诬滩,發(fā)現(xiàn)有msgSends開頭的日志文件,打開發(fā)現(xiàn)在崩潰前執(zhí)行了以下方法
    兩次動態(tài)方法決議:resolveInstanceMethod方法
    兩次消息快速轉(zhuǎn)發(fā):forwardingTargetForSelector方法
    兩次消息慢速轉(zhuǎn)發(fā):methodSignatureForSelector + resolveInvocation
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末灭将,一起剝皮案震驚了整個濱河市疼鸟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庙曙,老刑警劉巖空镜,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡姑裂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門男旗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舶斧,“玉大人,你說我怎么就攤上這事察皇≤罾鳎” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵什荣,是天一觀的道長矾缓。 經(jīng)常有香客問我,道長稻爬,這世上最難降的妖魔是什么嗜闻? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮桅锄,結(jié)果婚禮上琉雳,老公的妹妹穿的比我還像新娘。我一直安慰自己友瘤,他們只是感情好翠肘,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辫秧,像睡著了一般束倍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盟戏,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天绪妹,我揣著相機與錄音,去河邊找鬼抓半。 笑死喂急,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的笛求。 我是一名探鬼主播廊移,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼探入!你這毒婦竟也來了狡孔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蜂嗽,失蹤者是張志新(化名)和其女友劉穎苗膝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體植旧,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡辱揭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年离唐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片问窃。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡亥鬓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出域庇,到底是詐尸還是另有隱情嵌戈,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布听皿,位于F島的核電站熟呛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尉姨。R本人自食惡果不足惜庵朝,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啊送。 院中可真熱鬧偿短,春花似錦、人聲如沸馋没。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篷朵。三九已至勾怒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間声旺,已是汗流浹背笔链。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腮猖,地道東北人鉴扫。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像澈缺,于是被迫代替她去往敵國和親坪创。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容