iOS底层 -- 内存管理之内存布局、Tagged Pointer

一、iOS程序的内存布局
iOS底层 -- 内存管理之内存布局、Tagged Pointer_第1张图片
  • 代码段:编译之后的代码

  • 数据段

    • 字符串常量:比如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

 栈
 &e =0x7ffeed25a994
 &f =0x7ffeed25a990
 */

二、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);
}

执行结果

图解说明

iOS底层 -- 内存管理之内存布局、Tagged Pointer_第2张图片
四、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);

你可能感兴趣的:(iOS底层 -- 内存管理之内存布局、Tagged Pointer)