[孙源sunnyxx]&神经病院objc runtime入院考试
[转载]神经病院objc runtime入院考试
《神经病眼中的objc runtime》北京线下分享活动顺利完成,为了配合讲解编造的几个runtime考题发出来分享下:
- 为分享内容配合讲解用,可不是为了面试别人的哦(容易被抽)
- 这几个题分别对应了runtime中几个隐蔽的知识点,挺非主流的,没必要深究
- 答案在本页末尾给出,有同学针对这几道题写了讲解,所以就一笔带过了
- 分享的具体内容争取找个时间写个blog总结下
神经病院objc runtime入院考试
(1) 下面的代码输出什么?
@implementation Son : Father
|
(2) 下面代码的结果?
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
|
(3) 下面的代码会?Compile Error / Runtime Crash / NSLog…?
@interface NSObject (Sark)
|
(4) 下面的代码会?Compile Error / Runtime Crash / NSLog…?
@interface Sark : NSObject
|
答案
(1) Son / Son 因为super为编译器标示符,向super发送的消息被编译成objc_msgSendSuper,但仍以self作为reveiver
(2) YES / NO / NO / NO <NSObject>协议有一套类方法的隐藏实现,所以编译运行正常;由于NSObject meta class的父类为NSObject class,所以只有第一句为YES
(3) 编译运行正常,两行代码都执行-foo。 [NSObject foo]方法查找路线为 NSObject meta class –super-> NSObject class,和第二题知识点很相似。
(4)编译运行正常,输出ViewController中的self对象。 编译运行正常,调用了-speak方法,由于
id cls = [Sark class];
|
obj已经满足了构成一个objc对象的全部要求(首地址指向ClassObject),遂能够正常走消息机制;
由于这个人造的对象在栈上,而取self.name的操作本质上是self指针在内存向高位地址偏移(32位下一个指针是4字节),按viewDidLoad执行时各个变量入栈顺序从高到底为(self,
_cmd, self.class, self,
obj)(前两个是方法隐含入参,随后两个为super调用的两个压栈参数),遂栈低地址的obj+4取到了self。
原创文章,版权声明:署名-非商业性使用-相同方式共享 2.5
对博主感兴趣?微信订阅号中关注 sunnyxx 或关注微博@我就叫Sunny怎么了
© 2015 - 2016 sunnyxx | Powered by Hexo
其他参考:https://www.jianshu.com/p/04f84472c1b8
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[TestObject class] isKindOfClass:[TestObject class]];
BOOL res4 = [(id)[TestObject class] isMemberOfClass:[TestObject class]];
答案:除了第一个是YES,其他三个都是NO。
在推测结果之前,首先要明白两个问题。isKindOfClass 和 isMemberOfClass 的区别是什么?
isKindOfClass:
returns YES if the receiver is an instance of the specified class or an instance of any class that inherits from the specified class.
方法调用者是传入的类的实例对象,或者调用者是传入类的继承者链中的类的实例对象,则返回YES。
isMemberOfClass:
returns YES if the receiver is an instance of the specified class.
方法调用者必须是传入的类的实例对象才返回YES。
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->super_class) {
if(tcls == cls) return YES;
}
return NO;
}
-(BOOL)isKindOfClass:(Class)cls {
for(Class tcls = [self class]; tcls; tcls = tcls->super_class) {
if(tcls == cls) return YES;
}
return NO;
}
平时开发过程中只会接触到对象方法的isKindOfClass和isMemberOfClass,但是在NSObject类中还隐式的实现了类方法版本。不只这两个方法,其他NSObject中的对象方法,都有其对应的类方法版本。因为在OC中,类和元类也都是对象。这四个调用由于都是类对象发起调用的,所以最终执行的都是类方法版本。
先把Runtime的对象模型拿出来,方便后面的分析。

因为调用类的class方法,会把类自身直接返回,所以第一次调用者是NSObject类对象,+ (BOOL)isKindOfClass:(Class)cls方法的参数cls也是NSObject类对象。
进入到for循环中,在此说明一下object_getClass()方法相当于取出isa指针指向的类,所以会从NSObject的元类开始遍历,所以第一次NSObject meta class != NSObject class,匹配失败。第二次循环将tcls 设置为superclass的NSObject class,NSObject class==NSObject class,匹配成功。
NSObject 能匹配成功,是因为这个类比较特殊,在第二次获取 superClass的时候,NSObject 元类的superClass就是NSObject的类对象,所以会匹配成功。而其他三种匹配,则都会失败,各位同学可以去自己分析一下剩下三种。
T

浙公网安备 33010602011771号