美文网首页
iOS 内存的区域划分与使用

iOS 内存的区域划分与使用

作者: 山杨 | 来源:发表于2021-09-28 14:14 被阅读0次
    image.png

    代码段

    存放编译后的代码

    数据段(内存分配:从低地址到高地址)

    字符串常量:如: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 栈空间地址打印可以得到栈空间中保存的对象

    相关文章

      网友评论

          本文标题:iOS 内存的区域划分与使用

          本文链接:https://www.haomeiwen.com/subject/vvwlnltx.html