OC中內(nèi)存分為五大區(qū)域
棧:存儲(chǔ)局部變量和指針
堆:創(chuàng)建對象
BSS:存儲(chǔ)未初始化的全局變量和靜態(tài)變量
數(shù)據(jù)段(常量區(qū)):存儲(chǔ)已經(jīng)初始化的全局變量、靜態(tài)變量、常量數(shù)據(jù)
代碼段:存儲(chǔ)程序的代碼
類加載
當(dāng)類被第一次加載的時(shí)候,會(huì)將類的代碼存儲(chǔ)到代碼段中,這個(gè)過程叫類加載。
只有第一次訪問的時(shí)候,才有類加載。
一旦類被加載到程序中,直到程序結(jié)束的時(shí)候才會(huì)被回收
創(chuàng)建對象
[[NSObject alloc] init];
alloc分配內(nèi)存 init初始化
NSObject.+alloc
struct obj_layout {
NSUInteger retained;
}
+ (id) alloc {
int size = sizeof(struct obj_layout);
struct obj_layout *p = (struct obj_layout *)calloc(1, size);
return (id)(p + 1);
}
銷毀對象
NSObject.release中,首先調(diào)用NSDecrementExtraRefCountWasZero使retrain-1。如果retrainCount == 0,調(diào)用release方法釋放內(nèi)存
NSObject.dealloc
釋放內(nèi)存
GNUstep
alloc類方法通過 struct obj_layout中對retrained整數(shù)來保存引用記數(shù),并將其寫入對象內(nèi)存頭部,將該對象內(nèi)存塊全部置0后返回
也就是,一個(gè)對象的引用記數(shù),放在這個(gè)類地址的最前面。
apple
使用一個(gè)散列表保存所有的引用記數(shù)。將對象的地址映射到散列表中。
autorelease
autorelease會(huì)在超過對象的作用域之后,自動(dòng)調(diào)用對象的release方法(記數(shù)-1。如果記數(shù)==0,調(diào)用dealloc方法)
GNUstep中autorelease的實(shí)現(xiàn)
[obj autorelease]方法的原理就是把對象添加到NSAutoreleasePool中
[NSAutoreleasePool drain]就是調(diào)用填入的所有對象的release方法
apple的實(shí)現(xiàn)
RunLoop和線程
在iOS開發(fā)中,會(huì)遇到兩個(gè)線程對象:pthread_t 和 NSThread,NSThread只是對pthread的封裝,兩者肯定是一一對應(yīng)的,我們可以通過pthread_main_thread_np()或者 [NSThread mainThread]來獲取主線程,也可以通過pthread_self() [NSThread currentThread]來獲取當(dāng)前的線程。
和runloop相關(guān)的有5個(gè)類,分別是
NSDefaultRunLoopMode:app默認(rèn)的mode,通常主線程是在這個(gè)mode下運(yùn)行的
UITrackingRunLoopMode:界面跟蹤mode。用于scorllview追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受影響
UIInitializationRunLoop:剛啟動(dòng)時(shí)app進(jìn)入第一個(gè)mode,啟動(dòng)完成就不再使用
GSRunLoopReceiveRunLoopMode:接受系統(tǒng)時(shí)間的內(nèi)部mode,繪圖服務(wù),通常用不到
NSRunLoopCommonModes:這是一個(gè)占位用的mode,不是一種真的mode
ARC
OC為了處理對象,將變量類型定義為id類型和各種對象類型
對象類型就是指向NSObject這樣的OC指針(NSObject)
id類型用于隱藏對象類型的類名。相當(dāng)于C語言中的void
所有權(quán)修飾符
所有權(quán)修飾符一種有4種
__strong
__weak
__unsafe_unretained
__autoreleasing
__strong
__strong是id類型和對象類型默認(rèn)的所有權(quán)修飾符
id object = [NSObject new]等價(jià)于 id __strong object = [NSObject new];
強(qiáng)引用的變量,在超過變量作用域時(shí),會(huì)自動(dòng)釋放。
{
id object = [NSObject new]
}
超過作用于自動(dòng)釋放
__weak
oc使用引用記數(shù),因此會(huì)出現(xiàn)循環(huán)引用的問題。__weak就是為了解決循環(huán)引用的問題。
下面的例子會(huì)出現(xiàn)循環(huán)引用
id test0 = [Test new];
id test1 = [Test new];
test0.myObj = test1
test1.myObj = test0
持有某對象的若引用時(shí),如果該對象被廢棄,此弱引用自動(dòng)失效,并被置為nil
__unsafe_unretrained
__unsafe_unretrained不會(huì)持有對象(類似弱引用),但是指向?qū)ο蟊会尫艜r(shí),不會(huì)自動(dòng)指向nil,還是會(huì)指向原來的地址,所以會(huì)導(dǎo)致野指針。
一般不使用__unsafe_unretrained
__autoreleasing
使用ARC的時(shí)候,不能直接使用autorelease方法,但是可以使用__autoreleaseing權(quán)限修飾符
ARC無效的時(shí)候的寫法
NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease]
[pool drain];
ARC有效的時(shí)候的寫法
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
編譯器會(huì)自動(dòng)檢查方法名是否以alloc/new/copy/mutableCopy開始,如果不是則自動(dòng)將返回值注冊到autoreleasepool。
ARC下的內(nèi)存管理規(guī)則
不能使用retain/release/retainCount/autorelease
不能使用NSAllocateObject/NSDeallocateObject
必須遵守內(nèi)存管理的方法命名
alloc
new
copy
mutableCopy
以上述名稱開始的方法在返回對象時(shí),必須返回給調(diào)用放應(yīng)當(dāng)持有的對象
ARC追加了一條
init方法必須是實(shí)例方法。返回的對象應(yīng)當(dāng)是id類型、方法聲明的對象類型、或者是該類型的父類或子類
不要顯式調(diào)用dealloc
使用@autoreleasepool代替NSAutoreleasePool
不能使用區(qū)域(NSZone)
對象類型變量不能作為C語言結(jié)構(gòu)體成員
void*和id之間轉(zhuǎn)換,必須強(qiáng)制轉(zhuǎn)換
盡量不要使用這種轉(zhuǎn)換方式,ARC不推薦
ARC下面的三種轉(zhuǎn)換方式
id obj = [[NSObject alloc] init];
void *p = (__bridge void*)obj;
id o = (__bridge id)p
這種轉(zhuǎn)換類似于 __unsade_unretained。對象釋放不會(huì)置為nil,所以很容易出現(xiàn)野指針
id obj = [[NSObject alloc]init];
void *p = (__bridge_retrained void*)obj
對象引用記數(shù)+1,相當(dāng)于retain
相當(dāng)于
id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void*)obj
id obj = (__bridge_transfer id)p
相當(dāng)于
id obj = (id)p
[obj retain];
[(id)p release]
屬性
屬性和所有權(quán)修飾符對應(yīng)
assign __unsade_unretained
copy __strong (賦值的是被復(fù)制的對象)
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak
copy不是簡單的賦值,他的賦值是通過NSCopying接口的copyWithZone方法生成的對象,然后賦值。
id *類型默認(rèn)是autorelease,因此可以顯式的使用_strong
動(dòng)態(tài)數(shù)組(id *)中使用操作符和靜態(tài)數(shù)組(id[])不一樣,需要手動(dòng)釋放內(nèi)存。
因?yàn)樵陟o態(tài)數(shù)組中,編譯器能夠根據(jù)變量的作用域自動(dòng)插入釋放內(nèi)存的代碼。但是編譯器不能確定數(shù)組的生命周期,所以動(dòng)態(tài)數(shù)組需要按照下面的方式釋放
for (NSInteger i = 0; i < num; i++) {
array[i] = nil;
}
free(array)
類的成員變量和屬性
類的成員變量
@interface ViewController : UIViewController
{
//成員變量
NSArray *array1;
}
類的屬性
@property (nonatomic,strong) NSArray *array2;
類的屬性會(huì)自動(dòng)創(chuàng)建一個(gè)帶下劃線的成員變量,并且會(huì)給這個(gè)成員變量創(chuàng)建getter和setter方法。
ARC的實(shí)現(xiàn)
__strong的實(shí)現(xiàn)
id __strong obj = [[NSObject alloc] init];
編譯器會(huì)自動(dòng)插入釋放代碼,如下
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj)
__weak的實(shí)現(xiàn)
{
id __weak obj1 = obj;
}
等價(jià)于
id obj1
objc_initWeak(&obj1, obj);
objc_destoryWeak(&obj1);
等價(jià)于
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_storeWeak(&obj1,0);
oc會(huì)維護(hù)一張weak表,key是對象的地址,value是帶有weak變量的地址。
objc_storeWeak(&obj1, obj)把對象的地址作為key,變量的地址作為value存放到weak表里面。
objc_storeWeak(&obj1,0);把對象對象地址對應(yīng)的value清0
廢棄對象時(shí),會(huì)根據(jù)該對象的地址,找到weak表里面對應(yīng)的變量地址,然后給變量賦值nil。