在<objc/objc.h>頭文件中查看class與object在Objective-C的定義
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class; //類供璧,結(jié)構(gòu)體指針半抱,實(shí)際也是對象
/// Represents an instance of a class.
struct objc_object { //實(shí)例對象的isa指針指向類對象泥耀,而類對象的isa指向元類
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id; //對象腾仅,結(jié)構(gòu)體指針
由上面可以看出,對象本質(zhì)是個(gè)結(jié)構(gòu)體指針碗誉,而類實(shí)際也是一個(gè)結(jié)構(gòu)體指針召嘶,所以類也是對象,實(shí)例對象的isa指針指向類(對象)哮缺,而類對象的isa指針指向元類
在objc/runtime.h中objc_class結(jié)構(gòu)體的定義如下:
以下為類結(jié)構(gòu)體弄跌,其中包含緩存方法列表、方法列表蝴蜓、屬性列表碟绑、父類指針等
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; //isa指針
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE; // 父類
const char * _Nonnull name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息茎匠,供運(yùn)行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; // 緩存方法列表
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; // 協(xié)議列表
#endif
} OBJC2_UNAVAILABLE;
消息發(fā)送流程:
1.首先檢查這個(gè)selector是不是要忽略格仲,如有了垃圾回收裝置就不會理會retain,release等
2.檢測這個(gè)selector的target是不是nil诵冒,OC允許我們對一個(gè)nil對象執(zhí)行任何方法不會Crash凯肋,因?yàn)檫\(yùn)行時(shí)會被忽略掉
3.查找這個(gè)類的實(shí)現(xiàn)IMP,先從緩存方法列表cache里查找汽馋,執(zhí)行過的方法會添加到該列表中侮东,如果找到了就運(yùn)行對應(yīng)的函數(shù)去執(zhí)行相應(yīng)的代碼
4.如果沒有找到就找類的方法列表methodLists中是否有對應(yīng)的方法,找到則執(zhí)行
5.如果該對象的類的方法列表中找不到就到父類的方法列表中查找豹芯,一直找到NSObject類為止
6.如果還是沒找到就要開始進(jìn)入消息轉(zhuǎn)發(fā)(動態(tài)方法解析悄雅,備援接收者,消息重定向)
*動態(tài)方法解析
當(dāng)runtime系統(tǒng)在Cache和類的方法列表(包括父類)中找不到要執(zhí)行的方法時(shí)runtime會調(diào)用resolveInstanceMethod: 或者resolveClassMethod: 來給我們一次動態(tài)添加方法實(shí)現(xiàn)的機(jī)會铁蹈,在該方法中可以使用class_addMethod動態(tài)添加方法宽闲,則例如:
//動態(tài)添加一個(gè)方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString * methodName = NSStringFromSelector(sel);
if([methodName isEqualToString:@"sendMessage:"]){
class_addMethod([self class], @selector(sendMessage:), (IMP)sendMessage, "v@:@");
return YES;
}
return [super resolveInstanceMethod:name];
}
void sendMessage(id self,SEL cmd,id value){
NSLog(@"value=%@",value);
}
動態(tài)方法解析是在消息轉(zhuǎn)發(fā)機(jī)制侵入之前執(zhí)行,動態(tài)方法解析器將會首先給予提供該方法選擇器對應(yīng)的IMP的機(jī)會握牧。如果你想讓該方法選擇器被傳送到轉(zhuǎn)發(fā)機(jī)制容诬,就讓resolveInstanceMethod:方法返回NO。
*備援接收者
當(dāng)對象所屬類不能動態(tài)添加方法后沿腰,runtime就會詢問當(dāng)前的接受者是否有其他對象可以處理這個(gè)未知的selector
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSString * methodName = NSStringFromSelector(aSelector);
if([methodName isEqualToString:@"sendMessage:"]){
return [[OtherObject alloc]init];//返回一個(gè)實(shí)現(xiàn)了某方法的對象览徒,讓他去調(diào)用,例如:OtherObject對象
}
return [super forwardingTargetForSelector:aSelector];
}
*消息重定向
當(dāng)沒有備援接收者時(shí)颂龙,就只剩下最后一次機(jī)會习蓬,那就是消息重定向。這個(gè)時(shí)候runtime會將未知消息的所有細(xì)節(jié)都封裝為NSInvocation對象
//消息中轉(zhuǎn),丟給能接受這個(gè)消息處理的對象
//獲取方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString * methodName = NSStringFromSelector(aSelector);
if([methodName isEqualToString:@"sendMessage:"]){
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL sel = anInvocation.selector;
OtherObject * oth = [[OtherObject alloc]init];
if([oth respondsToSelector:sel]){
[anInvocation invokeWithTarget:oth];
}else{
return [super forwardInvocation:anInvocation];
}
}