前言
讀完《iOS與OS X多線程和內(nèi)存管理》有一段時間了姊舵,當時是和《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個有效方法 》一起學習的喊积,個人認為這兩本書中有很多相輔相成的東西 ,很適合一起學習长已,現(xiàn)在有空閑時間技俐,就想把之前粗略的筆記進行一下簡單的整理旋恼,主要是書中的內(nèi)容整理和總結(jié)吏口,如有理解錯誤的地方,希望大家指出,我們一起學習和探討~
心得
內(nèi)存管理的內(nèi)容建議先閱讀《iOS與OS X多線程和內(nèi)存管理》中的內(nèi)容产徊,個人認為講解的很詳細和深入昂勒,閱讀完以后建議配合《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個有效方法 》中的內(nèi)存管理再次深入,這只是個人學習上的一點建議舟铜,希望對大家有幫助
內(nèi)容
- 自己生成的對象戈盈,自己所持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放
cocoa框架中Foundation框架類庫中NSObject類來負責內(nèi)存管理
一谆刨、自己生成的對象塘娶,自己所持有
使用以下名稱開頭的方法(并且方法名遵守駝峰式拼寫法),‘自己生成的對象只有自己持有’痊夭;調(diào)用該方法的調(diào)用者也意味著‘自己生成并持有對象’
alloc
id obj = [[NSObject alloc]init];
new
id obj = [NSObject new];
copy
mutableCopy
- 方法名命名實例:
//下列名稱也意味著自己生成并持有對象
allocMyObject
newThatObject
copyThis
mutablCopyYourObject
//不屬于同一類別的方法
allocated
newer
copying
mutableCopyed
二刁岸、非自己生成的對象,自己也能持有
使用除alloc她我、new虹曙、copy、mutableCopy以外的方法取得的對象鸦难,因為非自己生成并持有,所以自己不是該對象的持有者员淫,但是可以通過retain方法取得對象的持有權(quán)
id obj = [NSMutableArray array];//取得的對象存在合蔽,但自己不持有對象
[obj retain];//取得對象的持有權(quán)
三、不再需要自己持有的對象時釋放
注:自己持有的對象介返,一旦不再需要拴事,持有者有義務(wù)釋放該對象,釋放使用release方法
取得對象的持有權(quán)方式(包含釋放):
/*
* 通過alloc/new/copy/mutableCopy等方法‘自己生成并持有對象’
*/
id obj = [[NSObject alloc] init];//自己持有對象
[obj release]; //釋放對象
/*
* 通過alloc/new/copy/mutableCopy等開頭的自定義方法‘自己生成并持有對象’
*/
-(id)allocObject{
id obj = [[NSObject alloc] init];//自己持有對象
return obj;
}
//取得非自己生成并持有對象
id obj1 = [obj0 allocObject];//因為allocObject符合命名規(guī)則圣蝎,所以意味著‘自己生成并持有對象’
[obj release]; //釋放對象
/*
* 通過alloc/new/copy/mutableCopy等方法以外的方法‘取得非自己生成并持有對象’
*/
id obj = [NSMutableArray array];//取得的對象存在刃宵,但自己不持有對象
[obj retain];//通過retain方法,自己持有對象
[obj release]; //釋放對象
/*
* 通過alloc/new/copy/mutableCopy等開頭以外的自定義方法‘取得非自己生成并持有對象’
*/
-(id)object{
id obj = [[NSObject alloc] init];//自己持有對象
[obj autorelease];//取得的對象存在徘公,但自己不持有對象牲证,使用NSMutableArray類的array類方法等可以取得誰都不持有的對象,同時通過‘a(chǎn)utorelease實現(xiàn)的’
return obj;
}
id obj1 = [obj0 object];//因為object不符合命名規(guī)則关面,所以意味著‘取得的對象存在坦袍,但自己不持有對象’
[obj1 retain];//也能通過retain方法將調(diào)用autorelease方法取得的對象變?yōu)樽约撼钟校?
[obj release]; //釋放對象
四、非自己持有的對象無法釋放
可以并需要釋放對象的情況
- 用alloc/new/copy/mutableCopy方法生成并持有的對象
- 用retain方法持有的對象
不可以釋放對象的情況
- 自己生成并持有對象后等太,在釋放完不再需要的對象后再次釋放
id obj = [[NSObject alloc] init];
[obj release];
[obj release];
- 取得的對象存在捂齐,但自己不持有的情況下釋放對象
id obj1 = [obj0 object]
[obj1 release];
五、allco/new/copy/mutableCopy實現(xiàn)
alloc
id obj =[NSObject alloc]
/*
* alloc方法實現(xiàn)
* 通過allocWithZone:類方法調(diào)用NSAllocateObject函數(shù)分配了對象
* NSAllocateObject函數(shù)通過調(diào)用NSZoneMalloc函數(shù)來分配存放對象所需的內(nèi)存空間缩抡,之后將內(nèi)存空間置0奠宜,最后返回作為對象而使用的指針
* 執(zhí)行alloc后對象的retainCount=1(不考慮其他因素,只執(zhí)行alloc后通過[obj retainCount]方法獲取的值)
*/
+(id)alloc{
return [self allocWithZone : NSDfaultMallocZone()];
}
+(id) allocWithZone:(NSZone *)z{
return NSAllocateObject(self , 0 ,z);
}
疑惑點
1、 NSAllocateObject函數(shù)通過調(diào)用NSZoneMalloc函數(shù)來分配存放對象所需的內(nèi)存空間压真,之后將內(nèi)存空間置0娩嚼,最后返回作為對象而使用的指針
- 為什么要內(nèi)存空間置0 ?
- 答:分配存放對象A所需的內(nèi)存空間榴都,可能之前屬于對象B并且存放很多B的東西待锈,現(xiàn)在分配給A后,要將內(nèi)存空間置0嘴高,防止野指針產(chǎn)生竿音。
- 知識點擴充:
- 產(chǎn)生野指針的成因:
- 1、指針變量未初始化
- 2拴驮、指針釋放后之后未置空
- 3春瞬、指針操作超越變量作用域
- 產(chǎn)生野指針的成因:
總結(jié)
- a、 在objective-C的對象中存有引用計數(shù)這一整數(shù)值
- b套啤、調(diào)用alloc或是retain方法后宽气,引用計數(shù)值加1
- c、調(diào)用release后潜沦,引用計數(shù)值減1
- d萄涯、引用計數(shù)值為0時,調(diào)用dealloc方法廢棄對象
-
e唆鸡、蘋果采用散列表(引用計數(shù)表)來管理引用計數(shù)
- f涝影、引用計數(shù)表各記錄中存有內(nèi)存塊地址,可以從各個記錄追溯到各對象的內(nèi)存塊
-
f-a争占、有助于調(diào)試燃逻,即使出現(xiàn)故障導(dǎo)致對象占有的內(nèi)存塊損壞,只要引用計數(shù)表沒有被破壞臂痕,就可以確認各內(nèi)存塊的位置伯襟,另外在利用工具檢測內(nèi)存泄露時,引用計數(shù)表的各記錄有助于檢測各對象的持有者是否存在
-
六握童、autorelease
autorelease會像C語言的自動變量那樣對待對象實例姆怪,當超出其作用域(NSAutoreleasePool對象的生存周期)時,對象實例的release實例方法會被調(diào)用澡绩,另外編程人員可設(shè)定變量的作用域
-
autorelease的具體使用方法:
- (1)生成并持有NSAutoreleasePool對象
- (2)調(diào)用已有分配對象的autorelesae實例方法
- (3)廢棄NSAutoreleasePool對象
- 所有調(diào)用autorelease實例方法的對象片效,在廢棄NSAutoreleasePool對象時,都將調(diào)用release實例方法(只要不廢棄NSAutoreleasePool對象英古,那么生成的對象就不能被釋放)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSObject alloc]init];
[obj release];
[pool drain];//廢棄NSAutoreleasePool對象淀衣,相當于 [obj release];
-
程序主循環(huán)的NSRunLoop或者在其他程序可運行的地方,對NSAutoreleasePool對象進行生成召调、持有膨桥、廢棄處理
autorelease如何實現(xiàn)
/*
* autorelease實例方法的本質(zhì)就是調(diào)用NSAutoreleasePool對象的addObject類方法
* addObject類方法調(diào)用正在使用的NSAutoreleasePool對象的addObject實例方法(如果嵌套生成或持有NSAutoreleasePool對象蛮浑,使用最內(nèi)側(cè)的對象)
* 調(diào)用NSObject類的autorelease實例方法,該對象將被追加到正在使用的NSAutoreleasePool對象中的數(shù)組中
*/
[obj autorelease];
-(id)autorelease{
[NSAutorelesaePool addObject:self];
}
// addObject類方法:
+(id)addObject:(id) anObj{
NSAutorelesaePool *pool = 取得正在使用的NSAutorelesaePool對象;
if(pool != nil){
[pool addObject:anObj];
}else{
//NSAutorelesaePool對象非存在狀態(tài)下調(diào)用autorelease只嚣;
}
}
//NSAutoreleasePool對象的addObject實例方法
-(id)addObject:(id)anObj{
[array addObject: anObj] ;
}
//通過drain實例方法廢棄正在使用的NSAutoreleasePool對象過程(數(shù)組中所有的對象都調(diào)用了release實例方法)
-(void)drain{
[self dealloc];
}
-(void)dealloc{
[self emptyPool];
[array release];
}
-(void) emptyPool{
for(id obj in array){
[obj release];
}
}
總結(jié)
- 在Cocoa框架中有很多類方法用于返回autorelease的對象(例如:NSMutableArray類中的arrayWithCapacity類方法)
id array = [NAMutableArray arrayWithCapacity:1];
//等同與以下代碼
id array =[[[NAMutableArray alloc] initWithCapacity:1] autorelease] ;
- 可通過NSAutoreleasePool類中的非公開類方法showPools來確認已被autorelease的對象的狀況(該函數(shù)在檢查某對象是否被自動release時非常有用)
[NSAutorelease showPools];
- 對象的autorelease實例方法沮稚,實現(xiàn)上是調(diào)用的都是NSObject類的autorelease實例方法
七、ARC規(guī)則
- 設(shè)置ARC是否有效
- -fno-objc-arc 無效
- -fobjc-arc 有效(默認)
所有權(quán)修飾符
-
OC編程中為了處理對象册舞,可將變量類型定義為id類型或各種對象類型
- 對象類型:就是指向NSObject這樣的OC類的指針蕴掏,例如NSObject *
- id類型:用于隱藏對象類型的類名部分
- ARC有效時,id類型和對象類型上必須附加所有權(quán)修飾符
-
所有權(quán)修飾符
- _strong修飾符
- _weak修飾符
- _unsafe_unretained修飾符
- _autoreleaseing修飾符
_strong修飾符
- _strong修飾符是id類型和對象類型默認的所有權(quán)修飾符
id obj = [[NSObject alloc] init];
//等價于
id _strong obj = [[NSObject alloc] init];
- 源代碼ARC無效時情況
/*
* 情況一
*/
id obj = [[NSObject alloc] init];
//等價于
id obj = [[NSObject alloc] init];//ARC無效時
/*
* 情況二
*/
id _strong obj = [[NSObject alloc] init];
//等價于
id obj = [[NSObject alloc] init];
[obj release];//為了釋放生成并持有的對象调鲸,增加調(diào)用了release方法
- _strong修飾符表示對對象的‘強引用’盛杰,附有_strong修飾符的變量obj在超出其變量的作用域時,即在該被變量被廢棄時藐石,隨著強引用的失效即供,引用的對象會隨之釋放
- _strong修飾符的變量,不僅只在變量的作用域中于微,在賦值上也可以管理其對象的所有者
//obj0持有對象A的強引用
id obj0 = [[NSObject alloc] init];/*對象A*/
//obj1持有對象B的強引用
id obj1 = [[NSObject alloc] init];/*對象B*/
/*
* obj0持有由obj1賦值的對象B的強引用
* 因為obj0被賦值逗嫡,所以原先持有的對對象A的強引用失效
* 對象A的持有者不存在,因此廢棄對象A
* 此時持有對象B的強引用的變量為obj0 和obj1
*/
obj0 = obj1株依;
- _strong _weak _autorelease修飾符驱证,可以保證將附有這些修飾符的自動變量初始化為nil
- 通過_strong修飾符,不必再次鍵入retain和release
- 只要通過對帶有_strong修飾符的變量賦值就可達成‘自己生成的對象恋腕,自己持有’和‘非自己生成的對象抹锄,自己也能持有’
- 通過廢棄帶_strong修飾符的變量(變量作用域結(jié)束、成員變量所屬對象廢棄)或者對變量賦值吗坚,都可達到‘不在需要自己持有的對象時釋放’
_weak修飾符
- 循環(huán)引用:就是應(yīng)當廢棄的對象在超出其生命周期后繼續(xù)存在祈远,循環(huán)引用容易發(fā)生內(nèi)存泄露
{
//test0持有Test對象A的強引用
id test0 =[[Test alloc] init];/*對象A*/
//test1持有Test對象B的強引用
id test1 =[[Test alloc] init];/*對象A*/
/*
* Test對象A的obj_成員變量持有Test對象B的強引用
* 此時持有Test對象B的強引用變量為test1呆万、Test對象A的obj_
*/
[test0 setObject: test1];
/*
* Test對象B的obj_成員變量持有Test對象A的強引用
* 此時持有Test對象A的強引用變量為test0商源、Test對象B的obj_
*/
[test1 setObject: test0];
}
/*
* 因為test0變量超出其作用域,強引用失效谋减,自動釋放Test對象A
* 因為test1變量超出其作用域牡彻,強引用失效,自動釋放Test對象B
* 此時持有Test對象B的強引用變量為Test對象A的obj_
* 此時持有Test對象A的強引用變量為Test對象B的obj_
* 內(nèi)存泄露
*/
- _weak修飾符于_strong修飾符相反出爹,提供弱引用庄吼,弱引用不能持有對象實例
- 將自己生成并持有的對象賦值為附有_weak修飾符的變量,因為弱引用不能持有對象严就,在超出其變量的作用域時总寻,對象就沒有了持有者,所以廢棄該對象
- _weak修飾符梢为,在持有某對象的弱引用時渐行,若該對象被廢棄轰坊,則此弱引用將自動失效且處于nil被賦值的狀態(tài)
- _weak修飾符可避免循環(huán)引用,通過檢查 _weak修飾符的變量是否為nil祟印,可以判斷被賦值的對象是否已廢棄
_unsafe_unretained修飾符
- 盡管ARC式的內(nèi)存管理時編譯器的工作肴沫,但附有_unsafe_unretained修飾符的變量不屬于編譯器內(nèi)存管理對象
_autorelease修飾符
- ARC有效時不能使用autorelease方法,也不能使用NSAutoreleasePool類
- ARC有效時,通過將對象賦值給附有_autorelease修飾符的變量來代替調(diào)用autorelease方法蕴忆,將對象注冊到autoreleasepool
- 非顯示使用_autorelease修飾符
- 編譯器檢查方法名是否已alloc/new/copy/mutableCopy開始颤芬,如果不是則自動將返回值的對象注冊到autoreleasepool(init方法返回值的對象不注冊到autoreleasepool)
- 在使用_weak修飾符的變量時就必然要使用注冊到autoreleasepool中的對象
- id的指針或?qū)ο蟮闹羔樤跊]有顯示指定時會被附加_autorelease修飾符(id * 類型 默認為‘id _autorelease ’* 類型)
- 賦值給對象指針時,所有權(quán)修飾符必須一致
- NSRunLOop等實現(xiàn)無論ARC有效還是無效套鹅,均能隨時釋放注冊到main函數(shù)中autoreleasepool中的對象
ARC規(guī)則
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必須遵守內(nèi)存管理的命名規(guī)則
- 不能顯示調(diào)用dealloc
- 使用@autorelease塊代替NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對象型變量不能作為C語言結(jié)構(gòu)體(struct/union)的成員
- 顯示轉(zhuǎn)化‘id’和‘void’
1站蝠、不能使用retain/release/retainCount/autorelease
- 設(shè)置ARC有效時,禁止再次鍵入retain/release代碼
- retainCount獲取的值不準確 芋哭,不建議使用
2沉衣、不能使用NSAllocateObject/NSDeallocateObject
- 會引起編譯錯誤(同retain/release)
3、必須遵守內(nèi)存管理的命名規(guī)則
- ARC有效時追加一天命名規(guī)則init(駝峰式)
- 已init開頭的方法减牺,該方法必須時‘實例方法’
- 并且必須要返回對象
- 返回的對象應(yīng)為id類型或該方法聲明的對象類型豌习,抑或是該類的超類型或子類型
- 返回對象并不注冊到autoreleasepool上
- 基本上只是對alloc方法返回的對象進行初始化處理并返回該對象
4、不能顯示調(diào)用dealloc
- 無論ARC是否有效拔疚,只要對象的所有者都不持有該歇對象肥隆,該對象就被廢棄,對象廢棄稚失,不管ARC是否有效栋艳,都會調(diào)用dealloc方法
- ARC有效不能顯示調(diào)用dealloc
//ARC無效時
-(void)dealloc{
[super dealloc];
}
//ARC有效時(ARC會自動處理,ARC有效不能顯示調(diào)用dealloc所以不必書寫 [super dealloc])
-(void)dealloc{
}
- dealloc方法在大多數(shù)情況下還適用于刪除已注冊的代理或者觀察者對象
-(void)dealloc{
[ [NSNotificationCenter defaultCenter] removeObserver:self];
}
八句各、屬性
- 當ARC有效時吸占,OC類的屬性也發(fā)生變化
@property (nonatomic , strong) NSString * name;
- copy屬性不是簡單的賦值,它賦值的是通過NSCopying接口的copyWithZone:方法復(fù)制的復(fù)制源所生成的對象
- 在聲明類成員變量時凿宾,如果同屬性聲明中的屬性不一致則會引起編譯錯誤
/*
*錯誤舉例
*/
/聲明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實現(xiàn)
_strong修飾符
- 自己生成并持有對象
- 通過兩次調(diào)用objc_msgSend方法(alloc方法排作、init方法),變量作用域結(jié)束時通過調(diào)用objc_release釋放對象
- 非自己生成亚情,自己持有對象
-
objc_retainAutoreleasedReturnValue函數(shù):自己持有對象函數(shù)(持有的對象應(yīng)為返回注冊在autoreleasepool中的對象方法或函數(shù)返回值)
-
_weak修飾符
附有_weak修飾符的變量妄痪,通過objc_initWeak函數(shù)初始化,將對象賦值給該變量楞件,會將對象的作為參數(shù)調(diào)用objc_storeWeak函數(shù)衫生,objc_storeWeak函數(shù)將對象的地址作為鍵值僧著,對應(yīng)變量的地址注冊到weak表中,并且一個對象可同時賦值給多個_weak修飾的變量障簿,所以一個鍵值盹愚,可以注冊多個變量地址,對象釋放時時最后調(diào)用的objc_clear_deallocting函數(shù)會從weak表中獲取廢棄對象的地址為鍵值的記錄站故,將包含在記錄中的所有附有_weak修飾符變量的地址皆怕,賦值為nil,從weak表中刪除該記錄西篓,從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄 愈腾。
-
若附有_weakk修飾符的變量所引用的對象被廢棄,則將nil賦值給該變量
- 釋放對象時岂津,廢棄誰都不持有的對象的同時虱黄,有如下操作(詳情見《iOS與os x多線程和內(nèi)存管理》1.4.2 _weak修飾符 p68):
- 1、objc_release
- 2吮成、因為引用計數(shù)為0橱乱,所以執(zhí)行dealloc
- 3、_objc_rootDealloc(不知道)
- 4粱甫、objc_dispose(不知道)
- 5泳叠、objc_destructInstance(不知道)
- 6、objc_clear_deallocting(了解)
- 對象被廢棄時最后調(diào)用的objc_clear_deallocting函數(shù)操作如下:
- 1茶宵、從weak表中獲取廢棄對象的地址為鍵值的記錄
- 2危纫、將包含在記錄中的所有附有_weak修飾符變量的地址,賦值為nil
- 3乌庶、從weak表中刪除該記錄
- 4种蝶、從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
- 釋放對象時岂津,廢棄誰都不持有的對象的同時虱黄,有如下操作(詳情見《iOS與os x多線程和內(nèi)存管理》1.4.2 _weak修飾符 p68):
使用附有_weak修飾符的變量,即是使用注冊到autoreleasepool中的對象