引用計數式內存管理方式
- 自己生成的對象,自己所持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放
關鍵詞:“生成”,“持有”,“釋放”,“廢棄”
對象操作 | Objective-C 方法 |
---|---|
生成并持有對象 | alloc/new/copy/mutableCopy 等方法 |
持有對象 | retain 方法 |
釋放對象 | release 方法 |
廢棄對象 | dealloc 方法 |
*注意:這些有關Objective-C 內存管理的方法,實際上不包括在語言中,而是包含在Cocoa 框架中用于 OS X、iOS應用開發。Cocoa 框架中Foundation 框架類庫的NSObject 類擔負內存管理的職責 *
自己生成的對象,自己所持有
- alloc
- new
- copy
- mutableCopy
對“自己”一詞的理解,可以解釋”對象的使用環境“,或者理解為程序猿“本身”
/*
* 自己生成并持有對象
*/
id obj = [NSObject alloc] init];
/*
* 自己持有對象
*/
非自己生成的對象,自己也能持有
使用上述方法以外取得的對象,因為非自己生成持有,所以自己不是該對象的持有者
/*
* 取得非自己生成并持有的對象
*/
id obj = [NSMutableArray array];
/*
* 取得對象的存在,但自己并不持有對象
*/
[obj retain];
/*
* 自己持有對象
*/
不再需要自己持有的對象時釋放
/*
* 自己生成并持有對象
*/
id obj = [NSObject alloc] init];
/*
* 自己持有對象
*/
[obj release];
/*
* 釋放對象
* 指針仍然保留在變量obj中,但是對象一經釋放絕對不可訪問
*/
/*
* 自己生成并持有對象
*/
id obj = [NSObject alloc] init];
/*
* 自己持有對象
*/
[obj autorelease];
/*
* 取得對象的存在,但是自己不持有該對象
*/
[obj retain];
/*
* 自己持有對象
*/
[obj release];
/*
* 釋放對象
* 指針仍然保留在變量obj中,但是對象一經釋放絕對不可訪問
*/
** release是立即釋放的,autorelease是延遲釋放 **
無法釋放非自己持有的對象
/*
* 自己生成并持有對象
*/
id obj = [NSObject alloc] init];
/*
* 自己持有對象
*/
[obj release];
/*
* 對象已經釋放
*/
[obj release];
/*
* 釋放之后再次釋放已非自己持有的對象
* 應用程序崩潰
*/
autorelease
自動釋放,看起來感覺很像ARC
- 生成并持有NSAutoreleasePool對象
- 調用已分配對象的 autorelease 實例方法
- 廢棄NSAutoreleasePool 對象
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
ARC 出現了
ARC有效時候,其類型必須附加所有權修飾符,所以修飾符一共有4種
- __strong
- __weak
- __unsafe_unretained
- __autoreleasing
__strong 修飾符
id 類型 和對象類型默認的所有權修飾符
id obj = [NSObject alloc] init];
// 等同于
id __strong obj = [NSObject alloc] init];
/* ARC無效的情況下 */
{
id obj = [NSObject alloc] init];
[obj release];
}
/*
附有__strong 修飾符的變量obj 在其超出變量的作用域后,及該變量被廢棄的時候,會釋放其被賦予的對象NSObject
*/
__weak 修飾符
單單通過__strong 修飾符編譯器進行內存管理,還是不能解決有些重大的問題。例如“循環引用”
@interface Test : NSObject
{
id __strong _obj;
}
- (void)setObject:(id __strong)object;
@end
@implementation Test
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (void)setObject:(id __strong)object
{
_obj = object;
}
- (void)dealloc
{
NSLog(@"正常釋放了");
}
@end
- (void)test{
Test *test0 = [[Test alloc] init];
Test *test1 = [[Test alloc] init];
NSLog(@"%@", test0);
NSLog(@"%@", test1);
test0 = nil;
test1 = nil;
NSLog(@"%@", test0);
NSLog(@"%@", test1);
}
/*
打印結果
2017-05-19 16:14:22.115152 ios內存管理[14156:2305168] <Test: 0x1740170a0>
2017-05-19 16:14:22.115270 ios內存管理[14156:2305168] <Test: 0x1740170b0>
2017-05-19 16:14:24.304241 ios內存管理[14156:2305168] 正常釋放了
2017-05-19 16:14:25.107007 ios內存管理[14156:2305168] 正常釋放了
2017-05-19 16:14:25.107290 ios內存管理[14156:2305168] (null)
2017-05-19 16:14:25.107454 ios內存管理[14156:2305168] (null)
正常釋放
*/
- (void)test{
Test *test0 = [[Test alloc] init];
Test *test1 = [[Test alloc] init];
[test0 setObject:test1];
[test1 setObject:test0];
NSLog(@"%@", test0);
NSLog(@"%@", test1);
test0 = nil;
test1 = nil;
NSLog(@"%@", test0);
NSLog(@"%@", test1);
}
/*
打印結果
2017-05-19 16:18:33.019380 ios內存管理[14162:2306012] <Test: 0x17400a000>
2017-05-19 16:18:33.019495 ios內存管理[14162:2306012] <Test: 0x17400a010>
2017-05-19 16:18:33.019544 ios內存管理[14162:2306012] (null)
2017-05-19 16:18:33.019589 ios內存管理[14162:2306012] (null)
此時沒有正常釋放
*/
1.當nil被賦予了test0, test0對Test對象A的強引用失效,Test對象A的所有者不存在了,因此要廢棄Test對象A
2.當nil被賦予了test1, test1對Test對象B的強引用失效,Test對象B的所有者不存在了,因此要廢棄Test對象B
3.但是此時,Test對象A還被Test對象B的成員變量_obj 所持有
4.但是此時,Test對象B還被Test對象A的成員變量_obj所持有
所以,因為循環引用無法正常釋放,導致了內存泄漏
這個時候__weak 修飾符就派上用場了
個人理解:對象A被 用 __weak修飾的變量b所持有,當對象A不再被其他變量強引用著,對象A會直接被釋放
{
id __strong obj0 = [[NSObject alloc] init];
/*
obj0 變量為強引用
*/
id __weak obj1 = obj0;
/*
obj1 變量持有生成對象的弱引用
*/
}
/*
obj0 變量超出其作用域,強引用失效,所以自動釋放自己持有的對象
因為對象的所有者不再存在,所以廢棄該對象
*/
前面的對象Test的成員變量 _obj 用__weak 修飾,這樣就可避免循環引用
// 定義一個全局變量
// id __weak obj1;
- (void)test1
{
obj1 = nil;
Test __strong *test0 = [[Test alloc] init];
obj1 = test0;
NSLog(@"%@", test0);
NSLog(@"%@", test0);
}
NSLog(@"obj1 : %@", obj1);
/*
打印結果
2017-05-19 17:25:31.042324 ios內存管理[14237:2319832] <Test: 0x174003dd0>
2017-05-19 17:25:31.042444 ios內存管理[14237:2319832] <Test: 0x174003dd0>
2017-05-19 17:25:31.042537 ios內存管理[14237:2319832] 正常釋放了
2017-05-19 17:25:31.042613 ios內存管理[14237:2319832] obj1 : (null)
*/
/*
obj0 變量超出其作用域,強引用失效,所以自動釋放自己持有的對象,因為對象的所有者不再存在,所以廢棄該對象
在廢棄對象的同時,obj1變量的弱引用失效,nil賦值給obj1
*/
__unsafe_unretained修飾符
最不安全的修飾符,
它和__weak 修飾符號一樣都是不能自己生成并持有對象,因為會立即釋放。但是用__strong修飾符的變量賦值給他,要確保該賦值的對象確實的存在,不然會變成被廢棄的(懸垂指針)
__autoreleasing 修飾符
{
@autoreleasepool {
/*
取得非自己生成并持有的對象
*/
id __strong obj = [NSMutableArray array];
/*
因為變量obj為強引用,
所以自己持有對象
并且該對象
由編譯器判斷其方法名后
自動注冊到autoreleasepool中
*/
}
/*
變量obj在超出其作用域后,強引用失效,自動會釋放對象,
隨著@autoreleasepool塊的結束,所有注冊到@autoreleasepool中的對象被釋放,
對象的所有者不存在,所以廢棄對象
*/
}