本文是閱讀
《Objective-C進階程式設計》
過程中的一些心得。
1、ARC
1、記憶體管理
OC中根據自動計數來進行記憶體空間的管理,針對一個對象的操作經曆以下幾個階段:
- 生成并持有對象:
等方法,持有對象才能對該對象做出操作;alloc、new、copy、mutableCopy
- 持有對象:
,持有會使該對象的引用計數加一,可調用retain
檢視引用計數大小;retainCount
- 釋放對象:
,釋放會使該對象的引用計數減一,前提是已經持有該對象;release
- 廢棄對象:
,對象的引用計數為0時會清理對象的存儲空間。dealloc
還有一種操作
autorelease
,使用
NSRunloop
的
NSAutoreleasePool
管理引用持有的對象,直到其運作生命周期結束才會釋放所有持有的對象,相當于執行了對象的
release
方法,有點類似局部變量。
==注意:以上方法在
ARC
開啟時不可用。
2、引用修飾符
_ _strong
_ _strong
修飾符是id類型和對象類型的預設所有權修飾符,表示對對象的強引用,持有對象,該變量在超出其作用域時被廢棄,其引用的對象也會随之釋放。
強引用帶來的問題是循環引用,造成記憶體洩漏,看下面這個例子:
@interface Test:NSObject
{
id _strong obj;
}
- (void)setObject:(id _strong)obj;
@end
//循環引用
{
id test0 = [Test new]; //對象A
id test1 = [Test new]; //對象B
[test0 setObject:test1];
[test1 setObject:test0];
}
當test0要釋放對象A時,發現test1的變量對A有強引用,要test1先釋放B,同理要test0先釋放A。誰也無法釋放,導緻記憶體洩漏。
_ _weak
_ _weak
代表弱引用,不持有對象,當引用的對象被釋放時自動指向
nil
。前面的例子隻要把
Test
中的
obj
改為弱引用即可。
_ _unsafe_unretained
與
_ _weak
不同的是當引用的對象釋放時使用該修飾符修飾的變量時程式會崩潰。
_ _autoreleasing
相當于代替調用
autorelease
方法,一般不顯示附加該關鍵詞,取而代之的是
@autoreleasepool{ }
。在代碼塊中,編譯器會檢查方法名是否以
alloc、new、copy、mutableCopy
開始,如果不是則将傳回值的對象注冊到
autoreleasepool
。常用在循環中,可以減少記憶體占用率。
将對象注冊到
autoreleasepool
還有幾種隐示的方法(不加
_ _autorelease
關鍵字):
-
生成的對象超出函數範圍應該被自動釋放,但該對象同時又是函數的傳回值,會自動被注冊到+ (id) array {return [[NSMutableArray alloc] init]};
;autoreleasepool
- 通路
修飾的變量時,實際上必定通路注冊到_ _weak
的對象,因為通路過程中對象可能被廢棄,如果注冊的話保證在autoreleasepool
塊結束之前對象存在;@autoreleasepool
- id和對象的指針會預設加上
修飾符。_ _autoreleasing
3、屬性
屬性聲明的屬性與所有權修飾符的對應關系如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL5FFRNpXQE5UeRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxAjM2QDOxMjMzAzNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
對于
copy
關鍵詞,一般用于不可變類型,如
NSString、NSArray
等。相當于在
setter
方法中做了一次深拷貝,防止淺拷貝導緻該屬性随着所指派的改變而改變。而
strong
相當于在
setter
方法中進行淺拷貝,與所指派的指針共享對象。
2、BLOCK
1、文法
block可以了解為匿名函數,寫法為
^ 傳回值類型 (參數清單) {表達式}
,參數清單和傳回值都可省略,即便有傳回值,也可以通過
return
推測出來。當block作為變量時可以這樣寫:
傳回值類型 (^變量名稱) (參數清單)
,跟一般的變量不同的是block變量被複制後,可以用
變量名稱(參數清單)
,相當于函數調用。比較常見的方式是利用
typedef
聲明block變量,例如
typedef int (^blk_t) (int);
,之後就可以利用
blk_t blk
之類的方式聲明變量,非常友善。
block可截獲自動變量值,比如在block之前定義了一個變量,block裡面要利用此變量的值,之後修改了該變量的值,再次調用block的話此變量的值仍是最初捕獲的值。當在block中修改變量的值時會産生編譯錯誤,此時可在變量前加上
_ _block
修飾符。
2、BLOCK循環引用
當對象中含有block屬性,且block屬性中代碼塊中又利用到
self
或其它屬性的值,一般用
id _ _wak tmp = self;
來代替block中用到的
self
。
3、多線程
1、Dispatch Queue
将block加入不同的隊列執行順序也不一樣:
-
:按添加順序先後執行block,隻有一個線程Serial
-
:有多個線程,不同線程并行,同一個線程串行Concurrent
通過
dispatch_queue_create("<reverse DNS id>", DISPATCH_QUEUE_SERIAL/DISPATCH_QUEUE_CONCURRENT)
建立一個
dispatch_queue_t
對象,隊列中的每個blcok都持有該對象,除非執行完畢,否則不釋放。
API | 作用 |
---|---|
| 擷取主線程中執行的隊列,在RunLoop中執行,屬于Serial |
| 擷取全局的Concurrent隊列,優先級可以設定 |
| 擷取n秒之後的 對象 |
| n秒後将block加入到queue中 |
2、dispatch_async和dispatch_sync
-
表示異步,将block加入queue的隊尾,串/并執行取決于queue的類型,并馬上傳回dispatch_async
-
表示同步,将block加入queue的隊尾,串/并執行取決于queue的類型,但是會阻塞調用該函數的queue,直到block執行完畢才會傳回,使用時需要避免死鎖的情況:dispatch_sync
//主線程執行下面這條語句會出現死鎖
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_sync(dispatch_get_main_queue(), block);
});
3、Dispatch Group
-
建立diapactch_group_create()
對象dispatch_group_t
-
添加block到queuedispatch_group_async(group, queue, block)
-
監視block的執行情況,全部執行完畢會調用剛方法中的blockdispatch_group_notify(group, queue, block)
類似的還有
dispatch_apply
,均可實作等待所有block執行完畢:
dispatch_apply(counts, queue, ^(size_t index){ })
,将counts個block加入到queue中,全部執行完畢後再進行其它處理。
4、Dispatch Semaphore
利用信号量對資料的更新進行控制:
API | 作用 |
---|---|
| 擷取 對象,計數為n |
| 一直等待,直到計數值>=1 |
| 計數值+1 |
5、dispatch_once
單例模式:
dispatch_once(static修飾的dispatch_once_t對象, block進行資料初始化)