前言
讀完《iOS與OS X多線程和內(nèi)存管理》有一段時(shí)間了,當(dāng)時(shí)是和《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法 》一起學(xué)習(xí)的,個(gè)人認(rèn)為這兩本書中有很多相輔相成的東西 ,很適合一起學(xué)習(xí),現(xiàn)在有空閑時(shí)間,就想把之前粗略的筆記進(jìn)行一下簡單的整理,主要是書中的內(nèi)容整理和總結(jié),如有理解錯(cuò)誤的地方,希望大家指出,我們一起學(xué)習(xí)和探討~
心得
內(nèi)存管理的內(nèi)容建議先閱讀《iOS與OS X多線程和內(nèi)存管理》中的內(nèi)容,個(gè)人認(rèn)為講解的很詳細(xì)和深入,閱讀完以后建議配合《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法 》中的內(nèi)存管理再次深入,這只是個(gè)人學(xué)習(xí)上的一點(diǎn)建議,希望對(duì)大家有幫助
內(nèi)容
- 自己生成的對(duì)象,自己所持有
- 非自己生成的對(duì)象,自己也能持有
- 不再需要自己持有的對(duì)象時(shí)釋放
- 非自己持有的對(duì)象無法釋放

cocoa框架中Foundation框架類庫中NSObject類來負(fù)責(zé)內(nèi)存管理
一、自己生成的對(duì)象,自己所持有
使用以下名稱開頭的方法(并且方法名遵守駝峰式拼寫法),‘自己生成的對(duì)象只有自己持有’;調(diào)用該方法的調(diào)用者也意味著‘自己生成并持有對(duì)象’
alloc
id obj = [[NSObject alloc]init];
new
id obj = [NSObject new];
copy
mutableCopy
- 方法名命名實(shí)例:
//下列名稱也意味著自己生成并持有對(duì)象
allocMyObject
newThatObject
copyThis
mutablCopyYourObject
//不屬于同一類別的方法
allocated
newer
copying
mutableCopyed
二、非自己生成的對(duì)象,自己也能持有
使用除alloc、new、copy、mutableCopy以外的方法取得的對(duì)象,因?yàn)榉亲约荷刹⒊钟?,所以自己不是該?duì)象的持有者,但是可以通過retain方法取得對(duì)象的持有權(quán)
id obj = [NSMutableArray array];//取得的對(duì)象存在,但自己不持有對(duì)象
[obj retain];//取得對(duì)象的持有權(quán)
三、不再需要自己持有的對(duì)象時(shí)釋放
注:自己持有的對(duì)象,一旦不再需要,持有者有義務(wù)釋放該對(duì)象,釋放使用release方法
取得對(duì)象的持有權(quán)方式(包含釋放):
/*
* 通過alloc/new/copy/mutableCopy等方法‘自己生成并持有對(duì)象’
*/
id obj = [[NSObject alloc] init];//自己持有對(duì)象
[obj release]; //釋放對(duì)象
/*
* 通過alloc/new/copy/mutableCopy等開頭的自定義方法‘自己生成并持有對(duì)象’
*/
-(id)allocObject{
id obj = [[NSObject alloc] init];//自己持有對(duì)象
return obj;
}
//取得非自己生成并持有對(duì)象
id obj1 = [obj0 allocObject];//因?yàn)閍llocObject符合命名規(guī)則,所以意味著‘自己生成并持有對(duì)象’
[obj release]; //釋放對(duì)象
/*
* 通過alloc/new/copy/mutableCopy等方法以外的方法‘取得非自己生成并持有對(duì)象’
*/
id obj = [NSMutableArray array];//取得的對(duì)象存在,但自己不持有對(duì)象
[obj retain];//通過retain方法,自己持有對(duì)象
[obj release]; //釋放對(duì)象
/*
* 通過alloc/new/copy/mutableCopy等開頭以外的自定義方法‘取得非自己生成并持有對(duì)象’
*/
-(id)object{
id obj = [[NSObject alloc] init];//自己持有對(duì)象
[obj autorelease];//取得的對(duì)象存在,但自己不持有對(duì)象,使用NSMutableArray類的array類方法等可以取得誰都不持有的對(duì)象,同時(shí)通過‘a(chǎn)utorelease實(shí)現(xiàn)的’
return obj;
}
id obj1 = [obj0 object];//因?yàn)閛bject不符合命名規(guī)則,所以意味著‘取得的對(duì)象存在,但自己不持有對(duì)象’
[obj1 retain];//也能通過retain方法將調(diào)用autorelease方法取得的對(duì)象變?yōu)樽约撼钟校?
[obj release]; //釋放對(duì)象
四、非自己持有的對(duì)象無法釋放
可以并需要釋放對(duì)象的情況
- 用alloc/new/copy/mutableCopy方法生成并持有的對(duì)象
- 用retain方法持有的對(duì)象
不可以釋放對(duì)象的情況
- 自己生成并持有對(duì)象后,在釋放完不再需要的對(duì)象后再次釋放
id obj = [[NSObject alloc] init];
[obj release];
[obj release];
- 取得的對(duì)象存在,但自己不持有的情況下釋放對(duì)象
id obj1 = [obj0 object]
[obj1 release];
五、allco/new/copy/mutableCopy實(shí)現(xiàn)
alloc
id obj =[NSObject alloc]
/*
* alloc方法實(shí)現(xiàn)
* 通過allocWithZone:類方法調(diào)用NSAllocateObject函數(shù)分配了對(duì)象
* NSAllocateObject函數(shù)通過調(diào)用NSZoneMalloc函數(shù)來分配存放對(duì)象所需的內(nèi)存空間,之后將內(nèi)存空間置0,最后返回作為對(duì)象而使用的指針
* 執(zhí)行alloc后對(duì)象的retainCount=1(不考慮其他因素,只執(zhí)行alloc后通過[obj retainCount]方法獲取的值)
*/
+(id)alloc{
return [self allocWithZone : NSDfaultMallocZone()];
}
+(id) allocWithZone:(NSZone *)z{
return NSAllocateObject(self , 0 ,z);
}
疑惑點(diǎn)
1、 NSAllocateObject函數(shù)通過調(diào)用NSZoneMalloc函數(shù)來分配存放對(duì)象所需的內(nèi)存空間,之后將內(nèi)存空間置0,最后返回作為對(duì)象而使用的指針
- 為什么要內(nèi)存空間置0 ?
- 答:分配存放對(duì)象A所需的內(nèi)存空間,可能之前屬于對(duì)象B并且存放很多B的東西,現(xiàn)在分配給A后,要將內(nèi)存空間置0,防止野指針產(chǎn)生。
- 知識(shí)點(diǎn)擴(kuò)充:
- 產(chǎn)生野指針的成因:
- 1、指針變量未初始化
- 2、指針釋放后之后未置空
- 3、指針操作超越變量作用域
- 產(chǎn)生野指針的成因:
總結(jié)
- a、 在objective-C的對(duì)象中存有引用計(jì)數(shù)這一整數(shù)值
- b、調(diào)用alloc或是retain方法后,引用計(jì)數(shù)值加1
- c、調(diào)用release后,引用計(jì)數(shù)值減1
- d、引用計(jì)數(shù)值為0時(shí),調(diào)用dealloc方法廢棄對(duì)象
-
e、蘋果采用散列表(引用計(jì)數(shù)表)來管理引用計(jì)數(shù)
D253DA4A-D7C8-4589-8323-3AD3AEDAC06C.png - f、引用計(jì)數(shù)表各記錄中存有內(nèi)存塊地址,可以從各個(gè)記錄追溯到各對(duì)象的內(nèi)存塊
-
f-a、有助于調(diào)試,即使出現(xiàn)故障導(dǎo)致對(duì)象占有的內(nèi)存塊損壞,只要引用計(jì)數(shù)表沒有被破壞,就可以確認(rèn)各內(nèi)存塊的位置,另外在利用工具檢測內(nèi)存泄露時(shí),引用計(jì)數(shù)表的各記錄有助于檢測各對(duì)象的持有者是否存在
D09BA31A-4A1D-4E3F-896B-3A71A964D9E2.png
-
六、autorelease
autorelease會(huì)像C語言的自動(dòng)變量那樣對(duì)待對(duì)象實(shí)例,當(dāng)超出其作用域(NSAutoreleasePool對(duì)象的生存周期)時(shí),對(duì)象實(shí)例的release實(shí)例方法會(huì)被調(diào)用,另外編程人員可設(shè)定變量的作用域
-
autorelease的具體使用方法:
- (1)生成并持有NSAutoreleasePool對(duì)象
- (2)調(diào)用已有分配對(duì)象的autorelesae實(shí)例方法
- (3)廢棄NSAutoreleasePool對(duì)象

- 所有調(diào)用autorelease實(shí)例方法的對(duì)象,在廢棄NSAutoreleasePool對(duì)象時(shí),都將調(diào)用release實(shí)例方法(只要不廢棄NSAutoreleasePool對(duì)象,那么生成的對(duì)象就不能被釋放)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSObject alloc]init];
[obj release];
[pool drain];//廢棄NSAutoreleasePool對(duì)象,相當(dāng)于 [obj release];
-
程序主循環(huán)的NSRunLoop或者在其他程序可運(yùn)行的地方,對(duì)NSAutoreleasePool對(duì)象進(jìn)行生成、持有、廢棄處理
27529D24-9612-4FB8-8D6C-7C94D84CADCB.png autorelease如何實(shí)現(xiàn)
/*
* autorelease實(shí)例方法的本質(zhì)就是調(diào)用NSAutoreleasePool對(duì)象的addObject類方法
* addObject類方法調(diào)用正在使用的NSAutoreleasePool對(duì)象的addObject實(shí)例方法(如果嵌套生成或持有NSAutoreleasePool對(duì)象,使用最內(nèi)側(cè)的對(duì)象)
* 調(diào)用NSObject類的autorelease實(shí)例方法,該對(duì)象將被追加到正在使用的NSAutoreleasePool對(duì)象中的數(shù)組中
*/
[obj autorelease];
-(id)autorelease{
[NSAutorelesaePool addObject:self];
}
// addObject類方法:
+(id)addObject:(id) anObj{
NSAutorelesaePool *pool = 取得正在使用的NSAutorelesaePool對(duì)象;
if(pool != nil){
[pool addObject:anObj];
}else{
//NSAutorelesaePool對(duì)象非存在狀態(tài)下調(diào)用autorelease;
}
}
//NSAutoreleasePool對(duì)象的addObject實(shí)例方法
-(id)addObject:(id)anObj{
[array addObject: anObj] ;
}
//通過drain實(shí)例方法廢棄正在使用的NSAutoreleasePool對(duì)象過程(數(shù)組中所有的對(duì)象都調(diào)用了release實(shí)例方法)
-(void)drain{
[self dealloc];
}
-(void)dealloc{
[self emptyPool];
[array release];
}
-(void) emptyPool{
for(id obj in array){
[obj release];
}
}
總結(jié)
- 在Cocoa框架中有很多類方法用于返回autorelease的對(duì)象(例如:NSMutableArray類中的arrayWithCapacity類方法)
id array = [NAMutableArray arrayWithCapacity:1];
//等同與以下代碼
id array =[[[NAMutableArray alloc] initWithCapacity:1] autorelease] ;
- 可通過NSAutoreleasePool類中的非公開類方法showPools來確認(rèn)已被autorelease的對(duì)象的狀況(該函數(shù)在檢查某對(duì)象是否被自動(dòng)release時(shí)非常有用)
[NSAutorelease showPools];
- 對(duì)象的autorelease實(shí)例方法,實(shí)現(xiàn)上是調(diào)用的都是NSObject類的autorelease實(shí)例方法
七、ARC規(guī)則
- 設(shè)置ARC是否有效
- -fno-objc-arc 無效
- -fobjc-arc 有效(默認(rèn))
所有權(quán)修飾符
-
OC編程中為了處理對(duì)象,可將變量類型定義為id類型或各種對(duì)象類型
- 對(duì)象類型:就是指向NSObject這樣的OC類的指針,例如NSObject *
- id類型:用于隱藏對(duì)象類型的類名部分
- ARC有效時(shí),id類型和對(duì)象類型上必須附加所有權(quán)修飾符
-
所有權(quán)修飾符
- _strong修飾符
- _weak修飾符
- _unsafe_unretained修飾符
- _autoreleaseing修飾符
_strong修飾符
- _strong修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符
id obj = [[NSObject alloc] init];
//等價(jià)于
id _strong obj = [[NSObject alloc] init];
- 源代碼ARC無效時(shí)情況
/*
* 情況一
*/
id obj = [[NSObject alloc] init];
//等價(jià)于
id obj = [[NSObject alloc] init];//ARC無效時(shí)
/*
* 情況二
*/
id _strong obj = [[NSObject alloc] init];
//等價(jià)于
id obj = [[NSObject alloc] init];
[obj release];//為了釋放生成并持有的對(duì)象,增加調(diào)用了release方法
- _strong修飾符表示對(duì)對(duì)象的‘強(qiáng)引用’,附有_strong修飾符的變量obj在超出其變量的作用域時(shí),即在該被變量被廢棄時(shí),隨著強(qiáng)引用的失效,引用的對(duì)象會(huì)隨之釋放
- _strong修飾符的變量,不僅只在變量的作用域中,在賦值上也可以管理其對(duì)象的所有者
//obj0持有對(duì)象A的強(qiáng)引用
id obj0 = [[NSObject alloc] init];/*對(duì)象A*/
//obj1持有對(duì)象B的強(qiáng)引用
id obj1 = [[NSObject alloc] init];/*對(duì)象B*/
/*
* obj0持有由obj1賦值的對(duì)象B的強(qiáng)引用
* 因?yàn)閛bj0被賦值,所以原先持有的對(duì)對(duì)象A的強(qiáng)引用失效
* 對(duì)象A的持有者不存在,因此廢棄對(duì)象A
* 此時(shí)持有對(duì)象B的強(qiáng)引用的變量為obj0 和obj1
*/
obj0 = obj1;
- _strong _weak _autorelease修飾符,可以保證將附有這些修飾符的自動(dòng)變量初始化為nil
- 通過_strong修飾符,不必再次鍵入retain和release
- 只要通過對(duì)帶有_strong修飾符的變量賦值就可達(dá)成‘自己生成的對(duì)象,自己持有’和‘非自己生成的對(duì)象,自己也能持有’
- 通過廢棄帶_strong修飾符的變量(變量作用域結(jié)束、成員變量所屬對(duì)象廢棄)或者對(duì)變量賦值,都可達(dá)到‘不在需要自己持有的對(duì)象時(shí)釋放’
_weak修飾符
- 循環(huán)引用:就是應(yīng)當(dāng)廢棄的對(duì)象在超出其生命周期后繼續(xù)存在,循環(huán)引用容易發(fā)生內(nèi)存泄露
{
//test0持有Test對(duì)象A的強(qiáng)引用
id test0 =[[Test alloc] init];/*對(duì)象A*/
//test1持有Test對(duì)象B的強(qiáng)引用
id test1 =[[Test alloc] init];/*對(duì)象A*/
/*
* Test對(duì)象A的obj_成員變量持有Test對(duì)象B的強(qiáng)引用
* 此時(shí)持有Test對(duì)象B的強(qiáng)引用變量為test1、Test對(duì)象A的obj_
*/
[test0 setObject: test1];
/*
* Test對(duì)象B的obj_成員變量持有Test對(duì)象A的強(qiáng)引用
* 此時(shí)持有Test對(duì)象A的強(qiáng)引用變量為test0、Test對(duì)象B的obj_
*/
[test1 setObject: test0];
}
/*
* 因?yàn)閠est0變量超出其作用域,強(qiáng)引用失效,自動(dòng)釋放Test對(duì)象A
* 因?yàn)閠est1變量超出其作用域,強(qiáng)引用失效,自動(dòng)釋放Test對(duì)象B
* 此時(shí)持有Test對(duì)象B的強(qiáng)引用變量為Test對(duì)象A的obj_
* 此時(shí)持有Test對(duì)象A的強(qiáng)引用變量為Test對(duì)象B的obj_
* 內(nèi)存泄露
*/
- _weak修飾符于_strong修飾符相反,提供弱引用,弱引用不能持有對(duì)象實(shí)例
- 將自己生成并持有的對(duì)象賦值為附有_weak修飾符的變量,因?yàn)槿跻貌荒艹钟袑?duì)象,在超出其變量的作用域時(shí),對(duì)象就沒有了持有者,所以廢棄該對(duì)象
- _weak修飾符,在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài)
- _weak修飾符可避免循環(huán)引用,通過檢查 _weak修飾符的變量是否為nil,可以判斷被賦值的對(duì)象是否已廢棄
_unsafe_unretained修飾符
- 盡管ARC式的內(nèi)存管理時(shí)編譯器的工作,但附有_unsafe_unretained修飾符的變量不屬于編譯器內(nèi)存管理對(duì)象
_autorelease修飾符
- ARC有效時(shí)不能使用autorelease方法,也不能使用NSAutoreleasePool類
- ARC有效時(shí),通過將對(duì)象賦值給附有_autorelease修飾符的變量來代替調(diào)用autorelease方法,將對(duì)象注冊到autoreleasepool
- 非顯示使用_autorelease修飾符
- 編譯器檢查方法名是否已a(bǔ)lloc/new/copy/mutableCopy開始,如果不是則自動(dòng)將返回值的對(duì)象注冊到autoreleasepool(init方法返回值的對(duì)象不注冊到autoreleasepool)
- 在使用_weak修飾符的變量時(shí)就必然要使用注冊到autoreleasepool中的對(duì)象
- id的指針或?qū)ο蟮闹羔樤跊]有顯示指定時(shí)會(huì)被附加_autorelease修飾符(id * 類型 默認(rèn)為‘id _autorelease ’* 類型)
- 賦值給對(duì)象指針時(shí),所有權(quán)修飾符必須一致
- NSRunLOop等實(shí)現(xiàn)無論ARC有效還是無效,均能隨時(shí)釋放注冊到main函數(shù)中autoreleasepool中的對(duì)象
ARC規(guī)則
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必須遵守內(nèi)存管理的命名規(guī)則
- 不能顯示調(diào)用dealloc
- 使用@autorelease塊代替NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對(duì)象型變量不能作為C語言結(jié)構(gòu)體(struct/union)的成員
- 顯示轉(zhuǎn)化‘id’和‘void’
1、不能使用retain/release/retainCount/autorelease
- 設(shè)置ARC有效時(shí),禁止再次鍵入retain/release代碼
- retainCount獲取的值不準(zhǔn)確 ,不建議使用
2、不能使用NSAllocateObject/NSDeallocateObject
- 會(huì)引起編譯錯(cuò)誤(同retain/release)
3、必須遵守內(nèi)存管理的命名規(guī)則
- ARC有效時(shí)追加一天命名規(guī)則init(駝峰式)
- 已init開頭的方法,該方法必須時(shí)‘實(shí)例方法’
- 并且必須要返回對(duì)象
- 返回的對(duì)象應(yīng)為id類型或該方法聲明的對(duì)象類型,抑或是該類的超類型或子類型
- 返回對(duì)象并不注冊到autoreleasepool上
- 基本上只是對(duì)alloc方法返回的對(duì)象進(jìn)行初始化處理并返回該對(duì)象
4、不能顯示調(diào)用dealloc
- 無論ARC是否有效,只要對(duì)象的所有者都不持有該歇對(duì)象,該對(duì)象就被廢棄,對(duì)象廢棄,不管ARC是否有效,都會(huì)調(diào)用dealloc方法
- ARC有效不能顯示調(diào)用dealloc
//ARC無效時(shí)
-(void)dealloc{
[super dealloc];
}
//ARC有效時(shí)(ARC會(huì)自動(dòng)處理,ARC有效不能顯示調(diào)用dealloc所以不必書寫 [super dealloc])
-(void)dealloc{
}
- dealloc方法在大多數(shù)情況下還適用于刪除已注冊的代理或者觀察者對(duì)象
-(void)dealloc{
[ [NSNotificationCenter defaultCenter] removeObserver:self];
}
八、屬性
- 當(dāng)ARC有效時(shí),OC類的屬性也發(fā)生變化
@property (nonatomic , strong) NSString * name;

- copy屬性不是簡單的賦值,它賦值的是通過NSCopying接口的copyWithZone:方法復(fù)制的復(fù)制源所生成的對(duì)象
- 在聲明類成員變量時(shí),如果同屬性聲明中的屬性不一致則會(huì)引起編譯錯(cuò)誤
/*
*錯(cuò)誤舉例
*/
/聲明id型obj成員變量
id obj;
//定義其屬性聲明為weak
@property (nonatomic , weak) id obj;
/*
*正確舉例1
*/
/聲明id型obj成員變量
id _weak obj;
//定義其屬性聲明為weak
@property (nonatomic , weak) id obj;
/*
*正確舉例2
*/
//聲明id型obj成員變量
id obj;
//定義其屬性聲明為strong
@property (nonatomic , strong) id obj;
九、數(shù)組
- 除_unsafe_unretained修飾符以外, _strong修飾符、 _weak修飾符、 _autorelease修飾符保證其指定的變量,數(shù)組初始化為nil
十、ARC實(shí)現(xiàn)
_strong修飾符
- 自己生成并持有對(duì)象
- 通過兩次調(diào)用objc_msgSend方法(alloc方法、init方法),變量作用域結(jié)束時(shí)通過調(diào)用objc_release釋放對(duì)象
- 非自己生成,自己持有對(duì)象
-
objc_retainAutoreleasedReturnValue函數(shù):自己持有對(duì)象函數(shù)(持有的對(duì)象應(yīng)為返回注冊在autoreleasepool中的對(duì)象方法或函數(shù)返回值)
CFA44A73-0A3C-4428-A455-2E0419676574.png
-
_weak修飾符
附有_weak修飾符的變量,通過objc_initWeak函數(shù)初始化,將對(duì)象賦值給該變量,會(huì)將對(duì)象的作為參數(shù)調(diào)用objc_storeWeak函數(shù),objc_storeWeak函數(shù)將對(duì)象的地址作為鍵值,對(duì)應(yīng)變量的地址注冊到weak表中,并且一個(gè)對(duì)象可同時(shí)賦值給多個(gè)_weak修飾的變量,所以一個(gè)鍵值,可以注冊多個(gè)變量地址,對(duì)象釋放時(shí)時(shí)最后調(diào)用的objc_clear_deallocting函數(shù)會(huì)從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄,將包含在記錄中的所有附有_weak修飾符變量的地址,賦值為nil,從weak表中刪除該記錄,從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄 。
-
若附有_weakk修飾符的變量所引用的對(duì)象被廢棄,則將nil賦值給該變量
- 釋放對(duì)象時(shí),廢棄誰都不持有的對(duì)象的同時(shí),有如下操作(詳情見《iOS與os x多線程和內(nèi)存管理》1.4.2 _weak修飾符 p68):
- 1、objc_release
- 2、因?yàn)橐糜?jì)數(shù)為0,所以執(zhí)行dealloc
- 3、_objc_rootDealloc(不知道)
- 4、objc_dispose(不知道)
- 5、objc_destructInstance(不知道)
- 6、objc_clear_deallocting(了解)
- 對(duì)象被廢棄時(shí)最后調(diào)用的objc_clear_deallocting函數(shù)操作如下:
- 1、從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄
- 2、將包含在記錄中的所有附有_weak修飾符變量的地址,賦值為nil
- 3、從weak表中刪除該記錄
- 4、從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄
- 釋放對(duì)象時(shí),廢棄誰都不持有的對(duì)象的同時(shí),有如下操作(詳情見《iOS與os x多線程和內(nèi)存管理》1.4.2 _weak修飾符 p68):
使用附有_weak修飾符的變量,即是使用注冊到autoreleasepool中的對(duì)象



