示例
#import "NSPerson.h"
@interface NSWorker : NSPerson
@property NSString *name;
- (void)print;
@end
#import "NSWorker.h"
@implementation NSWorker
- (void)person
{
[super person];
id cls = [self class];
void *obj = &cls;
[(__bridge id)obj print];
}
#import "NSWorker.h"
int main()
{
[[NSWorker new] person];
}
//log
2020-08-28 20:10:31.537720+0800 runtime[9289:160822] my name is <NSWorker: 0x10071f120>
Program ended with exit code: 0
图示
示意图分析1
对比上图两种方法调用方式,可以看出:
1>通过obj调用print: 是直接找到NSWorker类,然后调用print
2>通过worker调用print: 是通过worker对象内部的isa&isa_mask找到NSWorker类,然后调用print
结论,
可以调用成功
分析2
两种方式的区别
1>obj调用print:局部变量obj与cls在函数栈空间是连续的
,即局部变量obj与cls(NSWorker)内部isa内存地址是连续的
2>worker调用print:局部变量worker与函数栈空间其他局部变量是连续的
,worker 调用print是通过worker内部isa & isa_mask找到NSWorker类(isa),所以worker与NSWorker内部的isa的内存地址是不连续的
结论:print内部访问结构体NSWorker_IMPL的name成员,是访问其isa后面的8个字节
1>obj调用print:因为obj直接指向了cls = NSWorker_IMPL(isa),所以相当于访问cls后面8个字节,name的值是pls前面的那个局部变量的值
2>worker调用print:因为worker是间接指向NSWorker_IMPL,所以访问的8个字节的值不会是函数栈空间的其他局部变量
分析3
为什么输出结果是
my name is <NSWorker: 0x10071f120>
- (void)person
{
[super person];
id cls = [self class];
void *obj = &cls;
[(__bridge id)obj print];
}
通过分析1和分析2
person方法内部的局部变量有:
obj
cls
objc_super objc_s = {self , [self class]};
上面三个地址由低到高,而obj = cls->isa,所以后面8个字节是objc_s的前8个字节,即self(当前对象)
网友评论