iOS題

一、如果讓你實(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 中的 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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評(píng)論 0 9
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 828評(píng)論 0 2
  • 前言 runtime其實(shí)在我們?nèi)粘i_(kāi)發(fā)過(guò)程中很少使用到,尤其是像我現(xiàn)在比較初級(jí)的程序猿就更用不到了。但是去面試很多...
    WolfTin閱讀 840評(píng)論 0 2
  • runtime 運(yùn)行時(shí)語(yǔ)言,實(shí)現(xiàn)Object-C的C語(yǔ)言庫(kù),將OC轉(zhuǎn)換成C進(jìn)行編譯的過(guò)渡者。 作為一門(mén)動(dòng)態(tài)編程語(yǔ)言...
    夜雨聲煩_閱讀 624評(píng)論 0 0
  • runtime 和 runloop 作為一個(gè)程序員進(jìn)階是必須的,也是非常重要的, 在面試過(guò)程中是經(jīng)常會(huì)被問(wèn)到的, ...
    SOI閱讀 22,020評(píng)論 3 63

友情鏈接更多精彩內(nèi)容