代码段
存放编译后的代码
数据段(内存分配:从低地址到高地址)
字符串常量:如:NSString *str = @"xxx";
已初始化数据:已初始化的全局变量、静态变量等
static
修饰的变量和全局变量一样地址唯一
未初始化数据:如:int a;
栈(内存分配:从高地址到低地址)
函数调用开销
堆(内存分配:从低地址到高地址)
通过alloc、malloc、calloc
等动态分配的内存空间
以下内容都是由一道面试题引发的
下面的代码能正常运行吗?如果可以输出结果是什么?为什么?
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Class fruit = [YSFruit class];
void *obj = &fruit;
[(__bridge id)obj growUp];
}
@end
@interface YSFruit : NSObject
@property (nonatomic, copy) NSString *name;
- (void)growUp;
@end
@implementation YSFruit
- (void)growUp {
NSLog(@"%@在生长", self.name);
}
@end
结果打印的是:<ViewController>在生长
分析猜测:在growUp方法中self.name的值是ViewController对象,也就是说访问self.name的内存地址和ViewController对象的内存地址相同。又因为(__bridge id)obj中并不存在name属性,所以在使用self.name的时候obj的指针应该是指向了更的高的栈空间地址,而这个地址处刚好存放了ViewController的地址。所以才有了
<ViewController>在生长
这样的打印结果。
验证一下猜测:
- 打印obj的下一个指针地址验证
long long *p = (long long *)obj; 打印po *(p+1)得到的是ViewController对象
- 在obj和ViewController的地址之间插入一个字符串会不会变成另外一个结果
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
};
// 通过编译成汇编代码得知,[super viewDidLoad]实际是调用objc_msgSendSuper2()
- (void)viewDidLoad {
struct objc_super superObjc = {self, objc_getClass("ViewController")};
objc_msgSendSuper2(superObjc, @selector(viewDidLoad));
// 在obj和ViewController的地址之间插入一个字符串
NSString *apple = @"苹果";
Class fruit = [YSFruit class];
// obj指向的是YSFruit类对象和YSFruit的实例对象的isa指针相同
// 可以看做obj = [YSFruit new]->isa;
void *obj = &fruit;
// OC对象本质是以一个isa成员为基础的结构体
// 由于obj指向YSFruit的类对象,(__bridge id)obj就相当于只有一个isa的YSFruit的一个实例对象
[(__bridge id)obj growUp];
}
打印结果:苹果在生长
- 那么就验证了之前的猜测
原因是栈空间临时变量是按照从高地址到低地址进行存放的,所以通过p/x & obj
打印可以获取到obj的栈空间地址
,再通过obj的地址
向高位查找,使用x/4g obj的栈空间地址
打印得到了apple的地址
,如果拿掉apple字符串
,那么x/4g obj的栈空间地址
打印得到的就是ViewController的地址
- 使用
po 栈空间地址
打印可以得到栈空间中保存的对象
网友评论