RunLoop

// 供外部調(diào)用的公開(kāi)的CFRunLoopRun方法,其內(nèi)部會(huì)調(diào)用CFRunLoopRunSpecific
void CFRunLoopRun(void) {    /* DOES CALLOUT */
    int32_t result;
    do {
        // 調(diào)用CFRunLoopRunSpecific
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// 其內(nèi)部會(huì)調(diào)用 __CFRunLoopRun 函數(shù)
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    // 首先根據(jù)modeName找到對(duì)應(yīng)mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    // 如果沒(méi)找到 || 如果mode里沒(méi)有source/timer/observer, 直接返回
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    // 取上一次運(yùn)行的mode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    // 如果本次mode和上次的mode一致
    rl->_currentMode = currentMode;
    // 初始化一個(gè)result為kCFRunLoopRunFinished
    int32_t result = kCFRunLoopRunFinished;
    // 通知observer:即將進(jìn)入runloop
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // 核心的Loop邏輯
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 通知Observers:退出Loop
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    //記錄最后runloop狀態(tài)淮摔,用于return
    int32_t retVal = 0;
    do {
        //初始化一個(gè)存放內(nèi)核消息的緩沖池
        uint8_t msg_buffer[3 * 1024];
        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;
        //取所有需要監(jiān)聽(tīng)的port
        __CFPortSet waitSet = rlm->_portSet;
        
        //設(shè)置RunLoop為可以被喚醒狀態(tài)
        __CFRunLoopUnsetIgnoreWakeUps(rl);
        
        //2.通知observer,即將觸發(fā)timer回調(diào),處理timer事件
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        //3.通知observer段审,即將觸發(fā)Source0回調(diào)
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        
        //執(zhí)行加入當(dāng)前runloop的block
        __CFRunLoopDoBlocks(rl, rlm);
        
        //4.處理source0事件
        //有事件處理返回true犹菇,沒(méi)有事件返回false
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            //執(zhí)行加入當(dāng)前runloop的block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        //如果沒(méi)有Sources0事件處理 并且 沒(méi)有超時(shí)德迹,poll為false
        //如果有Sources0事件處理 或者 超時(shí),poll都為true
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
        
        //第一次do-while循環(huán)不會(huì)走該分支揭芍,因?yàn)閐idDispatchPortLastTime初始化是true
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
            //從緩沖區(qū)讀取消息
            msg = (mach_msg_header_t *)msg_buffer;
            //5.接收dispatchPort端口的消息胳搞,(接收source1事件)
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                //如果接收到了消息的話,前往第9步開(kāi)始處理msg
                goto handle_msg;
            }
        }
        
        didDispatchPortLastTime = false;
        
        //6.通知觀察者RunLoop即將進(jìn)入休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        //設(shè)置RunLoop為休眠狀態(tài)
        __CFRunLoopSetSleeping(rl);
        __CFPortSetInsert(dispatchPort, waitSet);
        
        __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);
        
        //這里有個(gè)內(nèi)循環(huán)称杨,用于接收等待端口的消息
        //進(jìn)入此循環(huán)后肌毅,線程進(jìn)入休眠,直到收到新消息才跳出該循環(huán)姑原,繼續(xù)執(zhí)行run loop
        do {
            //7.接收waitSet端口的消息
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
            //收到消息之后悬而,livePort的值為msg->msgh_local_port,
            if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                if (rlm->_timerFired) {
                    rlm->_timerFired = false;
                    break;
                } else {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                }
            } else {
                // Go ahead and leave the inner loop.
                break;
            }
        } while (1);
        
        __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);
        // 醒來(lái)
        __CFPortSetRemove(dispatchPort, waitSet);
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        //取消runloop的休眠狀態(tài)
        __CFRunLoopUnsetSleeping(rl);
        //8.通知觀察者runloop被喚醒
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
      
        //9.處理收到的消息
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
            //通過(guò)CFRunloopWake喚醒
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            //什么都不干锭汛,跳回2重新循環(huán)
            // do nothing on Mac OS
        }
        //如果是定時(shí)器事件
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            //9.1 處理timer事件
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        //如果是定時(shí)器事件
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
           //9.1處理timer事件
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        //如果是dispatch到main queue的block
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);

            //9.2執(zhí)行block
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            // 有source1事件待處理
            if (rls) {
                mach_msg_header_t *reply = NULL;
                //9.2 處理source1事件
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
                if (NULL != reply) {
                    (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
                }
            }
        }
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
        
        __CFRunLoopDoBlocks(rl, rlm);
        // 根據(jù)之前的執(zhí)行結(jié)果來(lái)決定怎么做笨奠,為retVal賦相應(yīng)的值
        if (sourceHandledThisLoop && stopAfterHandle) {
            //進(jìn)入RunLoop時(shí)傳入的參數(shù),處理完事件就返回
            retVal = kCFRunLoopRunHandledSource;
        }else if (timeout_context->termTSR < mach_absolute_time()) {
            //RunLoop超時(shí)
            retVal = kCFRunLoopRunTimedOut;
        }else if (__CFRunLoopIsStopped(rl)) {
            //RunLoop被手動(dòng)終止
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        }else if (rlm->_stopped) {
            //mode被終止
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        }else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            //mode中沒(méi)有要處理的事件
            retVal = kCFRunLoopRunFinished;
        }
        //除了上面這幾種情況唤殴,都繼續(xù)循環(huán)
    } while (0 == retVal);
    return retVal;
}
  • 實(shí)際上 RunLoop 內(nèi)部就是一個(gè) do-while 循環(huán)
  • 當(dāng)你調(diào)用 CFRunLoopRun() 時(shí)般婆,線程就會(huì)一直停留在這個(gè)循環(huán)里,直到超時(shí)或被手動(dòng)停止朵逝,該函數(shù)才會(huì)返回
  • 同時(shí)RunLoop有很多個(gè)mode腺兴,但是RunLoop在run的時(shí)候必須只能指定其中一個(gè)mode,運(yùn)行起來(lái)之后廉侧,被指定的mode即為currentMode

LLDB常用的調(diào)試命令页响?

po:print object的縮寫,表示顯示對(duì)象的文本描述段誊,如果對(duì)象不存在則打印nil闰蚕。
p:可以用來(lái)打印基本數(shù)據(jù)類型。
call:執(zhí)行一段代碼 如:call NSLog(@"%@", @"yang")
expr:動(dòng)態(tài)執(zhí)行指定表達(dá)式
bt:打印當(dāng)前線程堆棧信息 (bt all 打印所有線程堆棧信息)
image:常用來(lái)尋找棧地址對(duì)應(yīng)代碼位置 如:image lookup --address 0xxxx

斷點(diǎn)調(diào)試连舍?

條件斷點(diǎn)
打上斷點(diǎn)之后没陡,對(duì)斷點(diǎn)進(jìn)行編輯,設(shè)置相應(yīng)過(guò)濾條件。下面簡(jiǎn)單的介紹一下條件設(shè)置:

  1. Condition:返回一個(gè)布爾值盼玄,當(dāng)布爾值為真觸發(fā)斷點(diǎn)贴彼,一般里面我們可以寫一個(gè)表達(dá)式。
  2. Ignore:忽略前N次斷點(diǎn)埃儿,到N+1次再觸發(fā)斷點(diǎn)器仗。
  3. Action:斷點(diǎn)觸發(fā)事件,分為六種:
  • AppleScript:執(zhí)行腳本童番。
  • Capture GPU Frame:用于OpenGL ES調(diào)試精钮,捕獲斷點(diǎn)處GPU當(dāng)前繪制幀。
  • Debugger Command:和控制臺(tái)中輸入LLDB調(diào)試命令一致剃斧。
  • Log Message:輸出自定義格式信息至控制臺(tái)轨香。
  • Shell Command:接收命令文件及相應(yīng)參數(shù)列表,Shell Command是異步執(zhí)行的幼东,只有勾選“Wait until done”才會(huì)等待Shell命令執(zhí)行完在執(zhí)行調(diào)試臂容。
  • Sound:斷點(diǎn)觸發(fā)時(shí)播放聲音。
  • Options(Automatically continue after evaluating actions選項(xiàng)):選中后根蟹,表示斷點(diǎn)不會(huì)終止程序的運(yùn)行策橘。

異常斷點(diǎn)
異常斷點(diǎn)可以快速定位不滿足特定條件的異常,比如常見(jiàn)的數(shù)組越界娜亿,這時(shí)候很難通過(guò)異常信息定位到錯(cuò)誤所在位置。這個(gè)時(shí)候異常斷點(diǎn)就可以發(fā)揮作用了蚌堵。
Exception:可以選擇拋出異常對(duì)象類型:OC或C++买决。
Break:選擇斷點(diǎn)接收的拋出異常來(lái)源是Throw還是Catch語(yǔ)句。

符號(hào)斷點(diǎn)
符號(hào)斷點(diǎn)的創(chuàng)建方式和異常斷點(diǎn)一樣一樣的吼畏,在符號(hào)斷點(diǎn)中可以指定要中斷執(zhí)行的方法:
Symbol:[類名 方法名]可以執(zhí)行到指定類的指定方法中開(kāi)始斷點(diǎn)督赤。

iOS 常見(jiàn)的崩潰類型有哪些?

  • unrecognized selector crash
  • KVO crash
  • NSNotification crash
  • NSTimer crash
  • Container crash
  • NSString crash
  • Bad Access crash (野指針)
  • UI not on Main Thread Crash

造成tableView卡頓的原因有哪些泻蚊?

  1. 最常用的就是cell的重用躲舌, 注冊(cè)重用標(biāo)識(shí)符
  • 如果不重用cell時(shí),每當(dāng)一個(gè)cell顯示到屏幕上時(shí)性雄,就會(huì)重新創(chuàng)建一個(gè)新的cell
  • 如果有很多數(shù)據(jù)的時(shí)候没卸,就會(huì)堆積很多cell。
  • 如果重用cell秒旋,為cell創(chuàng)建一個(gè)ID约计,每當(dāng)需要顯示cell 的時(shí)候,都會(huì)先去緩沖池中尋找可循環(huán)利用的cell迁筛,如果沒(méi)有再重新創(chuàng)建cell
  1. 避免cell的重新布局
  • cell的布局填充等操作比較耗時(shí)煤蚌,一般創(chuàng)建時(shí)就布局好
  • 如可以將cell單獨(dú)放到一個(gè)自定義類,初始化時(shí)就布局好
  1. 提前計(jì)算并緩存cell的屬性及內(nèi)容
  • 當(dāng)我們創(chuàng)建cell的數(shù)據(jù)源方法時(shí),編譯器并不是先創(chuàng)建cell 再定cell的高度
  • 而是先根據(jù)內(nèi)容一次確定每一個(gè)cell的高度尉桩,高度確定后筒占,再創(chuàng)建要顯示的cell,滾動(dòng)時(shí)蜘犁,每當(dāng)cell進(jìn)入憑虛都會(huì)計(jì)算高度翰苫,提前估算高度告訴編譯器,編譯器知道高度后沽瘦,緊接著就會(huì)創(chuàng)建cell革骨,這時(shí)再調(diào)用高度的具體計(jì)算方法,這樣可以方式浪費(fèi)時(shí)間去計(jì)算顯示以外的cell
  1. 減少cell中控件的數(shù)量
  • 盡量使cell得布局大致相同析恋,不同風(fēng)格的cell可以使用不用的重用標(biāo)識(shí)符良哲,初始化時(shí)添加控件,
  • 不適用的可以先隱藏
  1. 不要使用ClearColor助隧,無(wú)背景色筑凫,透明度也不要設(shè)置為0
  • 渲染耗時(shí)比較長(zhǎng)
  1. 使用局部更新
  • 如果只是更新某組的話,使用reloadSection進(jìn)行局部更
  1. 加載網(wǎng)絡(luò)數(shù)據(jù)并村,下載圖片巍实,使用異步加載,并緩存
  2. 少使用addView 給cell動(dòng)態(tài)添加view
  3. 按需加載cell哩牍,cell滾動(dòng)很快時(shí)棚潦,只加載范圍內(nèi)的cell
  4. 不要實(shí)現(xiàn)無(wú)用的代理方法,tableView只遵守兩個(gè)協(xié)議
  5. 緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時(shí)存在膝昆,這兩者同時(shí)存在才會(huì)出現(xiàn)“竄動(dòng)”的bug丸边。所以我的建議是:只要是固定行高就寫預(yù)估行高來(lái)減少行高調(diào)用次數(shù)提升性能。如果是動(dòng)態(tài)行高就不要寫預(yù)估方法了荚孵,用一個(gè)行高的緩存字典來(lái)減少代碼的調(diào)用次數(shù)即可
  6. 不要做多余的繪制工作妹窖。在實(shí)現(xiàn)drawRect:的時(shí)候,它的rect參數(shù)就是需要繪制的區(qū)域收叶,這個(gè)區(qū)域之外的不需要進(jìn)行繪制骄呼。例如上例中,就可以用CGRectIntersectsRect判没、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text蜓萄,然后再調(diào)用繪制方法。
  7. 預(yù)渲染圖像澄峰。當(dāng)新的圖像出現(xiàn)時(shí)绕德,仍然會(huì)有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫一遍摊阀,導(dǎo)出成UIImage對(duì)象耻蛇,然后再繪制到屏幕踪蹬;
  8. 使用正確的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù)。

如何提升 tableview 的流暢度臣咖?

本質(zhì)上是降低 CPU跃捣、GPU 的工作,從這兩個(gè)大的方面去提升性能夺蛇。
CPU:對(duì)象的創(chuàng)建和銷毀疚漆、對(duì)象屬性的調(diào)整、布局計(jì)算刁赦、文本的計(jì)算和排版娶聘、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制
GPU:紋理的渲染

  1. 卡頓優(yōu)化在 CPU 層面
  • 盡量用輕量級(jí)的對(duì)象甚脉,比如用不到事件處理的地方丸升,可以考慮使用 CALayer 取代 UIView
  • 不要頻繁地調(diào)用 UIView 的相關(guān)屬性,比如 frame牺氨、bounds狡耻、transform 等屬性,盡量減少不必要的修改
  • 盡量提前計(jì)算好布局猴凹,在有需要時(shí)一次性調(diào)整對(duì)應(yīng)的屬性夷狰,不要多次修改屬性
  • Autolayout 會(huì)比直接設(shè)置 frame 消耗更多的 CPU 資源
  • 圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
  • 控制一下線程的最大并發(fā)數(shù)量
  • 盡量把耗時(shí)的操作放到子線程
  • 文本處理(尺寸計(jì)算、繪制)
  • 圖片處理(解碼郊霎、繪制)
  1. 卡頓優(yōu)化在 GPU層面
  • 盡量避免短時(shí)間內(nèi)大量圖片的顯示沼头,盡可能將多張圖片合成一張進(jìn)行顯示
  • GPU能處理的最大紋理尺寸是 4096x4096,一旦超過(guò)這個(gè)尺寸书劝,就會(huì)占用 CPU 資源進(jìn)行處理进倍,所以紋理盡量不要超過(guò)這個(gè)尺寸
  • 盡量減少視圖數(shù)量和層次
  • 減少透明的視圖(alpha<1),不透明的就設(shè)置 opaque 為 YES
  • 盡量避免出現(xiàn)離屏渲染

iOS 保持界面流暢的技巧庄撮?

  1. 預(yù)排版,提前計(jì)算
  • 在接收到服務(wù)端返回的數(shù)據(jù)后毙籽,盡量將 CoreText 排版的結(jié)果洞斯、單個(gè)控件的高度、cell 整體的高度提前計(jì)算好坑赡,將其存儲(chǔ)在模型的屬性中烙如。需要使用時(shí),直接從模型中往外取毅否,避免了計(jì)算的過(guò)程亚铁。
  • 盡量少用 UILabel,可以使用 CALayer 螟加。避免使用 AutoLayout 的自動(dòng)布局技術(shù)徘溢,采取純代碼的方式
  1. 預(yù)渲染吞琐,提前繪制
  • 例如圓形的圖標(biāo)可以提前在,在接收到網(wǎng)絡(luò)返回?cái)?shù)據(jù)時(shí)然爆,在后臺(tái)線程進(jìn)行處理站粟,直接存儲(chǔ)在模型數(shù)據(jù)里,回到主線程后直接調(diào)用就可以了
  • 避免使用 CALayer 的 Border曾雕、corner奴烙、shadow、mask 等技術(shù)剖张,這些都會(huì)觸發(fā)離屏渲染切诀。
  1. 異步繪制
  2. 全局并發(fā)線程
  3. 高效的圖片異步加載

APP啟動(dòng)時(shí)間應(yīng)從哪些方面優(yōu)化?

App啟動(dòng)時(shí)間可以通過(guò)xcode提供的工具來(lái)度量搔弄,在Xcode的Product->Scheme-->Edit Scheme->Run->Auguments中幅虑,將環(huán)境變量DYLD_PRINT_STATISTICS設(shè)為YES,優(yōu)化需以下方面入手

  1. dylib loading time
  • 核心思想是減少dylibs的引用
  • 合并現(xiàn)有的dylibs(最好是6個(gè)以內(nèi))
  • 使用靜態(tài)庫(kù)
  1. rebase/binding time
  • 核心思想是減少DATA塊內(nèi)的指針
  • 減少Object C元數(shù)據(jù)量肯污,減少Objc類數(shù)量翘单,減少實(shí)例變量和函數(shù)(與面向?qū)ο笤O(shè)計(jì)思想沖突)
  • 減少c++虛函數(shù)
  • 多使用Swift結(jié)構(gòu)體(推薦使用swift)
  1. ObjC setup time
  • 核心思想同上,這部分內(nèi)容基本上在上一階段優(yōu)化過(guò)后就不會(huì)太過(guò)耗時(shí)
  • initializer time
  1. 使用initialize替代load方法
  • 減少使用c/c++的attribute((constructor))蹦渣;推薦使用dispatch_once() pthread_once() std:once()等方法
  • 推薦使用swift
  • 不要在初始化中調(diào)用dlopen()方法哄芜,因?yàn)榧虞d過(guò)程是單線程,無(wú)鎖柬唯,如果調(diào)用dlopen則會(huì)變成多線程认臊,會(huì)開(kāi)啟鎖的消耗,同時(shí)有可能死鎖
  • 不要在初始化中創(chuàng)建線程

Runtime方法調(diào)用流程锄奢?

1失晴、當(dāng)調(diào)用對(duì)象方法的時(shí)候,會(huì)通過(guò)obj_object的isa指針找對(duì)對(duì)應(yīng)的歸屬類拘央。
2涂屁、從歸屬類(obj_class)類中的obj_cache中尋找對(duì)應(yīng)的相等的sel方法編號(hào)。
3灰伟、如果沒(méi)有找到拆又,繼續(xù)obj_class中的obj_method_list中查找,如果找到寫入obj_cache中栏账。
4帖族、如果沒(méi)有到找到,會(huì)一直找到它的元類上挡爵。
5竖般、如果元類也沒(méi)有的話,會(huì)調(diào)用消息動(dòng)態(tài)解析方法+resovleInstanceMethod:+resloveClassMethod:的方法茶鹃,查看是否存在綁定的方法涣雕。
6艰亮、如果沒(méi)有綁定方法,會(huì)調(diào)用消息轉(zhuǎn)發(fā)方法-forwardingTargetForSelector:的方法胞谭。查看是否存在轉(zhuǎn)發(fā)對(duì)象垃杖。
7、如果沒(méi)有存在消息轉(zhuǎn)發(fā)對(duì)象丈屹,會(huì)調(diào)用-methodSignatureForSelector:的方法调俘,查看是否有方法簽名返回類型和參數(shù)類型。
8旺垒、不存在簽名方法和類型彩库,就會(huì)來(lái)到-doseNotRecognizeSelector:方法內(nèi)部程序crash提示無(wú)法識(shí)別選擇器unrecognized selector sent to instance。
9先蒋、存在簽名的方法骇钦,就是繼續(xù)執(zhí)行-forwardInvocation:尋找IMP,沒(méi)有找到IMP竞漾,就會(huì)來(lái)到-doseNotRecognizeSelector:方法內(nèi)部程序crash提示無(wú)法識(shí)別選擇器unrecognized selector sent to instance眯搭。

@implementation Person
- (BOOL)respondsToSelector:(SEL)aSelector {
    bool a= [super respondsToSelector:aSelector];
    return a;
}
//如果方法沒(méi)有實(shí)現(xiàn),默認(rèn)返回false
//如果返回false业岁,就會(huì)走消息轉(zhuǎn)發(fā)
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    bool a = [super resolveInstanceMethod:sel];
    return a;
}
//默認(rèn)返回空
//又被稱為快速消息轉(zhuǎn)發(fā)鳞仙。
// 如果為空,走慢速消息轉(zhuǎn)發(fā)笔时,繼續(xù)轉(zhuǎn)發(fā)消息
- (id)forwardingTargetForSelector:(SEL)aSelector {
    id a = [super forwardingTargetForSelector:aSelector];
    return a;
}
// 默認(rèn)一般普通方法是返回空的棍好。
// 如果是協(xié)議方法,沒(méi)有實(shí)現(xiàn)允耿,不會(huì)反回空借笙。
//反回空,到這里就會(huì)崩潰了
//如果這里返回了簽名较锡,會(huì)再次調(diào)用resolveInstanceMethod:(SEL)sel判斷是否實(shí)現(xiàn)
//如果仍然沒(méi)有實(shí)現(xiàn)业稼,就會(huì)走到fowardInvocation:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *a = [super methodSignatureForSelector:aSelector];
    return a;
}
//默認(rèn)實(shí)現(xiàn)是崩潰
//并且不能用try-catch捕獲
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [super forwardInvocation:anInvocation];
    NSLog(@"");
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蚂蕴,隨后出現(xiàn)的幾起案子低散,更是在濱河造成了極大的恐慌,老刑警劉巖掂墓,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谦纱,死亡現(xiàn)場(chǎng)離奇詭異看成,居然都是意外死亡君编,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門川慌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吃嘿,“玉大人祠乃,你說(shuō)我怎么就攤上這事《以铮” “怎么了亮瓷?”我有些...
    開(kāi)封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)降瞳。 經(jīng)常有香客問(wèn)我嘱支,道長(zhǎng),這世上最難降的妖魔是什么挣饥? 我笑而不...
    開(kāi)封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任除师,我火速辦了婚禮,結(jié)果婚禮上扔枫,老公的妹妹穿的比我還像新娘汛聚。我一直安慰自己,他們只是感情好短荐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布倚舀。 她就那樣靜靜地躺著,像睡著了一般忍宋。 火紅的嫁衣襯著肌膚如雪痕貌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天讶踪,我揣著相機(jī)與錄音芯侥,去河邊找鬼。 笑死乳讥,一個(gè)胖子當(dāng)著我的面吹牛柱查,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播云石,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼唉工,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了汹忠?” 一聲冷哼從身側(cè)響起淋硝,我...
    開(kāi)封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宽菜,沒(méi)想到半個(gè)月后谣膳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铅乡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年继谚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阵幸。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡花履,死狀恐怖芽世,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诡壁,我是刑警寧澤济瓢,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站妹卿,受9級(jí)特大地震影響旺矾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夺克,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一宠漩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懊直,春花似錦扒吁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至融撞,卻和暖如春盼铁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尝偎。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工饶火, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人致扯。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓肤寝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抖僵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鲤看,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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