下面的代码能编译执行吗?如果能执行的话,打印是什么?
@interface MJPerson : NSObject
@property (nonatomic, strong) NSString *name;
- (void)test;
@end
@implementation MJPerson
- (void)test
{
NSLog(@"my name is %@",self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = MJPerson.class;
void *obj = &cls;
[(__bridge id)obj test];
}
能执行,打印结果是my name is <ViewController: 0x7f99bac0e740>
分析:
实例方法 test 是存在于类对象MJPerson的方法列表中的,obj是个指针,指向MJPerson类对象的指针,所以能调用成功 ; 如果MJPerson中压根没有这个方法的实现并且没有针对这个方法做动态解析和消息转发,肯定就不能调用成功
在内存分配的时候,局部变量cls,obj都是分配在栈上的,并且从高地址向低地址分配,并且占8个字节的长度,再分析test方法中的打印self.name,成员属性是存在于成员对象中的,在成员属性中除了_name外还存在一个isa指针,isa指针指向类对象,占据8字节单位,并且在高位地址值,name的地址值紧挨着也占据8个字节单位,所以obj指针调用test方法,打印的self->name,其实这个地址值是此时指向的MJPerson类在往上数8位的高地址空间
NSObject *obj2 = [[NSObject alloc] init];
id cls = MJPerson.class;
void *obj = &cls;
[(__bridge id)obj test];
这个时候,obj2在高位,占据8字节,cls紧挨着,所以此时的打印结果会是my name is <NSObject: 0x7f99bac0e740>
至于一开始打印的ViewController,则是因为方法 [super viewDidLoad]
的原因,这个方法的本质是
objc_msgSendSuper({
self,
[UIViewController class]
},@selector(viewDidLoad));
等价于
struct str = {
self,
[UIViewController class]
};
objc_msgSendSuper(str,@selector(viewDidLoad));
此时的内存分配图如下
cls上的高地址前一位存储的是self,所以打印ViewController