1.what?
Objective-C具有相當(dāng)多的動(dòng)態(tài)特性,也就是經(jīng)常被提到和用到的有動(dòng)態(tài)類型(Dynamic typing),動(dòng)態(tài)綁定(Dynamic binding),和動(dòng)態(tài)加載(Dynamic loading)。這些特性都是基于runtime實(shí)現(xiàn)的。so,Objective-C的Runtime是一個(gè)運(yùn)行時(shí)庫(Runtime Libary),它是由C語音和匯編寫的庫。為C添加了面相對(duì)象的能力并創(chuàng)造了 Objective-C,這就是說它在類信息(Class information) 中被加載,完成所有的方法分發(fā),方法轉(zhuǎn)發(fā),等等。Objective-C runtime 創(chuàng)建了所有需要的結(jié)構(gòu)體,讓 Objective-C 的面相對(duì)象編程變?yōu)榭赡堋?/p>
Objective-C 是面相運(yùn)行時(shí)的語言(runtime oriented language),就是說它會(huì)盡可能的把編譯和鏈接時(shí)要執(zhí)行的邏輯延遲到運(yùn)行時(shí)。這就給了你很大的靈活性,你可以按需要把消息重定向給合適的對(duì)象,你甚至可以交換方法的實(shí)現(xiàn),等等。
2.引導(dǎo)--神經(jīng)病院objc runtime入院考試
看看神經(jīng)病院的objc runtime的入院考試題。這些個(gè)題雖然不會(huì)在面試中面到,但是對(duì)于runtime的理解是很有幫助的。
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
@interface NSObject (Sark)
+ (void)foo;
@end
@implementation NSObject (Sark)
- (void)foo {
NSLog(@"IMP: -[NSObject (Sark) foo]");
}
@end
// 測(cè)試代碼
[NSObject foo];
[[NSObject new] foo];
@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Sark
- (void)speak {
NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Sark class];
void *obj = &cls;
[(__bridge id)obj speak];
}
@end
帶著思考以及你們的答案,讓我們開始runtime之旅。
3. objc中你不能不知道的元素
3.1 從self和super開始
首先得理解self和super這兩個(gè)概念。否則無從下手。
self是類的隱藏參數(shù),指向當(dāng)前調(diào)用方法的這個(gè)類的實(shí)例。
super是一個(gè)Magic Keyword,它本質(zhì)是一個(gè)編譯器標(biāo)示符,和self 一樣都是指向是一個(gè)消息接收者。
3.2 id(對(duì)象)
id 定義? (objc.h )
/// A pointer to an instance of a class.
typedef struct objc_object *id;
按照解釋,通俗的來說就是一個(gè)指針。objc_object又是什么?
id這個(gè)struct的定義本身就帶了個(gè)*, 所以我們?cè)谑褂闷渌?code>NSObject類型聲明實(shí)例時(shí)需要在前加上*, 使用id時(shí)卻不用 。
什么是objc_object? (objc.h )
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
這里給出了解釋是說,objc_object代表的是一個(gè)類的實(shí)例。
這個(gè)時(shí)候我們知道Objective-C中的object在最后會(huì)被轉(zhuǎn)換成C的結(jié)構(gòu)體, 在這個(gè)struct中有個(gè)isa指針,指向它的類別Class。
有一種說法解釋isa還是比較容易理解的:is a pointer,是個(gè)指針。
3.3 class(類、類對(duì)象)
上面出現(xiàn)了class,這又是什么呢?
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
是一個(gè)隱式的類型代表OC中的類,那么objc_class是什么?
struct objc_class {
Class isa; // isa 指針
Class super_class; // 父類,指向父類
const char *name; // 類名
long version; // 版本
long info; // 類信息
long instance_size; // 實(shí)例大小
struct objc_ivar_list *ivars; // 參數(shù)鏈表
struct objc_method_list **methodLists; // 方法鏈表
struct objc_cache *cache; // 方法緩存,調(diào)用過的方法存入緩存列表,下次調(diào)用優(yōu)先從棧中尋找
struct objc_protocol_list *protocols; // 協(xié)議鏈表
}OBJC2_UNAVAILABLE;
Use `Class` instead of `struct objc_class`
objc_class的結(jié)構(gòu)體代表的是一個(gè)class。
我們都知道在oc中一切皆為對(duì)象。
下載Objc源碼,在 objc-runtime-new.h 中,objc_class還有這樣的定義
struct objc_class : objc_object {
// Class ISA;
Class superclass;
...
...
}
也就是說,class本身也是一個(gè)對(duì)象,也有superclass。class是一個(gè)指向類對(duì)象的指針。這個(gè)class也有一個(gè)isa指針,這個(gè)指針指向的就是元類。
比較繞,舉個(gè)例子
Son *son = [[Son alloc] init];
// 在這里,我們初始化了一個(gè)son的實(shí)例變量,就是常說的對(duì)象。
// son對(duì)象的類是Son。
// 而Son這個(gè)class也是一個(gè)對(duì)象。(類對(duì)象)
// Son這個(gè)對(duì)象的類就是上面說的元類。

總結(jié):
<b>
- 每個(gè)Class都有一個(gè)isa指針指向一個(gè)唯一的Meta Class。
- 每一個(gè)Meta Class的isa指針都指向最上層的Meta Class(圖中的NSObject的Meta Class)
- 最上層的Meta Class的isa指針指向自己,形成一個(gè)回路
- 每一個(gè)Meta Class的super class指針指向它原本Class的 Super Class的Meta Class。但是最上層的Meta Class的 Super Class指向NSObject Class本身
- 最上層的NSObject Class的super class指向 nil。
</b>
3.4 SEL
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
選擇器,一個(gè)方法selector。
并沒有找到objc_selector的結(jié)構(gòu)定義,selector用于表示一個(gè)運(yùn)行時(shí)方法的名字,在運(yùn)行時(shí),會(huì)根據(jù)方法的名字、參數(shù)序列生產(chǎn)一個(gè)唯一的標(biāo)識(shí),就是SEL,說白了就是方法名的地址,一個(gè)字符串。
在Objective-C同一個(gè)類(及類的繼承體系)中,不能存在2個(gè)同名的方法,即使參數(shù)類型不同也不行。相同的方法只能對(duì)應(yīng)一個(gè)SEL。這也就導(dǎo)致Objective-C在處理相同方法名且參數(shù)個(gè)數(shù)相同但類型不同的方法方面的能力很差。
不同的類中有相同的selector,這個(gè)無所謂,不同的類在執(zhí)行過程中,會(huì)在各自的方法列表中根據(jù)selector尋找相應(yīng)的SEL。
工程中SEL是一個(gè)set集合,每一個(gè)SEL都是唯一的。SEL只是一個(gè)指向方法的指針??梢酝ㄟ^以下三種獲取SEL:
- sel_registerName函數(shù)
- Objective-C編譯器提供的@selector()
- NSSelectorFromString()
SEL func1 = sel_registerName("btnClick");
SEL func2 = @selector(btnClick);
SEL func3 = NSSelectorFromString(@"btnClick");
if ([self respondsToSelector:func1]) {
[self performSelector:func1 withObject:nil];
}
這個(gè)時(shí)候會(huì)告訴你有警告, "PerformSelector may cause a leak because its selector is unknown"。
為什么會(huì)出現(xiàn)這種警告呢???
如果正常的使用[self btnClick], btnClick這個(gè)方法沒有實(shí)現(xiàn),系統(tǒng)在編譯階段會(huì)報(bào)錯(cuò),否則會(huì)走正常的流程調(diào)用方法。
如果使用上述方式,一切方法都是在運(yùn)行時(shí)進(jìn)行處理的,不知道有沒有實(shí)現(xiàn)該方法,只有在運(yùn)行時(shí)才會(huì)知道有沒有該方法,有則執(zhí)行,沒有發(fā)生crash。
這種警告怎么消除?讓我們繼續(xù)往下看。
3.5 IMP
/// A pointer to the function of a method implementation.
void (*IMP)(id, SEL, ...)
// id接受消息對(duì)象的id,SEL選擇器,返回一個(gè)void
是一個(gè)指針,函數(shù)的指針,指向方法實(shí)現(xiàn)的首地址。
我們可以根據(jù)選擇器SEL,獲取對(duì)應(yīng)的函數(shù)指針I(yè)MP,然后我們就可以像調(diào)用C的函數(shù)一樣使用函數(shù)指針。通過SEL獲取IMP的函數(shù)指針,這樣我們可以跳出runtime的運(yùn)行機(jī)制消息傳遞,直接執(zhí)行IMP的函數(shù)實(shí)現(xiàn)。這樣比直接向?qū)嵗龑?duì)象發(fā)送消息高效。
我們可以這樣獲取IMP:
method_getImplementation(Method m); // 獲取任意方法法指針
[self methodForSelector:sel]; // 獲取本類中的方法指針
我們可以通過如下轉(zhuǎn)換,將上面提到的警告消除。
SEL sel = NSSelectorFromString(@"btnClick");
IMP imp = [self methodForSelector:sel];
void (*func)(id, SEL) = (void*)imp;
func(self, sel);
主要去看IMP的定義 void (*IMP)(id, SEL),將現(xiàn)有的函數(shù)指針轉(zhuǎn)換成C的形式實(shí)現(xiàn)。
3.6 Method
// 方法鏈表
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
struct objc_method {
SEL method_name; // 方法名
char *method_types; // 類型 char指針 存儲(chǔ)參數(shù)和返回值
IMP method_imp; // 函數(shù)指針
}
在上面的objc_class的定義中有objc_method_list變量用來存儲(chǔ)方法列表,而Method的結(jié)構(gòu)體中,可以看出,存儲(chǔ)的是SEL <-> IMP的映射。
3.7 Cache
在看看cache的結(jié)構(gòu)體,cache存儲(chǔ)用過的方法
struct objc_cache {
unsigned int mask // total = mask + 1
OBJC2_UNAVAILABLE;
unsigned int occupied
OBJC2_UNAVAILABLE;
Method buckets[1]
OBJC2_UNAVAILABLE;
};
mask: 指定分配cache buckets的總數(shù)。在方法查找中,runtime使用這個(gè)字段確定數(shù)組的索引位置。
occupied: 實(shí)際占用cache buckets的總數(shù)。
buckets: 指定Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個(gè)數(shù)組可能包含不超過mask+1個(gè)元素。需要注意的是,指針可能是NULL,表示這個(gè)緩存bucket沒有被占用,另外被占用的bucket可能是不連續(xù)的。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長(zhǎng)。
3.8 Ivar
typedef struct objc_ivar *Ivar;
Ivar代表類中實(shí)例變量的類型。
objc_ivar的定義如下:
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; // 變量名
char *ivar_type OBJC2_UNAVAILABLE; // 變量類型
int ivar_offset OBJC2_UNAVAILABLE; // ?基地址偏移字節(jié)
#ifdef __LP64__
int space OBJC2_UNAVAILABLE; // 占用空間
#endif
}
class中提到的objc_ivar_list,定義如下:
struct objc_ivar_list {
int ivar_count OBJC2_UNAVAILABLE; // 變量個(gè)數(shù)
#ifdef __LP64__
int space OBJC2_UNAVAILABLE; // 占用大小
#endif
/* variable length structure */
struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE; // 變量數(shù)組
}
3.9 objc_property_t
typedef struct objc_property *objc_property_t;
objc_property_t是屬性。與之相關(guān)聯(lián)的還有一個(gè)objc_property_attribute_t。
/// Defines a property attribute
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
說了這么多,是不是覺得沒毛用?讓我們進(jìn)入正題。
4. 消息
4.1 objc_msgSend 動(dòng)態(tài)綁定
在OC中,消息在運(yùn)行時(shí)才會(huì)綁定到方法的實(shí)現(xiàn)上。編譯器會(huì)將消息表達(dá)式[receiver message]轉(zhuǎn)化為一個(gè)消息函數(shù)的調(diào)用,即objc_msgSend,這個(gè)函數(shù)將消息的接收者和方法名作為參數(shù)
objc_msgSend(receiver, selector)。
receiver:消息的接收者。
selector:方法的選擇器。
<b>這個(gè)函數(shù)完成了動(dòng)態(tài)綁定的所有事情:</b>
- 找到selector對(duì)應(yīng)的方法實(shí)現(xiàn),因?yàn)橥粋€(gè)方法可能在不同的的類中有不同的實(shí)現(xiàn),所以我么需要依賴接收者的類來找到確切的實(shí)現(xiàn)。
- 調(diào)用方法的實(shí)現(xiàn),并將接收者對(duì)象及方法的所有參數(shù)傳給它
- 將實(shí)現(xiàn)返回的值作為自己的返回值。
objc_msgSend每調(diào)用一次方法后,就會(huì)把該方法緩存到cache列表中,下次的時(shí)候,就直接優(yōu)先從cache列表中尋找,如果cache沒有,才從methodLists中查找方法。
- 當(dāng)消息發(fā)送給一個(gè)對(duì)象時(shí),首先從運(yùn)行時(shí)系統(tǒng)緩存使用過的方法中尋找(cache),如果找到,執(zhí)行該方法,如果未找到繼續(xù)執(zhí)行
-
objc_msgSend通過對(duì)象的isa指針獲取到類的結(jié)構(gòu)體,然后在方法列表中查找對(duì)應(yīng)的selector,如果沒有找到則在指向父類的方法列表中尋找,依次、最后到NSObject,如果找到,加入緩存Cache,沒有找到,則會(huì)走消息轉(zhuǎn)發(fā)流程。 - 這里需要注意的是,實(shí)例方法(-方法)存在對(duì)應(yīng)類的方法列表中,而類方法(+方法)存在對(duì)應(yīng)元類的方法列表中。
4.2 消息轉(zhuǎn)發(fā)
也有說是動(dòng)態(tài)方法解析,動(dòng)態(tài)方法決議。都是一個(gè)意思。
消息轉(zhuǎn)發(fā)機(jī)制基本上分為三個(gè)步驟:
- 動(dòng)態(tài)方法解析
- 備用接收者
-
完整轉(zhuǎn)發(fā)
消息轉(zhuǎn)發(fā).png
當(dāng)發(fā)送一個(gè)消息的時(shí)候,先從cache列表中尋找,有的話執(zhí)行,沒有從這個(gè)實(shí)例方法或者類方法的方法列表中查找,有的話執(zhí)行,沒有的話:動(dòng)態(tài)轉(zhuǎn)發(fā)第一步
- 方法解析。調(diào)用
+(BOOL)resolveInstanceMethod:(SEL)sel。(+resolveClassMethod)這個(gè)方法,叫動(dòng)態(tài)方法解析(決議)。在這個(gè)方法中,我們有機(jī)會(huì)為該未知消息新增一個(gè)”方法”“。不過使用該方法的前提是我們已經(jīng)實(shí)現(xiàn)了該”方法”,只需要在運(yùn)行時(shí)通過class_addMethod函數(shù)動(dòng)態(tài)添加到類里面就可以了?;蛘咛鎿Q為已知的方法。 - 備用接受者。如果返回NO,動(dòng)態(tài)轉(zhuǎn)發(fā)第二步
- (id)forwardingTargetForSelector:(SEL)aSelector,runtime會(huì)繼續(xù)調(diào)用這個(gè)方法,如果實(shí)現(xiàn)了這個(gè)方法并返回一個(gè)非nil的值,則這個(gè)返回值將作為新的消息接收者。如果沒有實(shí)現(xiàn)該方法, go on 第三步。 - 完整轉(zhuǎn)發(fā)。
- (void)forwardInvocation:(NSInvocation *)anInvocation,如果上一步?jīng)]有處理消息,則runtime走這個(gè)方法。這也是最后一次操作。runtime會(huì)在這個(gè)方法中將消息轉(zhuǎn)發(fā)給其他對(duì)象。不過在執(zhí)行這個(gè)方法前會(huì)首先調(diào)用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector來請(qǐng)求一個(gè)簽名,從而生成一個(gè)NSInvocation,對(duì)消息進(jìn)行完全轉(zhuǎn)發(fā)。
具體事例看本文的demo。
總結(jié):
- 我們可以通過2、3來模擬“多繼承”,一個(gè)類中可能會(huì)包含其他的類,當(dāng)這個(gè)類不能實(shí)現(xiàn)該方法時(shí),將方法的接收方改為其他的類,這樣就好像是自己完成了這些操作。
- 多繼承: 將不同的功能集成到一個(gè)對(duì)象中,會(huì)讓這個(gè)對(duì)象變的很大、涉及到的東西很多。
- 消息轉(zhuǎn)發(fā):將功能分解到不同的小的對(duì)象中,通過一種特定的方式將它們連接起來,并做消息轉(zhuǎn)發(fā)。
常用方法:
// 添加
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// cls 需要?jiǎng)討B(tài)決議的類
// name 需要?jiǎng)討B(tài)決議的方法
// imp 需要執(zhí)行的的方法的指針,(函數(shù)指針)
// types 類型 函數(shù)類型(字符串) v:返回值void @:參數(shù)id類型 ":":SEL對(duì)象 i:int d:double
// 獲取實(shí)例
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實(shí)現(xiàn)
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
// 類實(shí)例是否響應(yīng)指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
5. Category分類
/// An opaque type that represents a category.
typedef struct objc_category *Category;
在objc-runtime-new.h中有如下定義
struct category_t {
const char *name; // 指的是class_name,不是category_name
classref_t cls; // 是擴(kuò)展的類對(duì)象,編譯期間不會(huì)被定義,在runtime階段通過name被指定
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties; // 這也是category為什么能添加屬性的原因
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta) {
if (isMeta) return nil; // classProperties;
else return instanceProperties;
}
};
使用場(chǎng)景:
- 給現(xiàn)有的類添加方法;
- 將一個(gè)類的實(shí)現(xiàn)拆分成多個(gè)獨(dú)立的源文件;
- 聲明私有的方法。
// .h文件中添加一個(gè)屬性
@interface People (Add)
@property (nonatomic, copy) NSString *name;
@end
//-------------------------------
// .m中添加方法
static char *PeopleName;
@implementation People (Add)
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, PeopleName, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, PeopleName);
}
@end
set方法中調(diào)用void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)這個(gè)函數(shù):
object 需要關(guān)聯(lián)屬性的對(duì)象
key 需要關(guān)聯(lián)的key值 一般情況下使用靜態(tài)變量static char,
value 需要與key對(duì)應(yīng)的數(shù)據(jù)
policy 關(guān)聯(lián)策略 一種枚舉值
get方法中調(diào)用id objc_getAssociatedObject(id object, void *key)函數(shù)。
精神病院的考試題應(yīng)該就迎刃而解了。
第一題:
上面的例子中[self class]和[super class],接受消息的對(duì)象都是son這個(gè)實(shí)例變量。不同的是,self是在本類的方法中尋找,super則告訴編譯器在父類中的方法列表中尋找。(意思就是。self直接在本類中尋找,找不到,則在父類中尋找;而super則是直接在父類中尋找)。
使用clang編譯兩個(gè)NSLog之后
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0,
NSStringFromClass(((Class (*)(id, SEL))
(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_1,
NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))
(void *)objc_msgSendSuper)((__rw_objc_super){
(id)self, (id)class_getSuperclass(objc_getClass("Son"))
}, sel_registerName("class"))));
這兩個(gè)方法可以看到:
[self class]---> objc_msgSend
向self發(fā)消息 ---> id objc_msgSend(id self, SEL op, ...)
[super class]---> objc_msgSendSuper
super發(fā)消息 ---> id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一個(gè)參數(shù)d結(jié)構(gòu)體定義:
struct objc_super {
__unsafe_unretained id receiver; // 消息的接收者,就是當(dāng)前的 self 也就是son實(shí)例變量
__unsafe_unretained Class super_class; // 當(dāng)前類的父類
};
// 調(diào)用class的方法 函數(shù)實(shí)現(xiàn)
- (Class)class {
return object_getClass(self); // 這里的self指向的是son實(shí)例變量
}
看到這里是不是明白了為什么兩個(gè)輸出都是 Son。第一個(gè),調(diào)用[self class]直接在Son中查找,發(fā)現(xiàn)Son中沒有這個(gè)函數(shù),則去 Father中查找,還是沒有,則去NSObject中查找class,有返回?cái)?shù)據(jù). [super class] 則直接從 Father中查找。
如果我們?cè)贔ather這個(gè)類中,重寫- (Class)class方法
- (Class)class {
return nil;
}
會(huì)輸出什么呢?
nil son
這個(gè)時(shí)候,估計(jì)有人問了,最后都是在 NSObject 中調(diào)用的 class 方法,而在super的結(jié)構(gòu)體中這個(gè)receiver 上面說的是 self,實(shí)例變量son,那是不是super也是從 Son 中開始查找的呢?答案是肯定的不是,看 objc_msgSendSuper 的第二個(gè)參數(shù), class_getSuperclass(), 是從父類中尋找的方法。此時(shí)是先構(gòu)造了super的結(jié)構(gòu)體,已經(jīng)拿到了receiver 指向 self(son實(shí)例變量), 這個(gè)時(shí)候直接從 super 中找方法,不會(huì)理會(huì) Son 中的方法重寫。
第二題
首先,我們需要理解兩個(gè)概念:
isKindOfClass: 判斷當(dāng)前對(duì)象是不是該該類的實(shí)例對(duì)象,或者是繼承自該類的實(shí)例對(duì)象。
isMemberOfClass: 只能判斷當(dāng)前對(duì)象是不是該類的實(shí)例對(duì)象。
+ (Class)class {
return self;
}
// class的類方法返回本身self。
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
根據(jù)上面的源碼我們看到
isKindOfClass:的實(shí)現(xiàn)方法中比較的是類,如果相等返回YES,否則找 superclass 繼續(xù)比較,直到最后一個(gè) superclass 比較結(jié)束
isMemberOfClass: 則是直接比較當(dāng)前class
所以上面的例子中,第一個(gè)是 YES,其它都是NO。
根據(jù)上圖。我們知道,(id)[NSObject class]這是一個(gè)類對(duì)象,是由NSObject的元類創(chuàng)建的(isa指向元類),這時(shí)比較,兩個(gè)類不一樣,NSObject的元類的superClass指向NSObject本身。所以,第一個(gè)為YES。
第三題:
1是類方法,但是并沒有實(shí)現(xiàn)該類方法。所以在編譯的時(shí)候相當(dāng)于把這段方法的聲明注銷掉了。根據(jù)之前的例子,類對(duì)象尋找方法是在它的元類中找,NSObject的元類沒有此方法,這是在類的分類中添加的一個(gè)方法,所以元類中沒有,NSObject 的元類的父類是NSObject,在NSObject的方法列表中查找,發(fā)現(xiàn)了此方法,存入到元類的方法列表中。輸出結(jié)果。
2是實(shí)例方法,所以在NSObject的方法列表中找,找到,存入緩存,輸出結(jié)果。
這是在NSObject的分類中添加的方法,如果在自己寫的一個(gè)類(People: NSObject)的分類中添加這樣的方法,輸出會(huì)怎么樣?
@interface People : NSObject
@end
@interface People (Sark)
+ (void)foo;
@end
@implementation People (Sark)
- (void)foo {
NSLog(@"IMP: -[NSObject (Sark) foo]");
}
@end
// 測(cè)試代碼
[People foo]; // 這里會(huì)crash。
[[People new] foo];
之所以會(huì)發(fā)生crahs,是因?yàn)椋?foo方法沒有在People的元類中,People的元類的SuperClass是NSObject的元類,也沒有此法。NSObject的元類的superClass是NSObject,也沒有此方法,所以crash。
第四題
具體的內(nèi)存入棧方式是怎樣,沒有了解過,之后做處理,先留一個(gè)坑,歡迎大神們做解答,謝謝。
可以看原文是怎么解釋的,希望能看懂。
傳送門
總結(jié)
runtime的實(shí)際用例:
Method Swizzling,分類添加屬性、 字典轉(zhuǎn)模型等。 有空再整理。
引用
寫的不好,歡迎各位大神指正。喜歡的點(diǎn)贊,加個(gè)關(guān)注,謝謝!
