1. 程式人生 > >Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 讀書筆記(一)

Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 讀書筆記(一)

1.2.2

記憶體管理原則:

  • 自己生成的物件,自己所持有
  • 非自己生成的物件,自己也能持有
  • 不再需要自己持有的物件時釋放
  • 非自己持有的物件無法釋放

自己生成的物件,自己所持有

//自己生成並持有物件
id obj = [[NSObject alloc] init];
//自己持有物件

  使用NSObject類的alloc類方法就能自己生成並持有物件。指向生成並持有物件的指標
使用一下名稱開頭的方法名意味著自己生成的物件只有自己持有:

  • alloc
  • new
  • copy
  • mutableCopy

根據以上規則,下列名稱也意味著自己生成並持有物件

  • allocMyObject
  • newThatObject
    但是以下規則無效
  • allocate
  • newer

非自己生成的物件,自己也能持有

//非自己生成並持有的物件
id obj = [NSMutableArray array];
//取得的物件存在,但自己不持有物件

  原始碼中,NSMutableArray類物件唄賦值給變數obj,但變數obj自己並不持有該物件。使用retain方法可以持有物件

//在後面多加一句
[obj retain];
//自己持有物件

不再需要自己持有的物件時釋放

釋放時使用release方法

//自己生成並持有物件
id obj = [[NSObject alloc] init];
//自己持有物件
[obj release];
//釋放物件。指向物件的指標任然被保留在變數obj中,貌似能夠訪問
//但是物件一經釋放絕對不可訪問

  如果要用某個方法生成物件,並將其返還給該方法的呼叫方,那麼它的原始碼又是怎樣的呢

-(id)allocObject
{
//自己生成並持有物件
id obj = [[NSObject alloc] init];
//自己持有物件
return obj;
}

  如上,原封不動的返回用alloc方法生成並持有的物件,就能讓呼叫方也持有改物件。請注意allocObject這個名稱是符合前文命名規則的

//取得非自己生成並持有的物件
id obj1 = [ojb0 allocObject];
//自己持有物件

  所以,如果要自己不持有物件,就不能使用alloc/new/copy/mutableCopy開頭的方法名,實現如下

-(id)object
{
 id obj = [[NSObject alloc] init];
//自己持有物件
[obj autorelease];
//取得物件存在,但自己不持有
return obj;
}

  上例中,我們使用了autorelease方法,該方法可以取得物件存在,但自己不持有物件。autorelease提供使得物件在超出指定的生存範圍時能夠自動並正確得釋放的功能
  當然,也能夠通過retain方法將呼叫autorelease方法取得的物件變為自己持有

id obj1 = [obj0 object];
//取得但自己不持有
[obj1 retain];
//自己持有物件

無法釋放非自己持有的物件

id obj = [[NSObject alloc] init];
[obj release];
[obj release];
//這裡這裡就崩潰了
id obj1 = [obj0 object];
[obj1 release];
//這裡也崩潰

1.3.3所有權修飾符

__strong

ARC時,id型別的物件型別同C語言其他型別不同,其型別上必須附加所有權修飾符。所有權修飾符一共有4種

__strong __weak __unsafe __unretained __autoreleasing

__strong修飾符是id型別的和物件型別預設的所有權修飾符。也就是說,以下原始碼中的id變數,實際上被附加了所有權修飾符

id obj = [[NSObject alloc] init];

其與以下相同

id __strong obj = [[NSObject alloc] init];

MRC下:

id obj = [[NSObject alloc] init];

再看以下程式碼

{
id __strong obj = [[NSObject alloc] init];
}

MRC下:

{
id obj = [[NSObject alloc] init];
[obj release];
}

為了釋放生成並持有的物件,增加了release方法的程式碼
如此原始碼所示,附有strong修飾符的變數obj在超出其變數作用域時,即在該變數被廢棄時,會釋放其被賦予的物件 strong、weak、autoreleasing,可以保證附有這些修飾符的自動變數初始化為nil

id __strong obj0;
id __weak obj1;
id __autoreleasing obj2;

同:

id __strong obj0 = nil;
id __weak obj1 = nil;
id __autoreleasing obj2 = nil;
   id __weak obj = [[NSObject alloc] init];

        NSLog(@"弱引用自身地址:%p",&obj);

        NSLog(@"弱引用指向地址:%p",obj);



        id __strong obj0 = [[NSObject alloc] init];

        id __weak obj1 = obj0;

        NSLog(@"強引用自身地址:%p",&obj0);

        NSLog(@"弱引用自身地址:%p",&obj1);

        NSLog(@"強引用指向地址:%p",obj0);

        NSLog(@"弱引用指向地址:%p",obj1);



      //  obj1 = nil;

      //      obj0 = nil;

        NSLog(@"弱引用銷燬時強型別變數指向地址:%p",obj0);

        NSLog(@"弱引用銷燬時弱型別變數指向地址:%p",obj1);
1240
開啟obj0 = nil的註釋結果
1240
開啟obj1 = nil的註釋結果
由此可見,強引用時對變數擁有,弱引用時只是指向該變數而不擁有

非自己生成的物件,即alloc/new/copy/mutableCopy以外的建立方法取得物件,並且賦值給非strong變數時,物件將會自動註冊至autoreleasepool,從而不至於立刻被析構; (3.1)
當賦值給給strong變數時,編譯器作自動優化插入objc_retainAutoreleaseedReturnValue, 而不是插入retain,以避免註冊至autoreleasePool.(3.3)
訪問weak變數時,將會把指向物件註冊至autoreleasePool.(3.3)。另外,對weak變數再訪問會再註冊,訪問幾次則會註冊幾次,所以這裡,最好把weak變數賦給一個strong變數,避免重複註冊到aotureleasepool

獲取引用計數數值的函式uintptr_t objc_rootRetainCount(id obj)
例子:

{
id __strong obj = [[NSObject alloc] init];
id __weak o = obj;
NSLog(@"retain count = %d", _objc_rootRetainCount(obj));//結果為1
}

dispatch_sync函式。它意味著“同步”,也就是將制定Block“同步”追加到指定的Dispatch Queue中。在追加Block結束之前,dispatch_sync函式會一直等待
sync引起執行緒死鎖

sync 會等到 後面block 執行完成才返回, sync 又再 dispatch_get_main_queue() 佇列中,
它是序列佇列,sync 是後加入的,前一個是主執行緒,
所以 sync 想執行 block 必須等待主執行緒執行完成,主執行緒等待 sync 返回,去執行後續內容。

dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================2"); });

以下正面例子

- (void)viewDidLoad{ 
[super viewDidLoad]; 
dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
NSLog(@"=================1"); 
dispatch_sync(dispatch_get_main_queue(), ^{ 
NSLog(@"=================2"); }); 
NSLog(@"=================3"); });
}

程式會完成執行,為什麼不會出現死鎖。
首先: async 在主執行緒中 建立了一個非同步執行緒 加入 全域性併發佇列,async 不會等待block 執行完成,立即返回,
1,async 立即返回, viewDidLoad 執行完畢,及主執行緒執行完畢。2,同時,全域性併發佇列立即執行非同步 block , 列印 1, 當執行到 sync 它會等待 block 執行完成才返回, 及等待
dispatch_get_main_queue() 佇列中的 mianThread 執行完成, 然後才開始呼叫block 。因為1 和 2 幾乎同時執行,因為2 在全域性併發佇列上, 2 中執行到sync 時 1 可能已經執行完成或 等了一會,mainThread 很快退出, 2 等已執行後續內容。