有些东西可能只有串到一起才能理解的更加深刻。
有这么一个person类
@interface XHPerson : NSObject
@property (copy, nonatomic) NSString *name;
- (void)print;
@end
@implementation XHPerson
- (void)print
{
NSLog(@"my name is %@", self->_name);
}
@end
然后我们在viewController中这么写
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = @"123";
id cls = [XHPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
@end
那么现在有这么两个问题
1.这个项目能否编译成功,为什么?
2.如果能调用成功,上面的print函数打印的结果是什么,为什么?
来看第一个问题,项目能否编译成功
看上面这个问题的时候,先来看下面这种情况。
XHPerson *person = [[XHPerson alloc] init];
struct XHPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
struct NSObject_IMPL {
Class isa;
};
我们知道对象的本质是一个结构体。
所以我们可以得出**person对象 -> isa ->[XHPerson Class] **
我们再回头看
id cls = [XHPerson class];
void *obj = &cls;
[(__bridge id)obj print];
首先cls -> [XHPerson class],obj->cls.
那么obj -> cls -> [XHPerson class].
是不是跟上面的person对象的例子很像?
obj就像前面person对象,通过isa指针,去XHPerson类对象中去找对象方法print,肯定是可以找的到的。所以可以执行成功
就这样第一个问题解决了。
print打印的结果是什么?
栈空间是从高地址到低地址分配
- (void)print
{
NSLog(@"my name is %@", self->_name);
}
要打印的 _name 成员变量,其实是通过self -> 去查找(每个方法都会有两个参数, self 和 _cmd),这里的self就是函数的调用者,[(__bridge id)obj print]是 obj 开始找,而找 _name ,是通过指针地址查找,找得XHPerson_IMPL 结构体,因为这里的 XHPerson_IMPL 里面就两个变量,所以这里查找 _name,就是通过 isa 的地址,跳过8个字节,找到 _name.

所以在controller里,test处于高地址,obj处于低地址。
person对象 -> isa ->[XHPerson Class]
obj -> cls -> [XHPerson class]
我们可以理解为cls == isa。person需要跳过isa找到name,obj需要跳过cls找到test。
所以输出为123.
如果去掉test这一行呢?
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [XHPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
在这里正好就用到上一篇总结到的super调用方法的原理中的内容。
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
这里是存在一个objc_super的结构体。
栈是由高到底的内存地址分布,结构体由低到高
struct abc = {
self,
[ViewController class]
};
objc_msgSendSuper2(abc, sel_registerName("viewDidLoad"));
就可以转换成下面的这种关系

我们可以理解为cls == isa。person需要跳过isa找到name,obj需要跳过cls找到的是self。
所以print打印的结果是viewcontroller
网友评论