ios 內存管理

引用計數式內存管理方式

  • 自己生成的對象,自己所持有
  • 非自己生成的對象,自己也能持有
  • 不再需要自己持有的對象時釋放
  • 非自己持有的對象無法釋放
關鍵詞:“生成”,“持有”,“釋放”,“廢棄”
對象操作 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

  1. 生成并持有NSAutoreleasePool對象
  2. 調用已分配對象的 autorelease 實例方法
  3. 廢棄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中的對象被釋放,
            對象的所有者不存在,所以廢棄對象
         */
}
在訪問附有 __weak 修飾符的變量必須訪問注冊到 @autoreleaspool的對象!?。。∵@是因為__weak修飾符只持有對象的弱引用,在訪問對象的過程中,該對象隨時有可能被廢棄,如果把要訪問的對象注冊到@autoreleaspool中,那么在@autoreleaspool塊結束之前都能確保對象的存在。

ARC 的過程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容