摘自: ? http://www.cocoachina.com/ios/20150825/13195.html
25. _objc_msgForward函數(shù)是做什么的八孝,直接調(diào)用它將會(huì)發(fā)生什么藐翎?
_objc_msgForward是 IMP 類(lèi)型塑荒,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息,但它并沒(méi)有實(shí)現(xiàn)的時(shí)候俗冻,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)魄健。
我們可以這樣創(chuàng)建一個(gè)_objc_msgForward對(duì)象:
1
IMP?msgForwardIMP?=?_objc_msgForward;
在上篇中的《objc中向一個(gè)對(duì)象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?》曾提到objc_msgSend在“消息傳遞”中的作用旋奢。在“消息傳遞”過(guò)程中泳挥,objc_msgSend的動(dòng)作比較清晰:首先在 Class 中的緩存查找 IMP (沒(méi)緩存則初始化緩存),如果沒(méi)找到至朗,則向父類(lèi)的 Class 查找屉符。如果一直查找到根類(lèi)仍舊沒(méi)有實(shí)現(xiàn),則用_objc_msgForward函數(shù)指針代替 IMP 锹引。最后矗钟,執(zhí)行這個(gè) IMP 。
Objective-C運(yùn)行時(shí)是開(kāi)源的粤蝎,所以我們可以看到它的實(shí)現(xiàn)真仲。打開(kāi)Apple Open Source 里Mac代碼里的obj包下載一個(gè)最新版本,找到 objc-runtime-new.mm初澎,進(jìn)入之后搜索_objc_msgForward秸应。
里面有對(duì)_objc_msgForward的功能解釋?zhuān)?/p>
1
2
3
4
5
6
7
8
9
10
11
12/***********************************************************************
*?lookUpImpOrForward.
*?The?standard?IMP?lookup.
*?initialize==NO?tries?to?avoid?+initialize?(but?sometimes?fails)
*?cache==NO?skips?optimistic?unlocked?lookup?(but?uses?cache?elsewhere)
*?Most?callers?should?use?initialize==YES?and?cache==YES.
*?inst?is?an?instance?of?cls?or?a?subclass?thereof,?or?nil?if?none?is?known.
*???If?cls?is?an?un-initialized?metaclass?then?a?non-nil?inst?is?faster.
*?May?return?_objc_msgForward_impcache.?IMPs?destined?for?external?use
*???must?be?converted?to?_objc_msgForward?or?_objc_msgForward_stret.
*???If?you?don't?want?forwarding?at?all,?use?lookUpImpOrNil()?instead.
**********************************************************************/
對(duì) objc-runtime-new.mm文件里與_objc_msgForward有關(guān)的三個(gè)函數(shù)使用偽代碼展示下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35//??objc-runtime-new.mm?文件里與?_objc_msgForward?有關(guān)的三個(gè)函數(shù)使用偽代碼展示
//??Created?byhttps://github.com/ChenYilong
//??Copyright?(c)??微博@iOS程序犭袁(http://weibo.com/luohanchenyilong/).?All?rights?reserved.
//??同時(shí),這也是?obj_msgSend?的實(shí)現(xiàn)過(guò)程
id?objc_msgSend(id?self,?SEL?op,?...)?{
if(!self)returnnil;
IMP?imp?=?class_getMethodImplementation(self->isa,?SEL?op);
imp(self,?op,?...);//調(diào)用這個(gè)函數(shù)碑宴,偽代碼...
}
//查找IMP
IMP?class_getMethodImplementation(Class?cls,?SEL?sel)?{
if(!cls?||?!sel)returnnil;
IMP?imp?=?lookUpImpOrNil(cls,?sel);
if(!imp)return_objc_msgForward;//_objc_msgForward?用于消息轉(zhuǎn)發(fā)
returnimp;
}
IMP?lookUpImpOrNil(Class?cls,?SEL?sel)?{
if(!cls->initialize())?{
_class_initialize(cls);
}
Class?curClass?=?cls;
IMP?imp?=?nil;
do{//先查緩存,緩存沒(méi)有時(shí)重建,仍舊沒(méi)有則向父類(lèi)查詢(xún)
if(!curClass)break;
if(!curClass->cache)?fill_cache(cls,?curClass);
imp?=?cache_getImp(curClass,?sel);
if(imp)break;
}while(curClass?=?curClass->superclass);
returnimp;
}
雖然Apple沒(méi)有公開(kāi)_objc_msgForward的實(shí)現(xiàn)源碼软啼,但是我們還是能得出結(jié)論:
_objc_msgForward是一個(gè)函數(shù)指針(和 IMP 的類(lèi)型一樣),是用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息延柠,但它并沒(méi)有實(shí)現(xiàn)的時(shí)候祸挪,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)。
在上篇中的《objc中向一個(gè)對(duì)象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系贞间?》曾提到objc_msgSend在“消息傳遞”中的作用贿条。在“消息傳遞”過(guò)程中雹仿,objc_msgSend的動(dòng)作比較清晰:首先在 Class 中的緩存查找 IMP (沒(méi)緩存則初始化緩存),如果沒(méi)找到整以,則向父類(lèi)的 Class 查找胧辽。如果一直查找到根類(lèi)仍舊沒(méi)有實(shí)現(xiàn),則用_objc_msgForward函數(shù)指針代替 IMP 公黑。最后邑商,執(zhí)行這個(gè) IMP 。
為了展示消息轉(zhuǎn)發(fā)的具體動(dòng)作凡蚜,這里嘗試向一個(gè)對(duì)象發(fā)送一條錯(cuò)誤的消息人断,并查看一下_objc_msgForward是如何進(jìn)行轉(zhuǎn)發(fā)的。
首先開(kāi)啟調(diào)試模式朝蜘、打印出所有運(yùn)行時(shí)發(fā)送的消息: 可以在代碼里執(zhí)行下面的方法:
1
(void)instrumentObjcMessageSends(YES);
或者斷點(diǎn)暫停程序運(yùn)行恶迈,并在 gdb 中輸入下面的命令:
1
call?(void)instrumentObjcMessageSends(YES)
以第二種為例,操作如下所示:
之后谱醇,運(yùn)行時(shí)發(fā)送的所有消息都會(huì)打印到/tmp/msgSend-xxxx文件里了蝉绷。
終端中輸入命令前往:
1
open?/private/tmp
可能看到有多條,找到最新生成的枣抱,雙擊打開(kāi)
在模擬器上執(zhí)行執(zhí)行以下語(yǔ)句(這一套調(diào)試方案僅適用于模擬器,真機(jī)不可用辆床,關(guān)于該調(diào)試方案的拓展鏈接:Can the messages sent to an object in Objective-C be monitored or printed out?)佳晶,向一個(gè)對(duì)象發(fā)送一條錯(cuò)誤的消息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//
//??main.m
//??CYLObjcMsgForwardTest
//
//??Created?byhttp://weibo.com/luohanchenyilong/.
//??Copyright?(c)?2015年?微博@iOS程序犭袁.?All?rights?reserved.
//
#import?#import?"AppDelegate.h"
#import?"CYLTest.h"
int?main(int?argc,?char?*?argv[])?{
@autoreleasepool?{
CYLTest?*test?=?[[CYLTest?alloc]?init];
[test?performSelector:(@selector(iOS程序犭袁))];
returnUIApplicationMain(argc,?argv,?nil,?NSStringFromClass([AppDelegate?class]));
}
}
你可以在/tmp/msgSend-xxxx(我這一次是/tmp/msgSend-9805)文件里,看到打印出來(lái):
1
2
3
4
5
6
7
8
9
10
11
12
13
14+?CYLTest?NSObject?initialize
+?CYLTest?NSObject?alloc
-?CYLTest?NSObject?init
-?CYLTest?NSObject?performSelector:
+?CYLTest?NSObject?resolveInstanceMethod:
+?CYLTest?NSObject?resolveInstanceMethod:
-?CYLTest?NSObject?forwardingTargetForSelector:
-?CYLTest?NSObject?forwardingTargetForSelector:
-?CYLTest?NSObject?methodSignatureForSelector:
-?CYLTest?NSObject?methodSignatureForSelector:
-?CYLTest?NSObject?class
-?CYLTest?NSObject?doesNotRecognizeSelector:
-?CYLTest?NSObject?doesNotRecognizeSelector:
-?CYLTest?NSObject?class
結(jié)合《NSObject官方文檔》讼载,排除掉 NSObject 做的事轿秧,剩下的就是_objc_msgForward消息轉(zhuǎn)發(fā)做的幾件事:
調(diào)用resolveInstanceMethod:方法 (或 resolveClassMethod:)。允許用戶(hù)在此時(shí)為該 Class 動(dòng)態(tài)添加實(shí)現(xiàn)咨堤。如果有實(shí)現(xiàn)了菇篡,則調(diào)用并返回YES,那么重新開(kāi)始o(jì)bjc_msgSend流程一喘。這一次對(duì)象會(huì)響應(yīng)這個(gè)選擇器驱还,一般是因?yàn)樗呀?jīng)調(diào)用過(guò)class_addMethod。如果仍沒(méi)實(shí)現(xiàn)凸克,繼續(xù)下面的動(dòng)作议蟆。
調(diào)用forwardingTargetForSelector:方法,嘗試找到一個(gè)能響應(yīng)該消息的對(duì)象萎战。如果獲取到咐容,則直接把消息轉(zhuǎn)發(fā)給它,返回非 nil 對(duì)象蚂维。否則返回 nil 戳粒,繼續(xù)下面的動(dòng)作路狮。注意,這里不要返回 self 蔚约,否則會(huì)形成死循環(huán)奄妨。
調(diào)用methodSignatureForSelector:方法,嘗試獲得一個(gè)方法簽名炊琉。如果獲取不到展蒂,則直接調(diào)用doesNotRecognizeSelector拋出異常。如果能獲取苔咪,則返回非nil:創(chuàng)建一個(gè) NSlnvocation 并傳給forwardInvocation:锰悼。
調(diào)用forwardInvocation:方法,將第3步獲取到的方法簽名包裝成 Invocation 傳入团赏,如何處理就在這里面了箕般,并返回非ni。
調(diào)用doesNotRecognizeSelector: 舔清,默認(rèn)的實(shí)現(xiàn)是拋出異常丝里。如果第3步?jīng)]能獲得一個(gè)方法簽名,執(zhí)行該步驟体谒。
上面前4個(gè)方法均是模板方法杯聚,開(kāi)發(fā)者可以override,由 runtime 來(lái)調(diào)用抒痒。最常見(jiàn)的實(shí)現(xiàn)消息轉(zhuǎn)發(fā):就是重寫(xiě)方法3和4幌绍,吞掉一個(gè)消息或者代理給其他對(duì)象都是沒(méi)問(wèn)題的
也就是說(shuō)_objc_msgForward在進(jìn)行消息轉(zhuǎn)發(fā)的過(guò)程中會(huì)涉及以下這幾個(gè)方法:
resolveInstanceMethod:方法 (或 resolveClassMethod:)。
forwardingTargetForSelector:方法
methodSignatureForSelector:方法
forwardInvocation:方法
doesNotRecognizeSelector: 方法
下面回答下第二個(gè)問(wèn)題“直接_objc_msgForward調(diào)用它將會(huì)發(fā)生什么故响?”
直接調(diào)用_objc_msgForward是非常危險(xiǎn)的事傀广,如果用不好會(huì)直接導(dǎo)致程序Crash,但是如果用得好彩届,能做很多非澄北酷的事。
就好像跑酷樟蠕,干得好贮聂,叫“耍酷”坯墨,干不好就叫“作死”寂汇。
正如前文所說(shuō):
_objc_msgForward是 IMP 類(lèi)型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息捣染,但它并沒(méi)有實(shí)現(xiàn)的時(shí)候骄瓣,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)。
如何調(diào)用_objc_msgForward? _objc_msgForward隸屬 C 語(yǔ)言榕栏,有三個(gè)參數(shù) :
首先了解下如何調(diào)用 IMP 類(lèi)型的方法畔勤,IMP類(lèi)型是如下格式:
為了直觀,我們可以通過(guò)如下方式定義一個(gè) IMP類(lèi)型 :
1
typedef?void?(*voidIMP)(id,?SEL,?...)
一旦調(diào)用_objc_msgForward扒磁,將跳過(guò)查找 IMP 的過(guò)程庆揪,直接觸發(fā)“消息轉(zhuǎn)發(fā)”,
如果調(diào)用了_objc_msgForward妨托,即使這個(gè)對(duì)象確實(shí)已經(jīng)實(shí)現(xiàn)了這個(gè)方法缸榛,你也會(huì)告訴objc_msgSend:
“我沒(méi)有在這個(gè)對(duì)象里找到這個(gè)方法的實(shí)現(xiàn)”
想象下objc_msgSend會(huì)怎么做?通常情況下兰伤,下面這張圖就是你正常走objc_msgSend過(guò)程内颗,和直接調(diào)用_objc_msgForward的前后差別:
有哪些場(chǎng)景需要直接調(diào)用_objc_msgForward?最常見(jiàn)的場(chǎng)景是:你想獲取某方法所對(duì)應(yīng)的NSInvocation對(duì)象敦腔。舉例說(shuō)明:
JSPatch (Github 鏈接)就是直接調(diào)用_objc_msgForward來(lái)實(shí)現(xiàn)其核心功能的:
JSPatch 以小巧的體積做到了讓JS調(diào)用/替換任意OC方法均澳,讓iOS APP具備熱更新的能力。
作者的博文《JSPatch實(shí)現(xiàn)原理詳解》詳細(xì)記錄了實(shí)現(xiàn)原理符衔,有興趣可以看下找前。
26. runtime如何實(shí)現(xiàn)weak變量的自動(dòng)置nil?
runtime 對(duì)注冊(cè)的類(lèi)判族, 會(huì)進(jìn)行布局躺盛,對(duì)于 weak 對(duì)象會(huì)放入一個(gè) hash 表中。 用 weak 指向的對(duì)象內(nèi)存地址作為 key形帮,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì) dealloc颗品,假如 weak 指向的對(duì)象內(nèi)存地址是a,那么就會(huì)以a為鍵沃缘, 在這個(gè) weak 表中搜索,找到所有以a為鍵的 weak 對(duì)象则吟,從而設(shè)置為 nil槐臀。
在上篇中的《runtime 如何實(shí)現(xiàn) weak 屬性》有論述。(注:在上篇的《使用runtime Associate方法關(guān)聯(lián)的對(duì)象氓仲,需要在主對(duì)象dealloc的時(shí)候釋放么水慨?》里給出的“對(duì)象的內(nèi)存銷(xiāo)毀時(shí)間表”也提到__weak引用的解除時(shí)間。)
我們可以設(shè)計(jì)一個(gè)函數(shù)(偽代碼)來(lái)表示上述機(jī)制:
objc_storeWeak(&a, b)函數(shù):
objc_storeWeak函數(shù)把第二個(gè)參數(shù)--賦值對(duì)象(b)的內(nèi)存地址作為鍵值key敬扛,將第一個(gè)參數(shù)--weak修飾的屬性變量(a)的內(nèi)存地址(&a)作為value晰洒,注冊(cè)到 weak 表中。如果第二個(gè)參數(shù)(b)為0(nil)啥箭,那么把變量(a)的內(nèi)存地址(&a)從weak表中刪除谍珊,
你可以把objc_storeWeak(&a, b)理解為:objc_storeWeak(value, key),并且當(dāng)key變nil急侥,將value置nil砌滞。
在b非nil時(shí)侮邀,a和b指向同一個(gè)內(nèi)存地址,在b變nil時(shí)贝润,a變nil绊茧。此時(shí)向a發(fā)送消息不會(huì)崩潰:在Objective-C中向nil發(fā)送消息是安全的。
而如果a是由assign修飾的打掘,則: 在b非nil時(shí)华畏,a和b指向同一個(gè)內(nèi)存地址,在b變nil時(shí)尊蚁,a還是指向該內(nèi)存地址亡笑,變野指針。此時(shí)向a發(fā)送消息極易崩潰枝誊。
下面我們將基于objc_storeWeak(&a, b)函數(shù)况芒,使用偽代碼模擬“runtime如何實(shí)現(xiàn)weak屬性”:
1
2
3
4
5
6
7
8//?使用偽代碼模擬:runtime如何實(shí)現(xiàn)weak屬性
//http://weibo.com/luohanchenyilong/
//https://github.com/ChenYilong
id?obj1;
objc_initWeak(&obj1,?obj);
/*obj引用計(jì)數(shù)變?yōu)?,變量作用域結(jié)束*/
objc_destroyWeak(&obj1);
下面對(duì)用到的兩個(gè)方法objc_initWeak和objc_destroyWeak做下解釋?zhuān)?/p>
總體說(shuō)來(lái)叶撒,作用是: 通過(guò)objc_initWeak函數(shù)初始化“附有weak修飾符的變量(obj1)”绝骚,在變量作用域結(jié)束時(shí)通過(guò)objc_destoryWeak函數(shù)釋放該變量(obj1)。
下面分別介紹下方法的內(nèi)部實(shí)現(xiàn):
objc_initWeak函數(shù)的實(shí)現(xiàn)是這樣的:在將“附有weak修飾符的變量(obj1)”初始化為0(nil)后祠够,會(huì)將“賦值對(duì)象”(obj)作為參數(shù)压汪,調(diào)用objc_storeWeak函數(shù)。
1
2obj1?=?0古瓤;
obj_storeWeak(&obj1,?obj);
也就是說(shuō):
weak 修飾的指針默認(rèn)值是 nil (在Objective-C中向nil發(fā)送消息是安全的)
然后obj_destroyWeak函數(shù)將0(nil)作為參數(shù)止剖,調(diào)用objc_storeWeak函數(shù)。
1
objc_storeWeak(&obj1,?0);
前面的源代碼與下列源代碼相同落君。
1
2
3
4
5
6
7
8
9//?使用偽代碼模擬:runtime如何實(shí)現(xiàn)weak屬性
//http://weibo.com/luohanchenyilong/
//https://github.com/ChenYilong
id?obj1;
obj1?=?0;
objc_storeWeak(&obj1,?obj);
/*?...?obj的引用計(jì)數(shù)變?yōu)?穿香,被置nil?...?*/
objc_storeWeak(&obj1,?0);
objc_storeWeak函數(shù)把第二個(gè)參數(shù)--賦值對(duì)象(obj)的內(nèi)存地址作為鍵值,將第一個(gè)參數(shù)--weak修飾的屬性變量(obj1)的內(nèi)存地址注冊(cè)到 weak 表中绎速。如果第二個(gè)參數(shù)(obj)為0(nil)皮获,那么把變量(obj1)的地址從weak表中刪除。
27. 能否向編譯后得到的類(lèi)中增加實(shí)例變量纹冤?能否向運(yùn)行時(shí)創(chuàng)建的類(lèi)中添加實(shí)例變量洒宝?為什么?
不能向編譯后得到的類(lèi)中增加實(shí)例變量萌京;
能向運(yùn)行時(shí)創(chuàng)建的類(lèi)中添加實(shí)例變量雁歌;
解釋下:
因?yàn)榫幾g后的類(lèi)已經(jīng)注冊(cè)在 runtime 中,類(lèi)結(jié)構(gòu)體中的 objc_ivar_list 實(shí)例變量的鏈表 和 instance_size 實(shí)例變量的內(nèi)存大小已經(jīng)確定知残,同時(shí)runtime 會(huì)調(diào)用 class_setIvarLayout 或 class_setWeakIvarLayout 來(lái)處理 strong weak 引用靠瞎。所以不能向存在的類(lèi)中添加實(shí)例變量;
運(yùn)行時(shí)創(chuàng)建的類(lèi)是可以添加實(shí)例變量,調(diào)用 class_addIvar 函數(shù)较坛。但是得在調(diào)用 objc_allocateClassPair 之后印蔗,objc_registerClassPair 之前,原因同上丑勤。
28. runloop和線程有什么關(guān)系华嘹?
總的說(shuō)來(lái),Run loop法竞,正如其名耙厚,loop表示某種循環(huán),和run放在一起就表示一直在運(yùn)行著的循環(huán)岔霸。實(shí)際上薛躬,run loop和線程是緊密相連的,可以這樣說(shuō)run loop是為了線程而生呆细,沒(méi)有線程型宝,它就沒(méi)有存在的必要。Run loops是線程的基礎(chǔ)架構(gòu)部分絮爷, Cocoa 和 CoreFundation 都提供了 run loop 對(duì)象方便配置和管理線程的 run loop (以下都以 Cocoa 為例)趴酣。每個(gè)線程,包括程序的主線程( main thread )都有與之相應(yīng)的 run loop 對(duì)象坑夯。
runloop 和線程的關(guān)系:
1. 主線程的run loop默認(rèn)是啟動(dòng)的岖寞。
iOS的應(yīng)用程序里面,程序啟動(dòng)后會(huì)有一個(gè)如下的main()函數(shù)
1
2
3
4int?main(int?argc,?char?*?argv[])?{
@autoreleasepool?{returnUIApplicationMain(argc,?argv,?nil,?NSStringFromClass([AppDelegate?class]));
}
}
重點(diǎn)是UIApplicationMain()函數(shù)柜蜈,這個(gè)方法會(huì)為main thread設(shè)置一個(gè)NSRunLoop對(duì)象仗谆,這就解釋了:為什么我們的應(yīng)用可以在無(wú)人操作的時(shí)候休息,需要讓它干活的時(shí)候又能立馬響應(yīng)淑履。
2. 對(duì)其它線程來(lái)說(shuō)隶垮,run loop默認(rèn)是沒(méi)有啟動(dòng)的,如果你需要更多的線程交互則可以手動(dòng)配置和啟動(dòng)秘噪,如果線程只是去執(zhí)行一個(gè)長(zhǎng)時(shí)間的已確定的任務(wù)則不需要岁疼。
3. 在任何一個(gè) Cocoa 程序的線程中,都可以通過(guò)以下代碼來(lái)獲取到當(dāng)前線程的 run loop 缆娃。
1
NSRunLoop?*runloop?=?[NSRunLoop?currentRunLoop];
參考鏈接:《Objective-C之run loop詳解》。
29. runloop的mode作用是什么瑰排?
model 主要是用來(lái)指定事件在運(yùn)行循環(huán)中的優(yōu)先級(jí)的贯要,分為:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn),空閑狀態(tài)
UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)
UIInitializationRunLoopMode:?jiǎn)?dòng)時(shí)
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋(píng)果公開(kāi)提供的 Mode 有兩個(gè):
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
30. 以+ scheduledTimerWithTimeInterval...的方式觸發(fā)的timer椭住,在滑動(dòng)頁(yè)面上的列表時(shí)崇渗,timer會(huì)暫定回調(diào),為什么?如何解決宅广?
RunLoop只能運(yùn)行在一種mode下葫掉,如果要換mode,當(dāng)前的loop也需要停下重啟成新的跟狱。利用這個(gè)機(jī)制俭厚,ScrollView滾動(dòng)過(guò)程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode會(huì)切換到UITrackingRunLoopMode來(lái)保證ScrollView的流暢滑動(dòng):只能在NSDefaultRunLoopMode模式下處理的事件會(huì)影響scrllView的滑動(dòng)。
如果我們把一個(gè)NSTimer對(duì)象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運(yùn)行循環(huán)中的時(shí)候, ScrollView滾動(dòng)過(guò)程中會(huì)因?yàn)閙ode的切換驶臊,而導(dǎo)致NSTimer將不再被調(diào)度挪挤。
同時(shí)因?yàn)閙ode還是可定制的,所以:
Timer計(jì)時(shí)會(huì)被scrollView的滑動(dòng)影響的問(wèn)題可以通過(guò)將timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)來(lái)解決关翎。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//
//http://weibo.com/luohanchenyilong/(微博@iOS程序犭袁)
//https://github.com/ChenYilong
//將timer添加到NSDefaultRunLoopMode中
[NSTimer?scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
//然后再添加到NSRunLoopCommonModes里
NSTimer?*timer?=?[NSTimer?timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop?currentRunLoop]?addTimer:timer?forMode:NSRunLoopCommonModes];
31. 猜想runloop內(nèi)部是如何實(shí)現(xiàn)的扛门?
一般來(lái)講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù)纵寝,執(zhí)行完成后線程就會(huì)退出论寨。如果我們需要一個(gè)機(jī)制,讓線程能隨時(shí)處理事件但并不退出爽茴,通常的代碼邏輯 是這樣的:
1
2
3
4
5
6
7functionloop()?{
initialize();
do{
varmessage?=?get_next_message();
process_message(message);
}while(message?!=?quit);
}
或使用偽代碼來(lái)展示下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//
//http://weibo.com/luohanchenyilong/(微博@iOS程序犭袁)
//https://github.com/ChenYilong
int?main(int?argc,?char?*?argv[])?{
//程序一直運(yùn)行狀態(tài)
while(AppIsRunning)?{
//睡眠狀態(tài)葬凳,等待喚醒事件
id?whoWakesMe?=?SleepForWakingUp();
//得到喚醒事件
id?event?=?GetEvent(whoWakesMe);
//開(kāi)始處理事件
HandleEvent(event);
}
return0;
}
參考鏈接:
摘自博文CFRunLoop,原作者是微博@我就叫Sunny怎么了
32. objc使用什么機(jī)制管理對(duì)象內(nèi)存闹啦?
通過(guò) retainCount 的機(jī)制來(lái)決定對(duì)象是否需要釋放沮明。 每次 runloop 的時(shí)候,都會(huì)檢查對(duì)象的 retainCount窍奋,如果retainCount 為 0荐健,說(shuō)明該對(duì)象沒(méi)有地方需要繼續(xù)使用了,可以釋放掉了琳袄。
33. ARC通過(guò)什么方式幫助開(kāi)發(fā)者管理內(nèi)存江场?
編譯時(shí)根據(jù)代碼上下文,插入 retain/release
34. 不手動(dòng)指定autoreleasepool的前提下窖逗,一個(gè)autorealese對(duì)象在什么時(shí)刻釋放址否?(比如在一個(gè)vc的viewDidLoad中創(chuàng)建)
分兩種情況:手動(dòng)干預(yù)釋放時(shí)機(jī)、系統(tǒng)自動(dòng)去釋放碎紊。
手動(dòng)干預(yù)釋放時(shí)機(jī)--指定autoreleasepool 就是所謂的:當(dāng)前作用域大括號(hào)結(jié)束時(shí)釋放佑附。
系統(tǒng)自動(dòng)去釋放--不手動(dòng)指定autoreleasepool
Autorelease對(duì)象會(huì)在當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放。
如果在一個(gè)vc的viewDidLoad中創(chuàng)建一個(gè) Autorelease對(duì)象仗考,那么該對(duì)象會(huì)在 viewDidAppear 方法執(zhí)行前就被銷(xiāo)毀了音同。
參考鏈接:《黑幕背后的Autorelease》
35. BAD_ACCESS在什么情況下出現(xiàn)?
訪問(wèn)了野指針秃嗜,比如對(duì)一個(gè)已經(jīng)釋放的對(duì)象執(zhí)行了release权均、訪問(wèn)已經(jīng)釋放對(duì)象的成員變量或者發(fā)消息顿膨。 死循環(huán)
36. 蘋(píng)果是如何實(shí)現(xiàn)autoreleasepool的?
autoreleasepool以一個(gè)隊(duì)列數(shù)組的形式實(shí)現(xiàn),主要通過(guò)下列三個(gè)函數(shù)完成.
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
看函數(shù)名就可以知道叽赊,對(duì)autorelease分別執(zhí)行push恋沃,和pop操作。銷(xiāo)毀對(duì)象時(shí)執(zhí)行release操作必指。
37. 使用block時(shí)什么情況會(huì)發(fā)生引用循環(huán)囊咏,如何解決?
一個(gè)對(duì)象中強(qiáng)引用了block取劫,在block中又使用了該對(duì)象匆笤,就會(huì)發(fā)射循環(huán)引用。 解決方法是將該對(duì)象使用__weak或者_(dá)_block修飾符修飾之后再在block中使用谱邪。
id weak weakSelf = self; 或者 weak __typeof(&*self)weakSelf = self該方法可以設(shè)置宏
id __block weakSelf = self;
38. 在block內(nèi)如何修改block外部變量炮捧?
默認(rèn)情況下,在block中訪問(wèn)的外部變量是復(fù)制過(guò)去的惦银,即:寫(xiě)操作不對(duì)原變量生效咆课。但是你可以加上__block來(lái)讓其寫(xiě)操作生效,示例代碼如下:
1
2
3
4
5
6__block?int?a?=?0;
void??(^foo)(void)?=?^{
a?=?1;
}
f00();
//這里扯俱,a的值被修改為1
參考鏈接:微博@唐巧_boy的著作《iOS開(kāi)發(fā)進(jìn)階》中的第11.2.3章節(jié)
39. 使用系統(tǒng)的某些block api(如UIView的block版本寫(xiě)動(dòng)畫(huà)時(shí))书蚪,是否也考慮引用循環(huán)問(wèn)題?
系統(tǒng)的某些block api中迅栅,UIView的block版本寫(xiě)動(dòng)畫(huà)時(shí)不需要考慮殊校,但也有一些api 需要考慮:
所謂“引用循環(huán)”是指雙向的強(qiáng)引用,所以那些“單向的強(qiáng)引用”(block 強(qiáng)引用 self )沒(méi)有問(wèn)題读存,比如這些:
1
2
3
4
5
6[UIView?animateWithDuration:duration?animations:^{?[self.superview?layoutIfNeeded];?}];
[[NSOperationQueue?mainQueue]?addOperationWithBlock:^{?self.someProperty?=?xyz;?}];
[[NSNotificationCenter?defaultCenter]?addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue?mainQueue]??????????????????????????????????????????????usingBlock:^(NSNotification?*?notification)?{
self.someProperty?=?xyz;?}];
這些情況不需要考慮“引用循環(huán)”为流。
但如果你使用一些參數(shù)中可能含有 ivar 的系統(tǒng) api ,如 GCD 让簿、NSNotificationCenter就要小心一點(diǎn):比如GCD 內(nèi)部如果引用了 self敬察,而且 GCD 的其他參數(shù)是 ivar,則要考慮到循環(huán)引用:
1
2
3
4
5
6
7__weak?__typeof__(self)?weakSelf?=?self;
dispatch_group_async(_operationsGroup,?_operationsQueue,?^
{
__typeof__(self)?strongSelf?=?weakSelf;
[strongSelf?doSomething];
[strongSelf?doSomethingElse];
}?);
類(lèi)似的:
1
2
3
4
5
6
7
8__weak?__typeof__(self)?weakSelf?=?self;
_observer?=?[[NSNotificationCenter?defaultCenter]?addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification?*note)?{
__typeof__(self)?strongSelf?=?weakSelf;
[strongSelf?dismissModalViewControllerAnimated:YES];
}];
self --> _observer --> block --> self 顯然這也是一個(gè)循環(huán)引用尔当。
40. GCD的隊(duì)列(dispatch_queue_t)分哪兩種類(lèi)型莲祸?
串行隊(duì)列Serial Dispatch Queue
并行隊(duì)列Concurrent Dispatch Queue
41. 如何用GCD同步若干個(gè)異步調(diào)用?(如根據(jù)若干個(gè)url異步加載多張圖片椭迎,然后在都下載完成后合成一張整圖)
使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢锐帜,就會(huì)執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。
1
2
3
4
5
6
7
8dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);
dispatch_group_t?group?=?dispatch_group_create();
dispatch_group_async(group,?queue,?^{/*加載圖片1?*/});
dispatch_group_async(group,?queue,?^{/*加載圖片2?*/});
dispatch_group_async(group,?queue,?^{/*加載圖片3?*/});
dispatch_group_notify(group,?dispatch_get_main_queue(),?^{
//?合并圖片
});
42. dispatch_barrier_async的作用是什么畜号?
在并行隊(duì)列中缴阎,為了保持某些任務(wù)的順序,需要等待一些任務(wù)完成后才能繼續(xù)進(jìn)行弄兜,使用 barrier 來(lái)等待之前任務(wù)完成药蜻,避免數(shù)據(jù)競(jìng)爭(zhēng)等問(wèn)題。 dispatch_barrier_async 函數(shù)會(huì)等待追加到Concurrent Dispatch Queue并行隊(duì)列中的操作全部執(zhí)行完之后替饿,然后再執(zhí)行 dispatch_barrier_async 函數(shù)追加的處理语泽,等 dispatch_barrier_async 追加的處理執(zhí)行結(jié)束之后,Concurrent Dispatch Queue才恢復(fù)之前的動(dòng)作繼續(xù)執(zhí)行视卢。
打個(gè)比方:比如你們公司周末跟團(tuán)旅游踱卵,高速休息站上,司機(jī)說(shuō):大家都去上廁所据过,速戰(zhàn)速?zèng)Q惋砂,上完廁所就上高速。超大的公共廁所绳锅,大家同時(shí)去西饵,程序猿很快就結(jié)束了,但程序媛就可能會(huì)慢一些鳞芙,即使你第一個(gè)回來(lái)眷柔,司機(jī)也不會(huì)出發(fā),司機(jī)要等待所有人都回來(lái)后原朝,才能出發(fā)驯嘱。 dispatch_barrier_async 函數(shù)追加的內(nèi)容就如同 “上完廁所就上高速”這個(gè)動(dòng)作。
43. 蘋(píng)果為什么要廢棄dispatch_get_current_queue喳坠?
dispatch_get_current_queue容易造成死鎖
44. 以下代碼運(yùn)行結(jié)果如何鞠评?
1
2
3
4
5
6
7
8
9-?(void)viewDidLoad
{
[superviewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(),?^{
NSLog(@"2");
});
NSLog(@"3");
}
只輸出:1 。發(fā)生主線程鎖死壕鹉。
45. addObserver:forKeyPath:options:context:各個(gè)參數(shù)的作用分別是什么剃幌,observer中需要實(shí)現(xiàn)哪個(gè)方法才能獲得KVO回調(diào)?
1
2
3
4
5
6
7
8//?添加鍵值觀察
/*
1?觀察者御板,負(fù)責(zé)處理監(jiān)聽(tīng)事件的對(duì)象
2?觀察的屬性
3?觀察的選項(xiàng)
4?上下文
*/
[self.person?addObserver:self?forKeyPath:@"name"options:NSKeyValueObservingOptionNew?|?NSKeyValueObservingOptionOld?context:@"Person?Name"];
observer中需要實(shí)現(xiàn)一下方法:
1
2
3
4
5
6
7
8//?所有的?kvo?監(jiān)聽(tīng)到事件锥忿,都會(huì)調(diào)用此方法
/*
1.?觀察的屬性
2.?觀察的對(duì)象
3.?change?屬性變化字典(新/舊)
4.?上下文,與監(jiān)聽(tīng)的時(shí)候傳遞的一致
*/
-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context;
46. 如何手動(dòng)觸發(fā)一個(gè)value的KVO
所謂的“手動(dòng)觸發(fā)”是區(qū)別于“自動(dòng)觸發(fā)”:
自動(dòng)觸發(fā)是指類(lèi)似這種場(chǎng)景:在注冊(cè) KVO 之前設(shè)置一個(gè)初始值怠肋,注冊(cè)之后敬鬓,設(shè)置一個(gè)不一樣的值,就可以觸發(fā)了笙各。
想知道如何手動(dòng)觸發(fā)钉答,必須知道自動(dòng)觸發(fā) KVO 的原理:
鍵值觀察通知依賴(lài)于 NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一個(gè)被觀察屬性發(fā)生改變之前杈抢, willChangeValueForKey: 一定會(huì)被調(diào)用数尿,這就 會(huì)記錄舊的值。而當(dāng)改變發(fā)生后惶楼, didChangeValueForKey: 會(huì)被調(diào)用栏笆,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用劳曹。如果可以手動(dòng)實(shí)現(xiàn)這些調(diào)用妓灌,就可以實(shí)現(xiàn)“手動(dòng)觸發(fā)”了。
那么“手動(dòng)觸發(fā)”的使用場(chǎng)景是什么晨汹?一般我們只在希望能控制“回調(diào)的調(diào)用時(shí)機(jī)”時(shí)才會(huì)這么做。
具體做法如下:
如果這個(gè) value 是 表示時(shí)間的 self.now 贷盲,那么代碼如下:最后兩行代碼缺一不可淘这。
1
2
3
4
5
6
7
8
9
10
11
12//??.m文件
//??Created?byhttps://github.com/ChenYilong
//??微博@iOS程序犭袁(http://weibo.com/luohanchenyilong/).
//??手動(dòng)觸發(fā)?value?的KVO,最后兩行代碼缺一不可巩剖。
//@property?(nonatomic,?strong)?NSDate?*now;
-?(void)viewDidLoad
{
[superviewDidLoad];
[self?willChangeValueForKey:@"now"];//?“手動(dòng)觸發(fā)self.now的KVO”铝穷,必寫(xiě)。
[self?didChangeValueForKey:@"now"];//?“手動(dòng)觸發(fā)self.now的KVO”佳魔,必寫(xiě)曙聂。
}
但是平時(shí)我們一般不會(huì)這么干,我們都是等系統(tǒng)去“自動(dòng)觸發(fā)”吃引〕锪辏“自動(dòng)觸發(fā)”的實(shí)現(xiàn)原理:
比如調(diào)用 setNow: 時(shí),系統(tǒng)還會(huì)以某種方式在中間插入 wilChangeValueForKey: 镊尺、 didChangeValueForKey: 和 observeValueForKeyPath:ofObject:change:context: 的調(diào)用朦佩。
大家可能以為這是因?yàn)?setNow: 是合成方法,有時(shí)候我們也能看到人們這么寫(xiě)代碼:
1
2
3
4
5-?(void)setNow:(NSDate?*)aDate?{
[self?willChangeValueForKey:@"now"];//?沒(méi)有必要
_now?=?aDate;
[self?didChangeValueForKey:@"now"];//?沒(méi)有必要
}
這是完全沒(méi)有必要的代碼庐氮,不要這么做语稠,這樣的話,KVO代碼會(huì)被調(diào)用兩次弄砍。KVO在調(diào)用存取方法之前總是調(diào)用 willChangeValueForKey: 仙畦,之后總是調(diào)用 didChangeValueForkey: 。怎么做到的呢?答案是通過(guò) isa 混寫(xiě)(isa-swizzling)音婶。下文《apple用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO慨畸?》會(huì)有詳述。
47. 若一個(gè)類(lèi)有實(shí)例變量 NSString *_foo 衣式,調(diào)用setValue:forKey:時(shí)寸士,可以以foo還是 _foo 作為key?
都可以碴卧。
48. KVC的keyPath中的集合運(yùn)算符如何使用弱卡?
必須用在集合對(duì)象上或普通對(duì)象的集合屬性上
簡(jiǎn)單集合運(yùn)算符有@avg, @count 住册, @max 婶博, @min ,@sum荧飞,
格式 @"@sum.age"或 @"集合屬性.@max.age"
49. KVC和KVO的keyPath一定是屬性么凡人?
KVO支持實(shí)例變量
50. 如何關(guān)閉默認(rèn)的KVO的默認(rèn)實(shí)現(xiàn)名党,并進(jìn)入自定義的KVO實(shí)現(xiàn)?
請(qǐng)參考:《如何自己動(dòng)手實(shí)現(xiàn) KVO》
51. apple用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO挠轴?
Apple 的文檔對(duì) KVO 實(shí)現(xiàn)的描述:
Automatic key-value observing is implemented using a technique called isa-swizzling... When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ...
從Apple 的文檔可以看出:Apple 并不希望過(guò)多暴露 KVO 的實(shí)現(xiàn)細(xì)節(jié)兑巾。不過(guò),要是借助 runtime 提供的方法去深入挖掘忠荞,所有被掩蓋的細(xì)節(jié)都會(huì)原形畢露:
當(dāng)你觀察一個(gè)對(duì)象時(shí),一個(gè)新的類(lèi)會(huì)被動(dòng)態(tài)創(chuàng)建帅掘。這個(gè)類(lèi)繼承自該對(duì)象的原本的類(lèi)委煤,并重寫(xiě)了被觀察屬性的 setter 方法。重寫(xiě)的 setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后修档,通知所有觀察對(duì)象:值的更改碧绞。最后通過(guò) isa 混寫(xiě)(isa-swizzling) 把這個(gè)對(duì)象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對(duì)象的類(lèi)是什么 ) 指向這個(gè)新創(chuàng)建的子類(lèi),對(duì)象就神奇的變成了新創(chuàng)建的子類(lèi)的實(shí)例吱窝。我畫(huà)了一張示意圖讥邻,如下所示:
KVO 確實(shí)有點(diǎn)黑魔法:
Apple 使用了 isa 混寫(xiě)(isa-swizzling)來(lái)實(shí)現(xiàn) KVO 。
下面做下詳細(xì)解釋?zhuān)?/p>
鍵值觀察通知依賴(lài)于 NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey: 院峡。在一個(gè)被觀察屬性發(fā)生改變之前兴使, willChangeValueForKey: 一定會(huì)被調(diào)用,這就 會(huì)記錄舊的值照激。而當(dāng)改變發(fā)生后发魄, didChangeValueForKey: 會(huì)被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用俩垃±祝可以手動(dòng)實(shí)現(xiàn)這些調(diào)用,但很少有人這么做口柳。一般我們只在希望能控制回調(diào)的調(diào)用時(shí)機(jī)時(shí)才會(huì)這么做苹粟。大部分情況下,改變通知會(huì)自動(dòng)調(diào)用跃闹。
比如調(diào)用 setNow: 時(shí)嵌削,系統(tǒng)還會(huì)以某種方式在中間插入 wilChangeValueForKey: 、 didChangeValueForKey: 和 observeValueForKeyPath:ofObject:change:context: 的調(diào)用辣卒。大家可能以為這是因?yàn)?setNow: 是合成方法掷贾,有時(shí)候我們也能看到人們這么寫(xiě)代碼:
1
2
3
4
5-?(void)setNow:(NSDate?*)aDate?{
[self?willChangeValueForKey:@"now"];//?沒(méi)有必要
_now?=?aDate;
[self?didChangeValueForKey:@"now"];//?沒(méi)有必要
}
這是完全沒(méi)有必要的代碼,不要這么做荣茫,這樣的話想帅,KVO代碼會(huì)被調(diào)用兩次。KVO在調(diào)用存取方法之前總是調(diào)用 willChangeValueForKey: 啡莉,之后總是調(diào)用 didChangeValueForkey: 港准。怎么做到的呢?答案是通過(guò) isa 混寫(xiě)(isa-swizzling)旨剥。第一次對(duì)一個(gè)對(duì)象調(diào)用 addObserver:forKeyPath:options:context: 時(shí),框架會(huì)創(chuàng)建這個(gè)類(lèi)的新的 KVO 子類(lèi)浅缸,并將被觀察對(duì)象轉(zhuǎn)換為新子類(lèi)的對(duì)象轨帜。在這個(gè) KVO 特殊子類(lèi)中, Cocoa 創(chuàng)建觀察屬性的 setter 衩椒,大致工作原理如下:
1
2
3
4
5-?(void)setNow:(NSDate?*)aDate?{
[self?willChangeValueForKey:@"now"];
[supersetValue:aDate?forKey:@"now"];
[self?didChangeValueForKey:@"now"];
}
這種繼承和方法注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的蚌父。這就是正確命名如此重要的原因。只有在使用KVC命名約定時(shí)毛萌,KVO才能做到這一點(diǎn)苟弛。
KVO 在實(shí)現(xiàn)中通過(guò) isa 混寫(xiě)(isa-swizzling) 把這個(gè)對(duì)象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對(duì)象的類(lèi)是什么 ) 指向這個(gè)新創(chuàng)建的子類(lèi),對(duì)象就神奇的變成了新創(chuàng)建的子類(lèi)的實(shí)例阁将。這在Apple 的文檔可以得到印證:
Automatic key-value observing is implemented using a technique called isa-swizzling... When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ...
然而 KVO 在實(shí)現(xiàn)中使用了 isa 混寫(xiě)( isa-swizzling) 膏秫,這個(gè)的確不是很容易發(fā)現(xiàn):Apple 還重寫(xiě)、覆蓋了 -class 方法并返回原來(lái)的類(lèi)做盅。 企圖欺騙我們:這個(gè)類(lèi)沒(méi)有變缤削,就是原本那個(gè)類(lèi)。吹榴。亭敢。
但是,假設(shè)“被監(jiān)聽(tīng)的對(duì)象”的類(lèi)對(duì)象是 MYClass 图筹,有時(shí)候我們能看到對(duì) NSKVONotifying_MYClass 的引用而不是對(duì) MYClass 的引用吨拗。借此我們得以知道 Apple 使用了 isa 混寫(xiě)(isa-swizzling)。具體探究過(guò)程可參考這篇博文婿斥。
52. IBOutlet連出來(lái)的視圖屬性為什么可以被設(shè)置成weak?
參考鏈接:Should IBOutlets be strong or weak under ARC?
文章告訴我們:
因?yàn)榧热挥型怄溎敲匆晥D在xib或者storyboard中肯定存在劝篷,視圖已經(jīng)對(duì)它有一個(gè)強(qiáng)引用了。
不過(guò)這個(gè)回答漏了個(gè)重要知識(shí)民宿,使用storyboard(xib不行)創(chuàng)建的vc娇妓,會(huì)有一個(gè)叫_topLevelObjectsToKeepAliveFromStoryboard的私有數(shù)組強(qiáng)引用所有top level的對(duì)象,所以這時(shí)即便outlet聲明成weak也沒(méi)關(guān)系
53. IB中User Defined Runtime Attributes如何使用活鹰?
它能夠通過(guò)KVC的方式配置一些你在interface builder 中不能配置的屬性哈恰。當(dāng)你希望在IB中作盡可能多得事情,這個(gè)特性能夠幫助你編寫(xiě)更加輕量級(jí)的viewcontroller
54. 如何調(diào)試BAD_ACCESS錯(cuò)誤
1. 重寫(xiě)object的respondsToSelector方法志群,現(xiàn)實(shí)出現(xiàn)EXEC_BAD_ACCESS前訪問(wèn)的最后一個(gè)object
2. 通過(guò) Zombie
3. 設(shè)置全局?jǐn)帱c(diǎn)快速定位問(wèn)題代碼所在行
4. Xcode 7 已經(jīng)集成了BAD_ACCESS捕獲功能:Address Sanitizer着绷。 用法如下:在配置中勾選?Enable Address Sanitizer
55. lldb(gdb)常用的調(diào)試命令?
breakpoint 設(shè)置斷點(diǎn)定位到某一個(gè)函數(shù)
n 斷點(diǎn)指針下一步
po打印對(duì)象
更多 lldb(gdb) 調(diào)試命令可查看
蘋(píng)果官方文檔:iOS Debugging Magic