目录
问题抛出
先来看段代码
Person.h
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, strong) NSString * name;
-(void)print;
@end
NS_ASSUME_NONNULL_END
Person.m
#import "Person.h"
@implementation Person
-(void)print{
NSLog(@"my name is %@",self.name);
}
@end
ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Person class];
void * obj = &cls;
[(__bridge id)obj print];
}
@end
print函数能不能调用成功?如果能调用成功打印结果是什么?
可以调用成功,打印结果如下
my name is <ViewController: 0x100704080>
问题解析
一、为什么可以调用
分析下上边代码的指针指向关系
从指针的指向可以看出cls相当于isa指针,指向Person类对象。obj就可以认为是一个指向Person实例对象的指针。
所以obj可以调用print方法。
二、为什么打印结果是<ViewController: 0x100704080>
1、如果是person对象调用print方法,self成员变量name是如何找到的?是在isa地址的基础上加8字节找到的。那么obj调用print的函数,相应找到的地址应该是cls的内存地址加8个字节,那么这个地址对应的是什么内容?
2、要弄清问题1,就要说下iOS程序的内存分布:代码段 数据段 堆 栈 虚拟内存的地址从低到高
堆空间分配地址从整体上来说由低到高,栈空间正好与它相反,地址的分配由高到低。
将ViewController.m文件通过以下指令编译并精简代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
struct __rw_objc_super s = {
(id)self,
class_getSuperclass(objc_getClass("ViewController"))
}
objc_msgSendSuper)(a,sel_registerName("viewDidLoad"));
id cls = objc_msgSend)(objc_getClass("Person"),sel_registerName("class"));
void * obj = &cls;
objc_msgSend)(obj, sel_registerName("print"));
}
在viewDidLoad方法内局部变量的分布如下
可以看出cls的地址加8字节就是self,这就是为什么打印结果是<ViewController: 0x100704080>的原因。
三、修改代码试下
- (void)viewDidLoad {
[super viewDidLoad];
NSString * name = @"小明";
id cls = [Person class];
void * obj = &cls;
[(__bridge id)obj print];
}
打印日志
my name is 小明
可以按上边的逻辑分析下原因。
行者常至,为者常成!