美文网首页iOS开发经验集
iOS-底层原理(26)-内存管理之内存布局+Tagged Po

iOS-底层原理(26)-内存管理之内存布局+Tagged Po

作者: 路飞_Luck | 来源:发表于2018-09-24 21:57 被阅读60次
    一 iOS程序的内存布局
    image.png
    • 代码段:编译之后的代码

    • 数据段

      • 字符串常量:比如NSString *str = @"123"
      • 已初始化数据:已初始化的全局变量、静态变量等
      • 未初始化数据:未初始化的全局变量、静态变量等
    • 栈:函数调用开销,比如局部变量。分配的内存空间地址越来越小

    • 堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大

    代码例子如下

    int a = 10;
    int b;
    
    implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self test1];
    }
    
    - (void)test1 {
        static int c = 20;
        
        static int d;
        
        int e;
        int f = 20;
        
        NSString *str = @"123";
        
        NSObject *obj = [[NSObject alloc] init];
    
        NSLog(@"\n&a=%p\n&b=%p\n&c=%p\n&d=%p\n&e=%p\n&f=%p\nstr=%p\nobj=%p\n",
              &a, &b, &c, &d, &e, &f, str, obj);
    }
    

    分析结果如下

    /*
     字符串常量
     str=0x1029a4068
     
     已初始化的全局变量、静态变量
     &a =0x1029a4db8
     &c =0x1029a4dbc
     
     未初始化的全局变量、静态变量
     &d =0x1029a4e80
     &b =0x1029a4e84
     
     堆
     obj=0x60400001b510
     
     栈
     &f =0x7ffeed25a990
     &e =0x7ffeed25a994
     */
    
    二 Tagged Pointer
    • 从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumberNSDateNSString等小对象的存储

    • 在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值

    • 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中

    • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据

    • objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销

    • 如何判断一个指针是否为Tagged Pointer

      • iOS平台,最高有效位是1(第64bit)
      • Mac平台,最低有效位是1
    三 判断是否为Tagged Pointer
    // 如果是iOS平台(指针的最高有效位是1,就是Tagged Pointer)
    #   define _OBJC_TAG_MASK (1UL<<63)
    
    // 如果是Mac平台(指针的最低有效位是1,就是Tagged Pointer)
    #   define _OBJC_TAG_MASK 1UL
    
    - (BOOL)isTaggedPointer:(id)pointer {
        return ((uintptr_t)pointer & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
    }
    

    调用

    // 是否是tagger pointer
    - (void)test3 {
        NSNumber *number1 = @4;
        NSNumber *number2 = @5;
        NSNumber *number3 = @(0xFFFFFFFFFFFFFFF);
        
        NSLog(@"%d %d %d", [self isTaggedPointer:number1], [self isTaggedPointer:number2], [self isTaggedPointer:number3]);
        NSLog(@"%p %p %p", number1, number2, number3);
    }
    

    执行结果

    image.png

    图解说明

    image.png
    四 MRC
    1.MRC环境下Set方法内部实现
    • assign修饰
    @property (nonatomic, assign) int age;
    
    - (void)setAge:(int)age {
        _age = age;
    }
    
    - (int)age {
        return _age;
    }
    
    • retain修饰
    @property (nonatomic, retain) Dog *dog;
    
    - (void)setDog:(Dog *)dog {
        if (_dog != dog) {
            [_dog release];
            _dog = [dog retain];
        }
    }
    
    - (Dog *)dog {
        return _dog;
    }
    
    • copy修饰
    @property (copy, nonatomic) NSArray *data;
    
    - (void)setData:(NSArray *)data {
        if (_data != data) {
            [_data release];
            _data = [data copy];
        }
    }
    
    五 OC对象的内存管理
    • 在iOS中,使用引用计数来管理OC对象的内存

    • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间

    • 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1

    内存管理的经验总结
    • 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它

    • 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1

    • 可以通过以下私有函数来查看自动释放池的情况
      pextern void _objc_autoreleasePoolPrint(void);

    // 声明一
    NSMutableArray *data = [[NSMutableArray alloc] init];
    self.data = data;
    [data release];
    
    // 声明二
    NSMutableArray *data = [NSMutableArray array];
    self.data = data;
    

    两个声明方式相同,只是一个有调用release操作,一个没有。因为array内部会调用autorelease方法。


    本文参考MJ底层原理教程,非常感谢


    项目连接地址 - MemoryManage-CADisplayLink+Timer

    相关文章

      网友评论

        本文标题:iOS-底层原理(26)-内存管理之内存布局+Tagged Po

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