- 數據結構:objc_object,objc_class,isa,class_data_bits_t,cache_t,method_t
- 對象,類對象,元類對象
- 消息傳遞
- 消息轉發
一、數據結構:objc_object,objc_class,isa,class_data_bits_t,cache_t,method_t
objc_object(id)
isa_t,關于isa操作相關,弱引用相關,關聯對象相關,內存管理相關objc_class (class) 繼承自objc_object
Class superClass,cache_t cache,class_data_bits_t bits-
isa指針,共用體isa_t
image isa指向
關于對象,其指向類對象。
關于類對象,其指向元類對象。
實例--(isa)-->class--(isa)-->MetaClasscache_t
用于快速查找方法執行函數,是可增量擴展的哈希表結構,是局部性原理的最佳運用
struct cache_t {
struct bucket_t *_buckets;//一個散列表,用來方法緩存,bucket_t類型,包含key以及方法實現IMP
mask_t _mask;//分配用來緩存bucket的總數
mask_t _occupied;//表明目前實際占用的緩存bucket的個數
}
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
}
- class_data_bits_t:對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的類的屬性、方法、以及遵循的協議都放在class_rw_t中,class_rw_t代表了類相關的讀寫信息,是對class_ro_t的封裝,而class_ro_t代表了類的只讀信息,存儲了 編譯器決定了的屬性、方法和遵守協議
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
函數四要素:名稱,返回值,參數,函數體
struct method_t {
SEL name; //名稱
const char *types;//返回值和參數
IMP imp; //函數體
}
二、 對象,類對象,元類對象
類對象存儲實例方法列表等信息。
-
元類對象存儲類方法列表等信息。
imagesuperClass是一層層集成的,到最后NSObject的superClass是nil.而NSObject的isa指向根元類,這個根元類的isa指向它自己,而它的superClass是NSObject,也就是最后形成一個環,
三、消息傳遞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 */ };
消息傳遞的流程:緩存查找-->當前類查找-->父類逐級查找
- 調用方法之前,先去查找緩存,看看緩存中是否有對應選擇器的方法實現,如果有,就去調用函數,完成消息傳遞(緩存查找:給定值SEL,目標是查找對應bucket_t中的IMP,哈希查找)
- 如果緩存中沒有,會根據當前實例的isa指針查找當前類對象的方法列表,看看是否有同樣名稱的方法 ,如果找到,就去調用函數,完成消息傳遞(當前類中查找:對于已排序好的方法列表,采用二分查找,對于沒有排序好的列表,采用一般遍歷)
- 如果當前類對象的方法列表沒有,就會逐級父類方法列表中查找,如果找到,就去調用函數,完成消息傳遞(父類逐級查找:先判斷父類是否為nil,為nil則結束,否則就繼續進行緩存查找-->當前類查找-->父類逐級查找的流程)
- 如果一直查到根類依然沒有查找到,則進入到消息轉發流程中,完成消息傳遞
四、消息轉發
+ (BOOL)resolveInstanceMethod:(SEL)sel;//為對象方法進行決議
+ (BOOL)resolveClassMethod:(SEL)sel;//為類方法進行決議
- (id)forwardingTargetForSelector:(SEL)aSelector;//方法轉發目標
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
那么最后消息未能處理的時候,還會調用到
- (void)doesNotRecognizeSelector:(SEL)aSelector
這個方法,我們也可以在這個方法中做處理,避免掉crash,但是只建議在線上環境的時候做處理,實際開發過程中還要把異常拋出來
-
方法交換(Method-Swizzling)
+ (void)load { Method test = class_getInstanceMethod(self, @selector(test)); Method otherTest = class_getInstanceMethod(self, @selector(otherTest)); method_exchangeImplementations(test, otherTest); }
應用場景:替換系統的方法,比如viewDidLoad,viewWillAppear以及一些響應方法,來進行統計信息
-
動態添加方法
class_addMethod(self, sel, testImp, "v@:"); void testImp (void) { NSLog(@"testImp"); }
@dynamic 動態方法解析
動態運行時語言將函數決議推遲到運行時
編譯時語言在編譯期進行函數決議[obj foo]和objc_msgSend()函數之間有什么關系?
objc_msgSend()是[obj foo]的具體實現。在runtime中,objc_msgSend()是一個c函數,[obj foo]會被翻譯成這樣的形式objc_msgSend(obj, foo)。runtime是如何通過selector找到對應的IMP地址的?
緩存查找-->當前類查找-->父類逐級查找能否向編譯后的類中增加實例變量?
不能。 編譯后,該類已經完成了實例變量的布局,不能再增加實例變量。
但可以向動態添加的類中增加實例變量。