***********************?MJPerson.h ?**************************
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject
@property (copy, nonatomic) NSString *name;
- (void)print;
@end
***********************?MJPerson.m ?**************************
#import "MJPerson.h"
@implementation MJPerson
- (void)print
{
NSLog(@"my name is %@", self->_name);
}
@end
@implementation ViewController
/*
1.print为什么能够调用成功?
2.为什么self.name变成了ViewController等其他内容
*/
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [MJPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
@end
RUN>
*********************** ?运行结果? ************************** 2021-05-08 16:03:34.586222+0800 Interview02-super[4242:166676] my name is <ViewController: 0x7fb47340ecf0>
1.print为什么能够调用成功?
2.为什么self.name变成了ViewController等其他内容
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = @"123";
id cls = [MJPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
RUN>
*********************** ?运行结果? ************************** 2021-05-08 16:05:32.180416+0800 Interview02-super[4288:170283] my name is 123
很奇怪,怎么会这样呢? 首先第一点是为什么会调用到MJPerson
的- (void)print
方法,第二,局部变量 NSString *test = @"123";
怎么就变成了person的实例成员。
- (void)viewDidLoad {
[super viewDidLoad];
//调用print
MJPerson *person = [[MJPerson alloc]init];
[person print];
}
RUN>
*********************** ?运行结果? ************************** 2021-05-09 19:55:04.568600+0800 Interview02-super[2315:98649] my name is (null)
id cls = [MJPerson class];
void *obj = &cls;
[(__bridge id)obj print];
这段代码在内存结构上发生了什么情况
[(__bridge id)obj print];
调用了能够调用到MJPerson
的- (void)print
方法。那我们把obj看出person的instance会是什么情况
他们的内存结构何其相似.cls在这里不就等价于
isa么?他们都指向
MJPerson
类对象.
[person print];通过实例对象 person
的isa
指针找到MJPerson 类对象
,然后从类对象的方法列表中查找方法.所以,方法的调用本质就是只要能找到类对象.而[(__bridge id)obj print];,
指针变量obj
中存储的cls
恰巧就指向类对象,当消息系统objc_msgSend
把obj当成类了,在他所指向的地址,获取前8位地址作为类的isa指针,指向了MJPerson 类对象
,所以能够当成MJPerson类,所以最后能调用成功.
接着分析 my name is 123
这个局部变量 NSString *test = @"123";
怎么就变成了person的实例成员。
// 局部变量分配在栈空间
// 栈空间分配,从高地址到低地址
void test()
{
long long a = 4; // 0x7ffee638bff8
long long b = 5; // 0x7ffee638bff0
long long c = 6; // 0x7ffee638bfe8
long long d = 7; // 0x7ffee638bfe0
NSLog(@"%p %p %p %p", &a, &b, &c, &d);
}
int main(int argc, char * argv[]) {
@autoreleasepool {
test();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
RUN>
*********************** ?运行结果? ************************** 2021-05-09 20:30:17.556720+0800 Interview02-super[2557:120622] 0x7ffeed6a7c28 0x7ffeed6a7c20 0x7ffeed6a7c18 0x7ffeed6a7c10
从打印结果中可以看到,栈内存分配空间是从高地址往低地址分配的,<font color='red'>先创建的局部变量分配在高地址,后创建的分配在低地址</font>.
NSString *test = @"123";
id cls = [MJPerson class];
void *obj = &cls;
[(__bridge id)obj print];
继续查看这段代码的内存结构
上面我们已经分析过,把cla看出isa指针,isa指针后面8个地址就是_name 属性指向地址。在获取
self.name的时候,如果转换成汇编代码会发现本质上就是找到
isa指针,然后越过8个字节,从而找到
_name.这就是内存访问的本质:找到某块内存的地址,读取地址中的值.
[(__bridge id)obj print];
也会同样的越过cls
这8个字节找到test
.所以最后就打印的是my name is 123
;
super 关键字的一点补充
在前面讲super
关键字的时候,我们看到super
转换为c++
代码的时候,被转换成了objc_msgSendSuper(arg1,arg2)
函数.其实实际上底层执行并不是objc_msgSendSuper(arg1,arg2)
函数,而是objc_msgSendSuper2(arg1,arg2)
函数.我们在[super viewDidLoad];
处打个断点,然后显示汇编语言看一下:
并且上面讲的objc_msgSendSuper(arg1,arg2)
中的第一个参数arg1
是__rw_objc_super
结构体,这个结构体如下:
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
}
而objc_super2
的结构体如下:
struct objc_super2 {
id receiver;
Class current_class;
};
可以看到objc_super2
这个结构体中传入的是Class current_class;
也就是当前类.而在_objc_msgSendSuper2
内部获取当前类的superClass
:
ENTRY _objc_msgSendSuper2
UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
CacheLookup NORMAL
END_ENTRY _objc_msgSendSuper2
ENTRY _objc_msgLookupSuper2
UNWIND _objc_msgLookupSuper2, NoFrame
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
CacheLookup LOOKUP
END_ENTRY _objc_msgLookupSuper2
所以,super
底层其实是调用objc_msgSendSuper2()
函数,然后传入的是当前类对象,只不过在内部又回取出当前类对象的superclass
.
这只是一个小细节,和我们最开头说的也不矛盾,知悉就好.
特别备注
本系列文章总结自MJ老师在腾讯课堂iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化,相关图片素材均取自课程中的课件。如有侵权,请联系我删除,谢谢!