iOS-底層原理(26)-內存管理之內存布局+Tagged Pointer+MRC

一 iOS程序的內存布局
image.png
  • 代碼段:編譯之后的代碼

  • 數(shù)據(jù)段

    • 字符串常量:比如NSString *str = @"123"
    • 已初始化數(shù)據(jù):已初始化的全局變量、靜態(tài)變量等
    • 未初始化數(shù)據(jù):未初始化的全局變量、靜態(tài)變量等
  • :函數(shù)調用開銷,比如局部變量。分配的內存空間地址越來越小

  • :通過alloc、malloc、calloc等動態(tài)分配的空間,分配的內存空間地址越來越大

代碼例子如下

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
 
 已初始化的全局變量、靜態(tài)變量
 &a =0x1029a4db8
 &c =0x1029a4dbc
 
 未初始化的全局變量、靜態(tài)變量
 &d =0x1029a4e80
 &b =0x1029a4e84
 
 堆
 obj=0x60400001b510
 
 棧
 &f =0x7ffeed25a990
 &e =0x7ffeed25a994
 */
二 Tagged Pointer
  • 從64bit開始,iOS引入了Tagged Pointer技術,用于優(yōu)化NSNumberNSDate、NSString等小對象的存儲

  • 在沒有使用Tagged Pointer之前, NSNumber等對象需要動態(tài)分配內存、維護引用計數(shù)等,NSNumber指針存儲的是堆中NSNumber對象的地址值

  • 使用Tagged Pointer之后,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中

  • 當指針不夠存儲數(shù)據(jù)時,才會使用動態(tài)分配內存的方式來存儲數(shù)據(jù)

  • objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法,直接從指針提取數(shù)據(jù),節(jié)省了以前的調用開銷

  • 如何判斷一個指針是否為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);
}

執(zhí)行結果

image.png

圖解說明

image.png
四 MRC
1.MRC環(huán)境下Set方法內部實現(xiàn)
  • 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中,使用引用計數(shù)來管理OC對象的內存

  • 一個新創(chuàng)建的OC對象引用計數(shù)默認是1,當引用計數(shù)減為0,OC對象就會銷毀,釋放其占用的內存空間

  • 調用retain會讓OC對象的引用計數(shù)+1,調用release會讓OC對象的引用計數(shù)-1

內存管理的經驗總結
  • 當調用alloc、new、copy、mutableCopy方法返回了一個對象,在不需要這個對象時,要調用release或者autorelease來釋放它

  • 想擁有某個對象,就讓它的引用計數(shù)+1;不想再擁有某個對象,就讓它的引用計數(shù)-1

  • 可以通過以下私有函數(shù)來查看自動釋放池的情況
    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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容