2019 iOS面試題大全---全方面剖析面試
- 數(shù)據(jù)結(jié)構(gòu):objc_object,objc_class,isa殴穴,class_data_bits_t柴钻,cache_t淮韭,method_t
- 對(duì)象,類(lèi)對(duì)象贴届,元類(lèi)對(duì)象
- 消息傳遞
- 消息轉(zhuǎn)發(fā)
一靠粪、數(shù)據(jù)結(jié)構(gòu):objc_object,objc_class毫蚓,isa占键,class_data_bits_t,cache_t元潘,method_t
- objc_object(id)
isa_t,關(guān)于isa操作相關(guān)畔乙,弱引用相關(guān),關(guān)聯(lián)對(duì)象相關(guān)翩概,內(nèi)存管理相關(guān) - objc_class (class) 繼承自objc_object
Class superClass,cache_t cache,class_data_bits_t bits -
isa指針牲距,共用體isa_t
- isa指向
關(guān)于對(duì)象,其指向類(lèi)對(duì)象钥庇。
關(guān)于類(lèi)對(duì)象牍鞠,其指向元類(lèi)對(duì)象。
實(shí)例--(isa)-->class--(isa)-->MetaClass - cache_t
用于快速查找方法執(zhí)行函數(shù)评姨,是可增量擴(kuò)展的哈希表結(jié)構(gòu)难述,是局部性原理的最佳運(yùn)用
struct cache_t {
struct bucket_t *_buckets;//一個(gè)散列表,用來(lái)方法緩存,bucket_t類(lèi)型胁后,包含key以及方法實(shí)現(xiàn)IMP
mask_t _mask;//分配用來(lái)緩存bucket的總數(shù)
mask_t _occupied;//表明目前實(shí)際占用的緩存bucket的個(gè)數(shù)
}
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
}
- class_data_bits_t:對(duì)class_rw_t的封裝
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
Objc的類(lèi)的屬性店读、方法、以及遵循的協(xié)議都放在class_rw_t中攀芯,class_rw_t代表了類(lèi)相關(guān)的讀寫(xiě)信息屯断,是對(duì)class_ro_t的封裝,而class_ro_t代表了類(lèi)的只讀信息敲才,存儲(chǔ)了 編譯器決定了的屬性裹纳、方法和遵守協(xié)議
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
- method_t
函數(shù)四要素:名稱(chēng),返回值紧武,參數(shù)剃氧,函數(shù)體
struct method_t {
SEL name; //名稱(chēng)
const char *types;//返回值和參數(shù)
IMP imp; //函數(shù)體
}
二、 對(duì)象阻星,類(lèi)對(duì)象朋鞍,元類(lèi)對(duì)象
類(lèi)對(duì)象存儲(chǔ)實(shí)例方法列表等信息。
-
元類(lèi)對(duì)象存儲(chǔ)類(lèi)方法列表等信息妥箕。
superClass是一層層集成的,到最后NSObject的superClass是nil.而NSObject的isa指向根元類(lèi),這個(gè)根元類(lèi)的isa指向它自己,而它的superClass是NSObject,也就是最后形成一個(gè)環(huán),
三滥酥、消息傳遞void objc_msgSend(void /* id self, SEL op, ... */ ) void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ ) struct objc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. #if !defined(__cplusplus) && !__OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };
消息傳遞的流程:緩存查找-->當(dāng)前類(lèi)查找-->父類(lèi)逐級(jí)查找
- 調(diào)用方法之前,先去查找緩存畦幢,看看緩存中是否有對(duì)應(yīng)選擇器的方法實(shí)現(xiàn)坎吻,如果有,就去調(diào)用函數(shù)宇葱,完成消息傳遞(緩存查找:給定值SEL,目標(biāo)是查找對(duì)應(yīng)bucket_t中的IMP瘦真,哈希查找)
- 如果緩存中沒(méi)有,會(huì)根據(jù)當(dāng)前實(shí)例的isa指針查找當(dāng)前類(lèi)對(duì)象的方法列表黍瞧,看看是否有同樣名稱(chēng)的方法 诸尽,如果找到,就去調(diào)用函數(shù)印颤,完成消息傳遞(當(dāng)前類(lèi)中查找:對(duì)于已排序好的方法列表您机,采用二分查找,對(duì)于沒(méi)有排序好的列表年局,采用一般遍歷)
- 如果當(dāng)前類(lèi)對(duì)象的方法列表沒(méi)有际看,就會(huì)逐級(jí)父類(lèi)方法列表中查找,如果找到矢否,就去調(diào)用函數(shù)仿村,完成消息傳遞(父類(lèi)逐級(jí)查找:先判斷父類(lèi)是否為nil,為nil則結(jié)束兴喂,否則就繼續(xù)進(jìn)行緩存查找-->當(dāng)前類(lèi)查找-->父類(lèi)逐級(jí)查找的流程)
- 如果一直查到根類(lèi)依然沒(méi)有查找到,則進(jìn)入到消息轉(zhuǎn)發(fā)流程中,完成消息傳遞
四衣迷、消息轉(zhuǎn)發(fā)
+ (BOOL)resolveInstanceMethod:(SEL)sel;//為對(duì)象方法進(jìn)行決議
+ (BOOL)resolveClassMethod:(SEL)sel;//為類(lèi)方法進(jìn)行決議
- (id)forwardingTargetForSelector:(SEL)aSelector;//方法轉(zhuǎn)發(fā)目標(biāo)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
那么最后消息未能處理的時(shí)候畏鼓,還會(huì)調(diào)用到
- (void)doesNotRecognizeSelector:(SEL)aSelector
這個(gè)方法,我們也可以在這個(gè)方法中做處理壶谒,避免掉crash云矫,但是只建議在線上環(huán)境的時(shí)候做處理,實(shí)際開(kāi)發(fā)過(guò)程中還要把異常拋出來(lái)
-
方法交換(Method-Swizzling)
+ (void)load { Method test = class_getInstanceMethod(self, @selector(test)); Method otherTest = class_getInstanceMethod(self, @selector(otherTest)); method_exchangeImplementations(test, otherTest); }
應(yīng)用場(chǎng)景:替換系統(tǒng)的方法汗菜,比如viewDidLoad让禀,viewWillAppear以及一些響應(yīng)方法,來(lái)進(jìn)行統(tǒng)計(jì)信息
-
動(dòng)態(tài)添加方法
class_addMethod(self, sel, testImp, "v@:"); void testImp (void) { NSLog(@"testImp"); }
@dynamic 動(dòng)態(tài)方法解析
動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言將函數(shù)決議推遲到運(yùn)行時(shí)
編譯時(shí)語(yǔ)言在編譯期進(jìn)行函數(shù)決議[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系陨界?
objc_msgSend()是[obj foo]的具體實(shí)現(xiàn)巡揍。在runtime中,objc_msgSend()是一個(gè)c函數(shù)菌瘪,[obj foo]會(huì)被翻譯成這樣的形式objc_msgSend(obj, foo)腮敌。runtime是如何通過(guò)selector找到對(duì)應(yīng)的IMP地址的?
緩存查找-->當(dāng)前類(lèi)查找-->父類(lèi)逐級(jí)查找能否向編譯后的類(lèi)中增加實(shí)例變量俏扩?
不能糜工。 編譯后,該類(lèi)已經(jīng)完成了實(shí)例變量的布局录淡,不能再增加實(shí)例變量捌木。
但可以向動(dòng)態(tài)添加的類(lèi)中增加實(shí)例變量。