一 iOS程序的內存布局

代碼段:編譯之后的代碼-
數(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)化NSNumber、NSDate、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í)行結果

圖解說明

四 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底層原理教程,非常感謝