一内列、如果讓你實(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è)置的值个粱, 不過在屬性所指的對(duì)象遭到摧毀時(shí)古毛,屬性值就會(huì)清空翻翩。
理解二:在setter方法中,需要對(duì)傳入的對(duì)象不進(jìn)行引用計(jì)數(shù)加1的操作稻薇。簡(jiǎn)單來說嫂冻,就是對(duì)傳入的對(duì)象沒有所有權(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案狠、寫 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 修飾符(我直接在書中看的拉庵,鏈接無效)
或者直接看:招聘一個(gè)靠譜iOS 程序員中第八節(jié) runtime 如何實(shí)現(xiàn) weak 屬性
二灿椅、如果讓你來實(shí)現(xiàn)屬性的atomic,如何實(shí)現(xiàn)?
2-1茫蛹、對(duì) atomic 的理解
atomic意為操作是原子的操刀,意味著只有一個(gè)線程訪問實(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)了警没,但沒有被聲名匈辱。如果要使用他們,必須自己聲名杀迹。具體來源: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)上摘錄下來的亡脸,據(jù)說速度和安全性肯定是更好的 ,對(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è)子類來實(shí)現(xiàn)续语?
這個(gè)題考察的實(shí)際上 KVO 的實(shí)現(xiàn)機(jī)制垂谢,或者說 KVO 的實(shí)現(xiàn)機(jī)制為什么是這樣的?
3-1疮茄、 KVO 大致實(shí)現(xiàn)機(jī)制:
簡(jiǎn)單的說滥朱,在我們對(duì)某個(gè)對(duì)象完成監(jiān)聽的注冊(cè)后,編譯器會(huì)修改監(jiān)聽對(duì)象的isa指針力试,讓這個(gè)指針指向一個(gè)新生成的中間類 (子類)徙邻,然后子類重寫所有的setter方法,并且該子類的- (Class) class和- (Class) superclass方法會(huì)被重寫畸裳,返回父類(原始類)的Class缰犁,最后將當(dāng)前對(duì)象的類改為這個(gè)KVO前綴的子類。
NSObject(NSKeyValueObserving)NSObject(NSKeyValueObserverRegistration)NSObject(NSKeyValueObservingCustomization)
KVO 實(shí)現(xiàn)圖 -- 源自iOS程序犭袁
3-2怖糊、為什么要?jiǎng)?chuàng)建一個(gè)子類來實(shí)現(xiàn)帅容?
可以這樣說,如果我們不通過創(chuàng)建子類伍伤,那可以通過什么方法來實(shí)現(xiàn)呢并徘?
提前知道的:通過子類繼承父類屬性并重寫了它的setter方法,當(dāng)這個(gè)屬性被改變時(shí)嚷缭,KVO 就可以觀察到饮亏。
通過method_swizzling方法來進(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這塊我們通常寫 nil, 但偶爾這樣是有問題的,當(dāng)對(duì)同一個(gè)keypath進(jìn)行兩次removeObserver時(shí)會(huì)導(dǎo)致程序 Crash 砰识,這種情況常常出現(xiàn)在父類有一個(gè) KVO 能扒,父類在dealloc中remove了一次,子類又remove了一次的情況下辫狼。 所以這塊我建議context在由繼承的情況下盡量 寫一個(gè)標(biāo)識(shí)值初斑。
詳細(xì)可以看看這篇KVO進(jìn)階 —— 源碼實(shí)現(xiàn)探究
四、類結(jié)構(gòu)體的組成膨处,isa指針指向了什么见秤?(這里應(yīng)該將元類和根元類也說一下)
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庫會(huì)通過Class的isa指針找到該類的metaClass(元類)腺占。并在該類的metaClass(元類)的methodLists方法列表中去查找alloc方法。
2痒谴、如果該類的metaClass(元類)的方法列表中沒找到alloc方法衰伯,那么就會(huì)向metaClass(元類)的基類的metaClass(元類)發(fā)送消息。而基類的metaClass則是指向自己的积蔚。
參考:
Objective-C 中的 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è)頁面加載之后才回到第一個(gè)mode
NSRunLoopCommonModes:默認(rèn)情況包括下第一個(gè)第二個(gè)捆交,在這種情況下就是這兩種情況都可以執(zhí)行
這個(gè)要展開的太多了,還是多看兩遍YY 大神的 深入理解RunLoop
六腐巢、方法列表的數(shù)據(jù)結(jié)構(gòu)是什么品追?
感覺是由于目前熱更新火的的原因,此處考察一下動(dòng)態(tài)加載的原理
PS: 今天最大的消息冯丙,蘋果對(duì)使用 JSPatch 的App 進(jìn)行警告了肉瓦。。胃惜。
不過了解下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語言的字符串) (函數(shù)的類型:返回值類型 參數(shù)類型 直接查文檔 文檔有表格)
”v@:”意思就是這已是一個(gè)void類型的方法船殉,沒有參數(shù)傳入鲫趁。
“i@:”就是說這是一個(gè)int類型的方法,沒有參數(shù)傳入利虫。
”v@:@”意思就是這已是一個(gè)void類型的方法挨厚,有參數(shù)傳入堡僻。
*/class_addMethod([self class], sel,@selector(testMethod),"v@:");
此處需要多了解下 runtime 中關(guān)于方法的一系列。
七疫剃、 分類是如何實(shí)現(xiàn)的苦始?它為什么會(huì)覆蓋掉原來的方法?
先真正的看一下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)單的通俗說:Category實(shí)際上就變成了一個(gè)方法列表, 被插入到類的信息內(nèi), 這樣查表的時(shí)候就能找到Category內(nèi)的方法。
將 Category 和它的主類(或元類)注冊(cè)到哈希表中蹄溉;
如果主類(或元類)已實(shí)現(xiàn)咨油,那么重建它的方法列表。
此處需要知道是柒爵,它分為兩種情況:Category中的實(shí)例方法役电、協(xié)議以及屬性添加到類上;而Category的類方法和協(xié)議添加到類的metaclass上的棉胀。
7-2法瑟、分類為什么會(huì)覆蓋掉原來的方法?
PS: 實(shí)際上如果Category和原來類都有相同的方法(testMethod)唁奢,那么Category附加完成之后霎挟,類的方法列表里會(huì)有兩個(gè)該方法(testMethod),而不是直接替換的麻掸。
Category的方法被放到了新方法列表的前面酥夭,而原來類的方法被放到了新方法列表的后面,這也就是我們平常所說的Category的方法會(huì)“覆蓋”掉原來類的同名方法脊奋,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的熬北,它只要一找到對(duì)應(yīng)名字的方法,就會(huì)停止了诚隙。
7-3讶隐、 此題的答案來源:
對(duì)于具體的實(shí)現(xiàn),確實(shí)需要看源代碼久又,我是通過下面兩篇解讀了解的:
Objective-C Category 的實(shí)現(xiàn)原理巫延、深入理解Objective-C:Category。