一、如果讓你實(shí)現(xiàn)屬性的weak,如何實(shí)現(xiàn)的?
PS: @property 等同于在.h文件中聲明實(shí)例變量的get/set方法, 而其中property有一些關(guān)鍵字,其中就包括weak,atomic的。
對(duì) weak 屬性的理解:
理解一:為這種屬性設(shè)置值時(shí),設(shè)置方法既不保留新設(shè)置的值,也不釋放之前設(shè)置的值, 不過(guò)在屬性所指的對(duì)象遭到摧毀時(shí),屬性值就會(huì)清空。
理解二:在setter方法中,需要對(duì)傳入的對(duì)象不進(jìn)行引用計(jì)數(shù)加1的操作。簡(jiǎn)單來(lái)說(shuō),就是對(duì)傳入的對(duì)象沒(méi)有所有權(quán),當(dāng)該對(duì)象引用計(jì)數(shù)為0時(shí),即該對(duì)象被釋放后,用weak聲明的實(shí)例變量指向nil。
如何實(shí)現(xiàn) 屬性的weak, 最關(guān)鍵的就是設(shè)置如何當(dāng) Object Dealloc 的時(shí)候設(shè)置 為nil
只是應(yīng)對(duì)某個(gè)具體屬性的場(chǎng)景:
1、寫(xiě) Setter 方法時(shí),將其新值 關(guān)聯(lián)一個(gè) 對(duì)象 (objc_setAssociatedObject)
2、并且實(shí)現(xiàn)該關(guān)聯(lián)對(duì)象的一個(gè)回調(diào)方法 ,在回調(diào)方法中 將新值 設(shè)置為 nil。
當(dāng)然該回調(diào)方法的執(zhí)行地方是在 dealloc 中實(shí)現(xiàn)的。
詳細(xì)可以看:【Objcective-C 高級(jí)編程 iOS 與 OS X多線程和內(nèi)存管理】中第一章第四節(jié) __weak 修飾符(我直接在書(shū)中看的,鏈接無(wú)效)
或者直接看:招聘一個(gè)靠譜iOS 程序員中第八節(jié) runtime 如何實(shí)現(xiàn) weak 屬性
二、如果讓你來(lái)實(shí)現(xiàn)屬性的atomic,如何實(shí)現(xiàn)?
2-1、對(duì) atomic 的理解
atomic意為操作是原子的,意味著只有一個(gè)線程訪問(wèn)實(shí)例變量。atomic是線程安全的,至少在當(dāng)前的存取器上是安全的。
2-2、如何實(shí)現(xiàn) 屬性的atomic,其實(shí)就是對(duì)線程安全的考察。
最簡(jiǎn)單的方法就是, 直接加線程鎖
用runtime方法
直接加線程鎖, 實(shí)現(xiàn)粗略的atomic
- (void)setTestObj:(id)testObj {@synchronized(self) {if(testObj != _testObj) {? ? ? ? ? ? _testObj = testObj;? ? ? ? }? ? }}- (id)testObj {@synchronized(self) {return_testObj;? ? }}
用runtime實(shí)現(xiàn), 注意該系列方法需要自己引入:
externvoidobjc_setProperty(idself, SEL _cmd, ptrdiff_t offset,idnewValue,BOOLatomic,BOOLshouldCopy);externidobjc_getProperty(idself, SEL _cmd, ptrdiff_t offset,BOOLatomic);externvoidobjc_copyStruct(void*dest,constvoid*src, ptrdiff_t size,BOOLatomic,BOOLhasStrong);
上面那幾個(gè)函數(shù)已經(jīng)被實(shí)現(xiàn)了,但沒(méi)有被聲名。如果要使用他們,必須自己聲名。具體來(lái)源:https://opensource.apple.com/source/objc4/objc4-371.2/runtime/Accessors.subproj/objc-accessors.h
#define AtomicRetainedSetToFrom(dest, source) objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, NO)#define AtomicCopiedSetToFrom(dest, source) objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, YES)#define AtomicAutoreleasedGet(source) objc_getProperty(self, _cmd, (ptrdiff_t)(&source) - (ptrdiff_t)(self), YES)#define AtomicStructToFrom(dest, source) objc_copyStruct(&dest,&source, sizeof(__typeof__(source)), YES, NO)
- (void)setTestStr:(NSString *)testStr {? ? AtomicCopiedSetToFrom(_testStr,testStr);}- (NSString *)testStr {returnAtomicAutoreleasedGet(_testStr);}
用runTime這個(gè)方法是從網(wǎng)上摘錄下來(lái)的,據(jù)說(shuō)速度和安全性肯定是更好的 ,對(duì)于此處暫時(shí)做了解。
2-3、實(shí)際參考的是:
objc系列譯文(2.4):線程安全類的設(shè)計(jì)
http://www.cocoawithlove.com/2009/10/memory-and-thread-safe-custom-property.html
三、KVO為什么要?jiǎng)?chuàng)建一個(gè)子類來(lái)實(shí)現(xiàn)?
這個(gè)題考察的實(shí)際上 KVO 的實(shí)現(xiàn)機(jī)制,或者說(shuō) KVO 的實(shí)現(xiàn)機(jī)制為什么是這樣的?
3-1、 KVO 大致實(shí)現(xiàn)機(jī)制:
簡(jiǎn)單的說(shuō),在我們對(duì)某個(gè)對(duì)象完成監(jiān)聽(tīng)的注冊(cè)后,編譯器會(huì)修改監(jiān)聽(tīng)對(duì)象的isa指針,讓這個(gè)指針指向一個(gè)新生成的中間類 (子類),然后子類重寫(xiě)所有的setter方法,并且該子類的- (Class) class和- (Class) superclass方法會(huì)被重寫(xiě),返回父類(原始類)的Class,最后將當(dāng)前對(duì)象的類改為這個(gè)KVO前綴的子類。
NSObject(NSKeyValueObserving)NSObject(NSKeyValueObserverRegistration)NSObject(NSKeyValueObservingCustomization)

KVO 實(shí)現(xiàn)圖 -- 源自iOS程序犭袁
3-2、為什么要?jiǎng)?chuàng)建一個(gè)子類來(lái)實(shí)現(xiàn)?
可以這樣說(shuō),如果我們不通過(guò)創(chuàng)建子類,那可以通過(guò)什么方法來(lái)實(shí)現(xiàn)呢?
提前知道的:通過(guò)子類繼承父類屬性并重寫(xiě)了它的setter方法,當(dāng)這個(gè)屬性被改變時(shí),KVO 就可以觀察到。
通過(guò)method_swizzling方法來(lái)進(jìn)行觀察值?
如最常用觀察的UITableView的contentOffset, 此處如果直接在 Setter 方法中用 method_swizzling 的方法,那么所有的UITableView都會(huì)受到影響,而我們一個(gè) App 中不止一個(gè)UITableView。
一個(gè)衍生的 KVO 注銷的坑
另外也可以從另一個(gè)角度理解,為什么使用 KVO 之后最后要記得移除它,創(chuàng)建了自然要銷毀嘛,但是同時(shí)也得注意一個(gè)移除的坑:
[_tableViewremoveObserver:selfforKeyPath:@"contentOffset"context:nil];
context這塊我們通常寫(xiě) nil, 但偶爾這樣是有問(wèn)題的,當(dāng)對(duì)同一個(gè)keypath進(jìn)行兩次removeObserver時(shí)會(huì)導(dǎo)致程序 Crash ,這種情況常常出現(xiàn)在父類有一個(gè) KVO ,父類在dealloc中remove了一次,子類又remove了一次的情況下。 所以這塊我建議context在由繼承的情況下盡量 寫(xiě)一個(gè)標(biāo)識(shí)值。
詳細(xì)可以看看這篇KVO進(jìn)階 —— 源碼實(shí)現(xiàn)探究
四、類結(jié)構(gòu)體的組成,isa指針指向了什么?(這里應(yīng)該將元類和根元類也說(shuō)一下)
4-1、此處考察的應(yīng)該是 Objective-C 的對(duì)象本質(zhì)。
Objective-C中的對(duì)象本質(zhì)上是結(jié)構(gòu)體對(duì)象,其中isa是它唯一的私有成員變量。
此處是在objc.h文件中看到的:
#if !OBJC_TYPES_DEFINED/// An opaque type that represents an Objective-C class.typedef struct objc_class *Class;/// Represents aninstanceof a class.struct objc_object {? ? Class isa? OBJC_ISA_AVAILABILITY;};/// A pointer to aninstanceof a class.typedef struct objc_object *id;#endif
類結(jié)構(gòu)體的組成
此處是 是在runtime.h文件中就可以看到的:
structobjc_class {? ? Class isa? OBJC_ISA_AVAILABILITY;// isa 指針#if!__OBJC2__Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 父類constchar*name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類名longversion? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類的版本號(hào)longinfo? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類的信息longinstance_size? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 實(shí)例大小structobjc_ivar_list *ivars? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 成員變量列表structobjc_method_list **methodLists? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 方法列表structobjc_cache *cache? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 方法緩存structobjc_protocol_list *protocols? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 協(xié)議列表#endif} OBJC2_UNAVAILABLE;/* Use `Class` instead of `struct objc_class *` */
從上面我們就可以看出其基本組成部分啦,其中成員變量列表,方法列表,方法緩存,及協(xié)議列表又是結(jié)構(gòu)體,另外特別要注意下isa指針。
4-2、 isa指針是指向 metaClass (元類)
metaClass是什么?
這就引出了metaClass的定義:metaClass是Class對(duì)象的類。
當(dāng)你向一個(gè)對(duì)象發(fā)送消息,就在那個(gè)對(duì)象的方法列表中查找那個(gè)消息。
當(dāng)你想一個(gè)類發(fā)送消息,就再那個(gè)類的metaClass中查找那個(gè)消息。
每個(gè)類都必須有一個(gè)唯一的metaClass,因?yàn)槊總€(gè)Class都有一個(gè)可能不一樣的類方法。
每個(gè)類里面都有個(gè)isa指針,這個(gè)isa指針是指向metaClass(元類)。

圖中步驟解釋:
1、當(dāng)[NSObject alloc]的時(shí)候,runtime庫(kù)會(huì)通過(guò)Class的isa指針找到該類的metaClass(元類)。并在該類的metaClass(元類)的methodLists方法列表中去查找alloc方法。
2、如果該類的metaClass(元類)的方法列表中沒(méi)找到alloc方法,那么就會(huì)向metaClass(元類)的基類的metaClass(元類)發(fā)送消息。而基類的metaClass則是指向自己的。
參考:
Objective-C Runtime(一)對(duì)象模型及類與元類
五、 RunLoop有幾種事件源?有幾種模式?
5-1、RunLoop有幾種事件源?
Run Loop對(duì)象處理的事件源分為兩種:Input sources 和 Timer sources。
Input sources:用分發(fā)異步事件,通常是用于其他線程或程序的消息。
Timer sources:用分發(fā)同步事件,通常這些事件發(fā)生在特定時(shí)間或者重復(fù)的時(shí)間間隔上(Timer事件(Schedule或者Repeat))。

經(jīng)典 RunLoop 圖
5-2、RunLoop有有幾種模式?
NSDefaultRunLoopMode :默認(rèn)狀態(tài)下,不滑動(dòng),空閑狀態(tài),程序啟動(dòng)之后就會(huì)被切到這個(gè)mode
UITrackingRunLoopMode : 滑動(dòng)的時(shí)候
UIInitializationRunLoopMode:私有的,可以追蹤到的,這個(gè)app啟動(dòng)的時(shí)候是這個(gè)mode,第一個(gè)頁(yè)面加載之后才回到第一個(gè)mode
NSRunLoopCommonModes:默認(rèn)情況包括下第一個(gè)第二個(gè),在這種情況下就是這兩種情況都可以執(zhí)行
這個(gè)要展開(kāi)的太多了,還是多看兩遍YY 大神的 深入理解RunLoop
六、方法列表的數(shù)據(jù)結(jié)構(gòu)是什么?
感覺(jué)是由于目前熱更新火的的原因,此處考察一下動(dòng)態(tài)加載的原理
PS: 今天最大的消息,蘋(píng)果對(duì)使用 JSPatch 的App 進(jìn)行警告了。。。
不過(guò)了解下objc_method和objc_method_list還是有必要的
類中每一個(gè)方法在內(nèi)部轉(zhuǎn)換后的結(jié)構(gòu)體objc_method
每一個(gè)類擁有的的函數(shù)列表objc_method_list
structobjc_method{? ? SEL method_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數(shù)名稱char*method_types? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數(shù)類型IMP method_imp? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//函數(shù)的具體實(shí)現(xiàn)()}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
方法列表的數(shù)據(jù)結(jié)構(gòu)也就如下了:
structobjc_method_list {structobjc_method_list *obsolete? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數(shù)列表intmethod_count? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數(shù)中的個(gè)數(shù)#ifdef__LP64__intspace? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;#endif/* variable length structure */structobjc_method method_list[1]? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數(shù)列表中的第一個(gè)函數(shù)地址}
通常使用了上述方法,下面這個(gè)方法一定是要了解的。
OBJC_EXPORTBOOLclass_addMethod(Classcls,SELname,IMPimp,constchar*types)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Class 給哪個(gè)類添加方法
* sel 要添加的方法編號(hào)(方法名)
* IMP 方法的實(shí)現(xiàn) ———— 函數(shù)的入口(函數(shù)的指針 函數(shù)名 是啥都可以 不一定和sel相同)
* types 方法的類型 編碼格式 (類型c語(yǔ)言的字符串) (函數(shù)的類型:返回值類型 參數(shù)類型 直接查文檔 文檔有表格)
”v@:”意思就是這已是一個(gè)void類型的方法,沒(méi)有參數(shù)傳入。
“i@:”就是說(shuō)這是一個(gè)int類型的方法,沒(méi)有參數(shù)傳入。
”v@:@”意思就是這已是一個(gè)void類型的方法,有參數(shù)傳入。
*/class_addMethod([self class], sel,@selector(testMethod),"v@:");
此處需要多了解下 runtime 中關(guān)于方法的一系列。
七、 分類是如何實(shí)現(xiàn)的?它為什么會(huì)覆蓋掉原來(lái)的方法?
先真正的看一下Category
typedefstructcategory_t{constchar*name;// 類的名字classref_tcls;// 類structmethod_list_t*instanceMethods;// 所有給類添加的實(shí)例方法的列表structmethod_list_t*classMethods;// 所有添加的類方法的列表structprotocol_list_t*protocols;// 實(shí)現(xiàn)的所有協(xié)議的列表structproperty_list_t*instanceProperties;// 添加的所有屬性}category_t;
7-1、分類是如何實(shí)現(xiàn)的?
簡(jiǎn)單的通俗說(shuō):Category實(shí)際上就變成了一個(gè)方法列表, 被插入到類的信息內(nèi), 這樣查表的時(shí)候就能找到Category內(nèi)的方法。
將 Category 和它的主類(或元類)注冊(cè)到哈希表中;
如果主類(或元類)已實(shí)現(xiàn),那么重建它的方法列表。
此處需要知道是,它分為兩種情況:Category中的實(shí)例方法、協(xié)議以及屬性添加到類上;而Category的類方法和協(xié)議添加到類的metaclass上的。
7-2、分類為什么會(huì)覆蓋掉原來(lái)的方法?
PS: 實(shí)際上如果Category和原來(lái)類都有相同的方法(testMethod),那么Category附加完成之后,類的方法列表里會(huì)有兩個(gè)該方法(testMethod),而不是直接替換的。
Category的方法被放到了新方法列表的前面,而原來(lái)類的方法被放到了新方法列表的后面,這也就是我們平常所說(shuō)的Category的方法會(huì)“覆蓋”掉原來(lái)類的同名方法,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法,就會(huì)停止了。
7-3、 此題的答案來(lái)源:
對(duì)于具體的實(shí)現(xiàn),確實(shí)需要看源代碼,我是通過(guò)下面兩篇解讀了解的:
Objective-C Category 的實(shí)現(xiàn)原理、深入理解Objective-C:Category。