如果讓你實現屬性的weak,如何實現的? 如果讓你來實現屬性的atomic,如何實現? KVO為什么要創建一個子類來實現? 類結構體的組成,isa指針指向了什么?(這里應該將元類和根元類也說一下) RunLoop有幾種事件源?有幾種模式? 方法列表的數據結構是什么? 分類是如何實現的?它為什么會覆蓋掉原來的方法?

一、如果讓你實現屬性的weak,如何實現的?

PS: @property 等同于在.h文件中聲明實例變量的get/set方法, 而其中property有一些關鍵字,其中就包括weak,atomic的。

對 weak 屬性的理解:

理解一:為這種屬性設置值時,設置方法既不保留新設置的值,也不釋放之前設置的值, 不過在屬性所指的對象遭到摧毀時,屬性值就會清空。

理解二:在setter方法中,需要對傳入的對象不進行引用計數加1的操作。簡單來說,就是對傳入的對象沒有所有權,當該對象引用計數為0時,即該對象被釋放后,用weak聲明的實例變量指向nil。

如何實現 屬性的weak, 最關鍵的就是設置如何當 Object Dealloc 的時候設置 為nil

只是應對某個具體屬性的場景:

1、寫 Setter 方法時,將其新值 關聯一個 對象 (objc_setAssociatedObject)

2、并且實現該關聯對象的一個回調方法 ,在回調方法中 將新值 設置為 nil。

當然該回調方法的執行地方是在 dealloc 中實現的。

詳細可以看:【Objcective-C 高級編程 iOS 與 OS X多線程和內存管理】中第一章第四節 __weak 修飾符(我直接在書中看的,鏈接無效)

或者直接看:招聘一個靠譜iOS 程序員中第八節 runtime 如何實現 weak 屬性

二、如果讓你來實現屬性的atomic,如何實現?

2-1、對 atomic 的理解

atomic意為操作是原子的,意味著只有一個線程訪問實例變量。atomic是線程安全的,至少在當前的存取器上是安全的。

2-2、如何實現 屬性的atomic,其實就是對線程安全的考察。

最簡單的方法就是, 直接加線程鎖

用runtime方法

直接加線程鎖, 實現粗略的atomic

- (void)setTestObj:(id)testObj {@synchronized(self) {if(testObj != _testObj) {? ? ? ? ? ? _testObj = testObj;? ? ? ? }? ? }}- (id)testObj {@synchronized(self) {return_testObj;? ? }}

用runtime實現, 注意該系列方法需要自己引入:

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);

上面那幾個函數已經被實現了,但沒有被聲名。如果要使用他們,必須自己聲名。具體來源: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這個方法是從網上摘錄下來的,據說速度和安全性肯定是更好的 ,對于此處暫時做了解。

2-3、實際參考的是:

objc系列譯文(2.4):線程安全類的設計

http://www.cocoawithlove.com/2009/10/memory-and-thread-safe-custom-property.html

三、KVO為什么要創建一個子類來實現?

這個題考察的實際上 KVO 的實現機制,或者說 KVO 的實現機制為什么是這樣的?

3-1、 KVO 大致實現機制:

簡單的說,在我們對某個對象完成監聽的注冊后,編譯器會修改監聽對象的isa指針,讓這個指針指向一個新生成的中間類 (子類),然后子類重寫所有的setter方法,并且該子類的- (Class) class和- (Class) superclass方法會被重寫,返回父類(原始類)的Class,最后將當前對象的類改為這個KVO前綴的子類。

NSObject(NSKeyValueObserving)NSObject(NSKeyValueObserverRegistration)NSObject(NSKeyValueObservingCustomization)

KVO 實現圖 -- 源自iOS程序犭袁

3-2、為什么要創建一個子類來實現?

可以這樣說,如果我們不通過創建子類,那可以通過什么方法來實現呢?

提前知道的:通過子類繼承父類屬性并重寫了它的setter方法,當這個屬性被改變時,KVO 就可以觀察到。

通過method_swizzling方法來進行觀察值?

如最常用觀察的UITableView的contentOffset, 此處如果直接在 Setter 方法中用 method_swizzling 的方法,那么所有的UITableView都會受到影響,而我們一個 App 中不止一個UITableView。

一個衍生的 KVO 注銷的坑

另外也可以從另一個角度理解,為什么使用 KVO 之后最后要記得移除它,創建了自然要銷毀嘛,但是同時也得注意一個移除的坑:

[_tableViewremoveObserver:selfforKeyPath:@"contentOffset"context:nil];

context這塊我們通常寫 nil, 但偶爾這樣是有問題的,當對同一個keypath進行兩次removeObserver時會導致程序 Crash ,這種情況常常出現在父類有一個 KVO ,父類在dealloc中remove了一次,子類又remove了一次的情況下。 所以這塊我建議context在由繼承的情況下盡量 寫一個標識值。

詳細可以看看這篇KVO進階 —— 源碼實現探究

四、類結構體的組成,isa指針指向了什么?(這里應該將元類和根元類也說一下)

4-1、此處考察的應該是 Objective-C 的對象本質。

Objective-C中的對象本質上是結構體對象,其中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

類結構體的組成

此處是 是在runtime.h文件中就可以看到的:

structobjc_class {? ? Class isa? OBJC_ISA_AVAILABILITY;// isa 指針#if!__OBJC2__Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 父類constchar*name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類名longversion? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類的版本號longinfo? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類的信息longinstance_size? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 實例大小structobjc_ivar_list *ivars? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 成員變量列表structobjc_method_list **methodLists? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 方法列表structobjc_cache *cache? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 方法緩存structobjc_protocol_list *protocols? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 協議列表#endif} OBJC2_UNAVAILABLE;/* Use `Class` instead of `struct objc_class *` */

從上面我們就可以看出其基本組成部分啦,其中成員變量列表,方法列表,方法緩存,及協議列表又是結構體,另外特別要注意下isa指針。

4-2、 isa指針是指向 metaClass (元類)

metaClass是什么?

這就引出了metaClass的定義:metaClass是Class對象的類。

當你向一個對象發送消息,就在那個對象的方法列表中查找那個消息。

當你想一個類發送消息,就再那個類的metaClass中查找那個消息。

每個類都必須有一個唯一的metaClass,因為每個Class都有一個可能不一樣的類方法。

每個類里面都有個isa指針,這個isa指針是指向metaClass(元類)。

圖中步驟解釋:

1、當[NSObject alloc]的時候,runtime庫會通過Class的isa指針找到該類的metaClass(元類)。并在該類的metaClass(元類)的methodLists方法列表中去查找alloc方法。

2、如果該類的metaClass(元類)的方法列表中沒找到alloc方法,那么就會向metaClass(元類)的基類的metaClass(元類)發送消息。而基類的metaClass則是指向自己的。

參考:

Objective-C 中的 MetaClass 是什么?

Objective-C Runtime(一)對象模型及類與元類

五、 RunLoop有幾種事件源?有幾種模式?

5-1、RunLoop有幾種事件源?

Run Loop對象處理的事件源分為兩種:Input sources 和 Timer sources。

Input sources:用分發異步事件,通常是用于其他線程或程序的消息。

Timer sources:用分發同步事件,通常這些事件發生在特定時間或者重復的時間間隔上(Timer事件(Schedule或者Repeat))。

經典 RunLoop 圖

5-2、RunLoop有有幾種模式?

NSDefaultRunLoopMode :默認狀態下,不滑動,空閑狀態,程序啟動之后就會被切到這個mode

UITrackingRunLoopMode : 滑動的時候

UIInitializationRunLoopMode:私有的,可以追蹤到的,這個app啟動的時候是這個mode,第一個頁面加載之后才回到第一個mode

NSRunLoopCommonModes:默認情況包括下第一個第二個,在這種情況下就是這兩種情況都可以執行

這個要展開的太多了,還是多看兩遍YY 大神的 深入理解RunLoop

六、方法列表的數據結構是什么?

感覺是由于目前熱更新火的的原因,此處考察一下動態加載的原理

PS: 今天最大的消息,蘋果對使用 JSPatch 的App 進行警告了。。。

不過了解下objc_method和objc_method_list還是有必要的

類中每一個方法在內部轉換后的結構體objc_method

每一個類擁有的的函數列表objc_method_list

structobjc_method{? ? SEL method_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數名稱char*method_types? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數類型IMP method_imp? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//函數的具體實現()}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

方法列表的數據結構也就如下了:

structobjc_method_list {structobjc_method_list *obsolete? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數列表intmethod_count? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數中的個數#ifdef__LP64__intspace? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;#endif/* variable length structure */structobjc_method method_list[1]? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 函數列表中的第一個函數地址}

通常使用了上述方法,下面這個方法一定是要了解的。

OBJC_EXPORTBOOLclass_addMethod(Classcls,SELname,IMPimp,constchar*types)OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

/**

* Class 給哪個類添加方法

* sel 要添加的方法編號(方法名)

* IMP 方法的實現 ———— 函數的入口(函數的指針 函數名 是啥都可以 不一定和sel相同)

* types 方法的類型 編碼格式 (類型c語言的字符串) (函數的類型:返回值類型 參數類型 直接查文檔 文檔有表格)

”v@:”意思就是這已是一個void類型的方法,沒有參數傳入。

“i@:”就是說這是一個int類型的方法,沒有參數傳入。

”v@:@”意思就是這已是一個void類型的方法,有參數傳入。

*/class_addMethod([self class], sel,@selector(testMethod),"v@:");

此處需要多了解下 runtime 中關于方法的一系列。

七、 分類是如何實現的?它為什么會覆蓋掉原來的方法?

先真正的看一下Category

typedefstructcategory_t{constchar*name;// 類的名字classref_tcls;// 類structmethod_list_t*instanceMethods;// 所有給類添加的實例方法的列表structmethod_list_t*classMethods;// 所有添加的類方法的列表structprotocol_list_t*protocols;// 實現的所有協議的列表structproperty_list_t*instanceProperties;// 添加的所有屬性}category_t;

7-1、分類是如何實現的?

簡單的通俗說:Category實際上就變成了一個方法列表, 被插入到類的信息內, 這樣查表的時候就能找到Category內的方法。

將 Category 和它的主類(或元類)注冊到哈希表中;

如果主類(或元類)已實現,那么重建它的方法列表。

此處需要知道是,它分為兩種情況:Category中的實例方法、協議以及屬性添加到類上;而Category的類方法和協議添加到類的metaclass上的。

7-2、分類為什么會覆蓋掉原來的方法?

PS: 實際上如果Category和原來類都有相同的方法(testMethod),那么Category附加完成之后,類的方法列表里會有兩個該方法(testMethod),而不是直接替換的。

Category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的后面,這也就是我們平常所說的Category的方法會“覆蓋”掉原來類的同名方法,這是因為運行時在查找方法的時候是順著方法列表的順序查找的,它只要一找到對應名字的方法,就會停止了。

7-3、 此題的答案來源:

對于具體的實現,確實需要看源代碼,我是通過下面兩篇解讀了解的:

Objective-C Category 的實現原理、深入理解Objective-C:Category。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,774評論 0 9
  • 轉載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 768評論 0 2
  • 前言 runtime其實在我們日常開發過程中很少使用到,尤其是像我現在比較初級的程序猿就更用不到了。但是去面試很多...
    WolfTin閱讀 662評論 0 2
  • Runtime是一套比較底層的純C語言API,包含了很多底層的C語言API。在我們平時編寫的OC代碼中,程序運行時...
    這個年紀的情愫丶閱讀 627評論 5 3
  • 我們常常會聽說 Objective-C 是一門動態語言,那么這個「動態」表現在哪呢?我想最主要的表現就是 Obje...
    Ethan_Struggle閱讀 2,231評論 0 7