前言
看ReactiveCocoa
源码的时候,被RACSwizzleClass
卡住了,做了以下研究,并把注释后的代码放在文章最后,如果不想看过程可以直接跳到最后。
RACSwizzleClass
中通过判断[obj class]
和 object_getClass(obj)
是否相同来执行不同的逻辑。
然而[obj class]
和object_getClass(obj)
有什么区别?他们不同到底意味着什么?
class和object_getClass
为此查阅了objc runtime的源代码,并整理了相关代码:get class 相关代码
结论:
对于一个普通的obj(不是class),[obj class]
和 object_getClass(obj)
会返回一样的结果,就是该对象所属的类。
对于一个class,[aclass class]
还是会返回该class,而object_getClass(aclass)
会溯本归源返回aclass的isa
,一般是返回该类的meta class
对象的isa
链会一直指向哪里?见下图:
(图片来自http://blog.devtang.com/2013/10/15/objective-c-object-model/ )
图中的Root class
一般是NSObject
,当你不停的调用object_getClass
,你最终会获取到NSObject
的meta class
。
isa-swizzling
既然对于一个普通的obj(不是class),[obj class]
和 object_getClass(obj)
会返回一样的结果,那么RACSwizzleClass
为什么要做相等性判断?
在苹果的文档中稍稍提到了一些:
Key-Value Observing Implementation Details
简单的说:
系统的KVO,是用isa-swizzling
实现的。
isa
指针,指向对象所属的类,类里面存储的是方法列表及其他一些信息。
当你给一个对象添加了observer之后,系统会修改该对象的isa
指针,使其指向一个中间类(中间类重写了setting方法以实现KVO),这时的isa
,就不是指向对象实际的类了。
所以,你应该用class
方法(因为系统同时重写了class方法使其返回对象原来的类),而不是isa
(object_getClass)来取得对象所属的类。
写了个小小的测试程序来验证:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
- (void)test{
//Now create an instance 'myobj' of class 'MyObject'
NSLog(@"Now create an instance 'myobj' of class 'MyObject'");
MyObject *myobj = [[MyObject alloc] init];
Class aclass = [myobj class];
NSLog(@"[myobj class] returns:%@(%p)", aclass, aclass);
Class aclass2 = object_getClass(myobj);
NSLog(@"object_getClass(myobj) returns:%@(%p)", aclass2, aclass2);
Class aclass3 = object_getClass([myobj class]);
NSLog(@"object_getClass([myobj class]) returns:%@(%p)", aclass3, aclass3);
//Now recursively call 'class'
NSLog(@"Now recursively call 'class'");
id obj = myobj;
for (int i=0; i<4; i++) {
Class aclass = [obj class];
obj = aclass;
NSLog(@"pass%d [obj class] returns %@(%p)", i+1, aclass, aclass);
}
// Now recursively call 'object_getClass'.
NSLog(@"Now recursively call 'object_getClass'");
obj = myobj;
for (int i=0; i<4; i++) {
Class aclass = object_getClass(obj);
obj = aclass;
NSLog(@"pass%d object_getClass(obj) returns %@(%p)", i+1, aclass, aclass);
}
// Now add KVO to myobj.
NSLog(@"Now add KVO to myobj.");
[myobj addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"After KVO. [myobj class] returns %@(%p)", [myobj class], [myobj class]);
NSLog(@"After KVO. object_getClass(myobj) returns %@(%p)", object_getClass(myobj), object_getClass(myobj));
myobj.title = @"Hello, this is new title!";
// Now remove KVO of myobj.
NSLog(@"Now remove KVO of myobj.");
[myobj removeObserver:self forKeyPath:@"title"];
NSLog(@"KVO removed. [myobj class] returns %@(%p)", [myobj class], [myobj class]);
NSLog(@"KVO removed. object_getClass(myobj) returns %@(%p)", object_getClass(myobj), object_getClass(myobj));
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"title"]) {
NSLog(@"KVO title changed:%@", [change objectForKey:NSKeyValueChangeNewKey]);
}
}
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2016-03-07 17:19:09.702 testOC[3925:131380] Now create an instance 'myobj' of class 'MyObject'
2016-03-07 17:19:09.704 testOC[3925:131380] [myobj class] returns:MyObject(0x106520120)
2016-03-07 17:19:09.704 testOC[3925:131380] object_getClass(myobj) returns:MyObject(0x106520120)
2016-03-07 17:19:09.704 testOC[3925:131380] object_getClass([myobj class]) returns:MyObject(0x1065200f8)
2016-03-07 17:19:09.705 testOC[3925:131380] Now recursively call 'class'
2016-03-07 17:19:09.705 testOC[3925:131380] pass1 [obj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.705 testOC[3925:131380] pass2 [obj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.705 testOC[3925:131380] pass3 [obj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.705 testOC[3925:131380] pass4 [obj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.705 testOC[3925:131380] Now recursively call 'object_getClass'
2016-03-07 17:19:09.706 testOC[3925:131380] pass1 object_getClass(obj) returns MyObject(0x106520120)
2016-03-07 17:19:09.706 testOC[3925:131380] pass2 object_getClass(obj) returns MyObject(0x1065200f8)
2016-03-07 17:19:09.706 testOC[3925:131380] pass3 object_getClass(obj) returns NSObject(0x106d7b198)
2016-03-07 17:19:09.712 testOC[3925:131380] pass4 object_getClass(obj) returns NSObject(0x106d7b198)
2016-03-07 17:19:09.712 testOC[3925:131380] Now add KVO to myobj.
2016-03-07 17:19:09.713 testOC[3925:131380] After KVO. [myobj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.713 testOC[3925:131380] After KVO. object_getClass(myobj) returns NSKVONotifying_MyObject(0x7fc66b61f000)
2016-03-07 17:19:09.713 testOC[3925:131380] KVO title changed:Hello, this is new title!
2016-03-07 17:19:09.713 testOC[3925:131380] Now remove KVO of myobj.
2016-03-07 17:19:09.714 testOC[3925:131380] KVO removed. [myobj class] returns MyObject(0x106520120)
2016-03-07 17:19:09.714 testOC[3925:131380] KVO removed. object_getClass(myobj) returns MyObject(0x106520120)
总结:
苹果的isa-swizzling
(也就是class-swizzling
)会更改对象的isa
指针,并且会重写class
方法以隐藏class已经改变的事实。。。
如果有兴趣,这还一篇文档教你怎么自己实现KVO:
如何自己动手实现 KVO
在搜索的时候,搜到了stackoverflow上类似的一个问题,然而点赞最多的回答并不准确,我在后面附上了准确的回答:
object_getClass(obj) and [obj class] give different results
附注释后代码
现在RACSwizzleClass
的代码就可以读得通了,附上注释如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// file NSObject+RACSelectorSignal.m
static Class RACSwizzleClass(NSObject *self) {
Class statedClass = self.class; // 声明的类
Class baseClass = object_getClass(self); // 实际指向的类
NSString *className = NSStringFromClass(baseClass);
if ([className hasSuffix:RACSubclassSuffix]) {
return baseClass; // 已经swizzle过了
} else if (statedClass != baseClass) {
// 正如下面的注释,该对象估计被类似KVO之类的isa-swizzle过了,只能swizzle它的ForwardInvocation方法了
// If the class is already lying about what it is, it's probably a KVO
// dynamic subclass or something else that we shouldn't subclass
// ourselves.
//
// Just swizzle -forwardInvocation: in-place. Since the object's class
// was almost certainly dynamically changed, we shouldn't see another of
// these classes in the hierarchy.
//
// Additionally, swizzle -respondsToSelector: because the default
// implementation may be ignorant of methods added to this class.
@synchronized (swizzledClasses()) {
if (![swizzledClasses() containsObject:className]) {
RACSwizzleForwardInvocation(baseClass);
RACSwizzleRespondsToSelector(baseClass);
[swizzledClasses() addObject:className];
}
}
return baseClass;
}
// 没有isa-swizzle过的,直接isa-swizzle
const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String;
Class subclass = objc_getClass(subclassName);
if (subclass == nil) {
subclass = [RACObjCRuntime createClass:subclassName inheritingFromClass:baseClass];
if (subclass == nil) return nil;
RACSwizzleForwardInvocation(subclass);
RACSwizzleRespondsToSelector(subclass);
objc_registerClassPair(subclass);
}
object_setClass(self, subclass);
return subclass;
}