主要摘錄了《 Effective Objective-C》里的編寫高質量的方法;
1 熟悉Objective -C
1.1 OC 起源
- OC 為C語言增加了面對對象的特性,是 C 的超集,并且使用動態綁定的消息結構;
1.2 在類的頭文件中盡量少引入其他頭文件
將引入頭文件的時機盡量延后,只在確有需要時才引入,這樣就可以減少類的使用者所需要引入的頭文件的數量:
- 除非確有必要,否則不要引入頭文件,一般來說,應在某個類的頭文件中使用向前聲明來提及別的類,并在實現文件中引入那些類的頭文件。這樣做可以盡量降低類之間的耦合;
- 有時無法使用向前聲明,比如要聲明某個類遵循一項協議。這種情況下,盡量把“該類遵循某協議”這條聲明移到
class-continuation
分類中。如果不行,則把協議單獨放在一個頭文件中,然后將其引入;
有時候在編寫頭文件時,需要引入某個類A(如作為當前類的某個屬性來使用),但是不需要知道這個類A的實現細節,此時我們不需要直接引入這個類A的頭文件,只需要告訴編譯器,類A 是一個類就可以了,然后在實現文件里再引入類A的頭文件; 向前聲明的語法為: @class 類A
;
1.3 多用字面量語法 ,少用與之等價的方法
- 應用使用字面量語法來創建字符串、數值、數組、字典。與創建此類對象的常規方法相比,這么做更加簡明扼要;
- 應用通過取下標操作來訪問數組下標顴字典中的鍵所對應的元素;
- 用字面量語法創建數組或字典時,值中有
nil
,則會拋出異常。因此,需要確保值里不含nil
;
如 :
NSNumber *someNumber = [NSNumber numberWithInt:1];
// 字面量語法
NSNumber *someNumber = @1;
NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys:
@"Matt",@"firstName",@"Ga",@"lastName",nil];
// 字面量語法
NSDictionary *persionData= @{@"firstName" : @"Matt",@"lastName" : @"Ga"};
NSString *lastName = [personData objectForKey:@"lastName"];
// 字面量語法
NSString *lastName = personData[@"lastName"];
由以上幾個例子中,可以很明顯可以看到字面量語法全更加簡潔直觀;但字面量語法有個小限制,就是除了字符串以外,所創建出來的對象必須屬于 Foundation
框架;
1.4 多用類型常量,少用# define 預處理指令
- 不要用預處理指令定義常量,這樣定義出來的常量不包含類型信息,編譯器只會在編譯前進行查找與替換操作,但不確保準確性;
- 在實現文件中使用
static const
來定義“只在編譯單元可見的常量”,這類常量一舉地在全局符號表里出現; - 在頭文件中使用
extern
來聲明全局常量,并在相關實現文件中定義其值。這類常量會出現在全局符號表里,所以其名稱應加以區分,通常使用類名做前綴;
全名法則: 若常量僅在編譯單元內可見,則在前面加字母k
,如果在類外可見,則通常以類名為前綴;
1.5 用枚舉表示狀態、選項、狀態碼
- 應該用枚舉來表示狀態機的狀態,傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字;
- 如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可以同時使用,則將各選項值定義為2的冪,以便通過按位或操作將其組合起來;
- 用
NS_ENUM
和NS_OPTIONS
宏來定義枚舉類型,并指明其底層的數據類型; - 在處理枚舉類型的
switch
語句中不要實現default
分支,這樣的話,加入新枚舉類型后,編譯器就會提示switch
語句并沒有處理所有的枚舉;
2 對象、消息、運行期
2.1 理解“屬性”這一概念
- 可以用
@property
語法來定義對象中所封裝的數據; - 通過“特質”來指定存儲數據所需的正確說到底;
- 在設置屬性所對應的實例變量時,一定要遵從該屬性所聲明的語義;
屬性可以擁有的特質分為四類: - 原子性,如果屬性具備
nonatomic
特質,則不使用同步鎖,否則它就是原子的; - 讀寫權限,readwrite/readonly,
- 內存管理語義
- assign 設置方法,只會執行針對“純量類型”的簡單賦值操作;
- strong 表明該屬性定義了一種“擁有關系”設置方法會先保留新值,并釋放舊值,然后再將新值設置上去;
- weak 表明該屬性定義了一種“非擁有關系”設置方法既不保留新值,也不釋放舊值,只是簡單的將新值設置上去,如果該屬性所指的對象遭到摧毀時,屬性值也會清空;
- unsafe_unretained 語義與 assign 相同,但是適用于“對象類型”,該特性表達一種“非擁有關系”,但目標對象遭到摧毀時,屬性值不會自動清空;
- copy 所屬關系與 strong 類似,但設置方法不保留新值,而是將其“copy";
2.2 在對象內部盡量直接訪問實例變量
- 在對象內部讀取數據時,應該直接通過實例變量來讀,而寫入數據時,則就通過屬性來寫;
- 在初始化方法及 dealloc 方法中總是應該直接通過實例變量來讀寫數據;
- 有時會使用惰性初始化技術配置某份數據,這種情況下,需要通過屬性來讀取數據;
2.3 理解”對象等同性“這一概念
- 若想檢測對象的等同性,請提供
isEqual:
和hash
方法; - 相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象卻未必相同;
- 不要盲目地逐個檢測每條屬性,而是應該依照具體需求來制定檢測方案;
- 編寫
hash
方法時,應該使用計算速度快而且哈希碼碰撞機率低的算法;
==
操作符比較的是兩個指針本身,而不是其所指對象,一般常用 isEqual
方法來判斷兩個對象的等同性;
2.4 以“類族模式” 隱藏實現細節
- 類族模式可以把實現細節隱藏在一套簡單的公共接口后面;
- 系統框架中經常使用類族;
- 從類族的公共抽象基類中繼承子類時要當心,若有開發文檔,則應首先閱讀;
類似于Java 設計模式的抽象工廠或工廠方法;
2.5 在既有類中使用關聯對象存放自定義數據
- 可以通過“關聯對象”機制來把兩個對象連起來;
- 定義關聯對象時可以指定內存管理語義,用以模仿定義屬性時所采用的“擁有關系”與“非擁有關系”;
- 只有在其他做法不可行時才應選用關聯對象,因為這種做法通常會引入難于查找的bug;
2.6 理解 objc_msgSend 作用
- 消息由接收者,選擇子及參數構成。給某對象“發送消息(
invoke a message
),也就相當于在該對象上“調用方法”; - 發給某對象的全部消息都要由“動態消息派發系統”來處理,該系統會查出對應的方法,并執行其代碼;
在OC中,如果向對象傳遞消息,那就會使用動態綁定機制來決定需要調用的方法。而在底層,所有方法都是普通的C語言函數,然而對象在接收到消息后,究竟該調用哪個方法則完全于運行期決定。如:
//以下語句是給對象發送一條消息
int value = [someObject messageName :parameter];
其中,someObject
叫接收者,messageName
叫選擇子(selector
),選擇子和參數合起來稱為消息;
2.7 理解消息轉發機制
- 若對象無法響應某個選擇子,則進入消息轉發流程;
- 通過運行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中;
- 對象可以把其無法解讀的某些選擇子轉交給其他對象來處理;
- 經過上述兩步后,如果還是沒有辦法處理選擇子,則啟動完整的消息轉發機制;
2.8 用“方法調配技術”調試“黑盒方法”
- 在運行期,可以向類中新增或替換選擇子所對應的方法實現;
- 使用另一份實現來替換原有的方法實現,常用來向原有實現中添加新功能;
- 一般來說,只有調試程序的時候才需要在運行期修改方法實現;
類的方法列表會把選擇子名稱映射到相關的方法實現之上,使用“動態消息派發系統能夠據此找到應該調用的方法。
2.9 理解”類對象“的用意
- 每個實例都有一個指向
Class
對象的指針,用以表明其類型,而這些Class 對象則構成了類的繼承體系; - 如果對象類型無法在編譯期確定,那么就應該使用類型信息查詢方法來探知;
- 盡量使用類型信息查詢方法來確定對象類型,而不要直接比較類對象,因為某些對象可能實現了消息轉發功能。
isMemberOfClass
能夠判斷出對象是否為某個特定類的實例;
isKindOfClass
能夠判斷出對象是否為某類或其派生類的實例;
3 接口與API設計
3.1 用前綴避免命名空間沖突
- 選擇與你的公司、應用程序或二者皆有關聯之名作為類名的前綴,并在所有代碼中均使用這一前綴;
- 若自己所開發的程序庫中用到了第三方庫,則應為其中的名稱加上前綴;
OC 沒有其他語言那種內置的命名空間機制。因此我們在起名時需要設法避免潛在的例句沖突。我們在選擇前綴時,應該是三個字母的;
3.2 提供“全能初始化方法”
- 在類中提供現代戰爭全能初始化方法,并于文檔里指明。其他初始化方法均應調用此方法;
- 若全能初始化方法與超類不同,則需覆寫超類中對應的方法;
- 如果超類的初始化方法不適用于子類,則應該覆寫這個超類方法,并在其中拋出異常;
全能初始化方法類似于 Java 中提供不同構造參數的構造方法,所有的構造方法最終都會調用其中參數最完整的構造方法;
3.3 實現 description 方法
- 實現 description 方法返回一個有意義的字符串,用以描述該實例;
- 若想在調試時打印出更詳盡的對象描述信息,則應實現 debugDescription 方法;
description
方法類似于 Java
里 Object
的 toString
方法的功能,而且在調試時,如果有實現debugDescription
方法,則會調用該方法來輸出更詳細的信息;
3.4 盡量使用不可變對象
- 盡量創建不可變的對象;
- 若某屬性僅可用于對象內部修改,則在
class-continuation
分類中將其由readonly
屬性擴展為readwrite
屬性; - 不要把可變的
collection
作為屬性公開,而應提供相關方法,以此修改對象中的可變collection
;
3.5 使用清晰而協調的命名方式
- 起名時應遵從OC的命名規范,這樣創建出來的接口更容易為開發者所理解;
- 方法名要言簡。。。。
- 方法名里不要使用縮略后的類型名稱;
- 給方法起名時的第一要務是確保其風格與你自己的代碼或所要集成的框架相符;
3.6 為私有方法名加前綴
- 給私有方法的名稱加上前綴,這樣可以很容易地將其同公共方法區分開;
- 不要單用現代戰爭下劃線做私有方法的前綴,因為這種做法是預留給蘋果使用的;
3.7 理解 OC 錯誤類型
- 只有發生了可使整個應用程序崩潰的嚴重錯誤時,才應使用異常;
- 在錯誤不那么嚴重的情況下,可以指派“委托方法”來處理錯誤,也可以把錯誤信息放在
NSError
對象里,經由“輸出參數”返回給調用者;
如果出現非致命的錯誤時,則可以令方法返回 nil/0
或使用 NSError
來表明其中有錯誤發生;
3.8 理解 NSCopying 協議
- 若想令自己所寫的對象具有拷貝功能,則需實現
NSCopying
; - 如果自定義的對象分為可變版本與不可變版本,那么就要同時實現
NSCopying
和NSMutableCopying
協議; - 復制對象時需決定采用淺拷貝還是深拷貝;
- 如果對象需要深拷貝,那么可考慮新增一個專門執行深拷貝的就去;
一般情況下,遵從了NSCopying
協議的對象,執行的都是淺拷貝,除非該對象有特別說明它是用深拷貝來實現Copying
,否則應該自己去編寫深拷貝的;
4 協議與分類
4.1 通過委托與數據源協議進行對象通信
- 委托模式為對象提供了一套接口,使其可由此將相關事件告知其他對象;
- 將委托對象應該支持的接口定義成協議,在協議中把可能需要處理的事件定義成方法;
- 當某對象需要從另外一個對象中獲取數據時,可以使用委托協議。這種情況下,該模式亦稱為
data source protocal
- 若有必要,可實現含有位段的結構體,將委托對象是否能響應相關協議方法這一信息緩存至其中;
OC 中廣泛使用 delegate pattern
的模式來實現對象間的通信,該模式的主旨是:定義一套接口,某對象若想接受另一對象的委托,則需遵從此接口; 其實這就是 Java
里的編程規則里的面向接口編程;所謂的位段結構體,就是用一個屬性來表明委托對象實現了哪些協議方法,每個協議方法對應于該屬性的一個二進制位;
需要注意的是 委托對象與被委托的對象之間的關系應該是非擁有關系,也就是對應的屬性得用weak
來修飾;
4.2 將類的實現代碼分散到便于管理的數個分類之中
- 使用分類機制把類的實現代碼劃分成易于管理的小塊;
- 將“私有”方法歸入名為
Private
的分類中,以隱藏實現細節;
4.3 總是為第三方類的分類名稱加前綴
- 向第三方類中添加分類時,總應給其名稱加上你專用的前綴;
- 向第三方類中添加分類時,總應給其中的方法名加上你專用的前綴;
分類機制通常用于向無源碼的既有類中新增功能,分類中的方法是直接添加在類里面的;
如:
//給NSString 添加一個分類:ABC_HTTP
@interface NSString(ABC_HTTP)
- (NSString *) abc_urlEncodeString;
@end
4.4 勿在分類中聲明屬性
- 把封裝數據所用的全部屬性都定義在接口里;
- 在
class-continuation
分類之外的其他分類中,可以定義存取方法,但盡量不要定義屬性;
正確的做法是把所有屬性都定義在主接口里,類所封裝的全部數據都應該定義在主接口里;
4.5 使用class-continuation
分類 隱藏實現細節
- 通過
class-continuation
分類向類中新增實例變量; - 如果某屬性在主接口中聲明為"只讀",而類的內部又要用設置方法修改此屬性,那么就在
class-continuation
分類中將其擴展為:可讀寫; - 把私有方法的原型聲明在
class-continuation
分類里面; - 若想使所遵循的協議不為人所知,則可于
class-continuation
分類里聲明;
例子:
// EOCPerson.h
@interface EOCPerson :NSObject {
.....
}
//EOCPerson.m
//這是EOCPerson的`class-continuation`分類
@interface EOCPerson(){
.....
}
@implementation EOCPerson {
...
}
### 4.6 通過協議提供匿名對象
- 協議可在某種程度上提供匿名類型,具體的對象類型可以淡化成遵從某協議的 id 類型,協議里規定了對象所應實現的方法;
- 使用匿名對象來隱藏類型名稱;
- 如果具體類型不重要,重要的是對象能夠響應特定方法,那么可使用匿名對象來表示;
OC 里的協議 就是 Java 里的接口,協議定義了一系列方法,遵從些協議的對象實現它們
5 內存管理
5.1 理解引用計數
- 引用計數機制通過可以遞增遞減的計數器來管理內存。對象創建好之后,其保留計數至少為1.若保留計數為正,則對象繼續存活。當保留計數降為0時,對象就被銷毀了;
- 在對象生命期中,其余對象通過引用來保留或釋放此對象。保留與釋放操作分別會 遞增及遞減保留計數;
5.2 以 ARC 簡化引用計數
- 有 ARC 后,就不需要擔心內存管理問題了;
- ARC 管理對象生命期的辦法基本上就是:在合適的地方插入“保留”和“釋放”操作;
- 由方法反返回的對象,其內存管理語義總是通過方法名來體現;ARC 將此對象確定為開發者必須遵守的規則;
- ARC 只負責管理
Objective-C
對象的內存;
若方法名以下列詞語開頭,則其返回的對象歸調用者所有:
- alloc
- new
- copy
- mutableCopy
5.3 在 dealloc 方法中只釋放引用并解除監聽
- 在
dealloc
方法里,應該做的事情就是釋放指向其他對象的引用,并取消原來訂閱的“鍵值觀測(KVO)”或 NSNotificationCenter 等通知,不要做其他事情; - 如果對象持有文件描述符等系統資源,那么應該專門編寫一個方法來釋放此資源;這樣的類要和其使用者約定,用完資源后必須調用 close 方法;
- 執行異步任務的方法不應在 dealloc 里調用;只能在正常狀態下執行的那些方法也不應在 dealloc 里調用,因為此時對象已處于正在回收的狀態了;
5.4 編寫 “異常安全代碼”時留意內存管理問題
- 在捕獲異常時,一定要注意將 try 塊內所創立的對象清理干凈;
- 在默認情況下,ARC 不生成安全處理異常所需的清理代碼。開啟編譯器標志后,可生成這種代碼,不過會導致應用程序變大,而且會降低運行效率;
5.5 以弱引用避免保留環
- 將某些引用設為 weak,可避免出現保留環;
- weak 引用可以自動清空,也可以不自動清空。自動清空(autonilling)是隨著 ARC 而引入的新特性,由運行期系統來實現。在具備自動清空功能的弱引用上,可以隨意讀取其數據,因為這種引用不會指向已經回收過的對象;
避免保留環的最佳方式就弱引用,這種引用經常用來表示“非擁有關系”(nonowning relationship)。將屬性聲明為unsafe_unretained
即可。
5.6 以“自動釋放池塊” 降低內存峰值
- 自動釋放池排布在棧中,對象收到
autorelease
消息后,系統將其放入最頂端的池里。 - 合理運用自動釋放池,可降低應用程序的內存峰值;
-
@autoreleasepool
這種新式寫法能創建出更為輕便的自動釋放池;
釋放對象有兩種方式:一種是調用realease
方法,使其保留計數立即遞減;另一種是調用autorealease
方法,將其加入“自動釋放池”中。自動釋放池用于存放那些需要在稍后某個時刻釋放的對象;
5.7 用“僵尸對象”調試內存管理問題
- 系統在回收對象時,可以不將其真的回收,而是把它轉化為僵尸對象。通過環境變量
NSZombieEnabled
可開啟此功能; - 系統會修改對象的
isa
指針,令其指向特殊的僵尸類。從而使該對象變為僵尸對象; - 僵尸類能夠響應所有的選擇子,響應方式為:打印一條包含消息內容及其接收者的消息,然后終止應用程序;
5.8 不要使用retainCount
- 對象的保留計數看似有用,實則不然,因為任何給定時間點上的“絕對保留計數”(absolute retain count)都無法反映對象生命期的全貌;
- 引入 ARC 之后,retainCount 方法就正式廢止了,在ARC下調用該方法會導致編譯器報錯;
6 塊與大中樞派發
6.1 理解“塊”這一概念
- 塊是 C、C++、Objective-C 中詞法的閉包;
- 塊可接受參數,也可返回值;
- 塊可以分配在棧或堆上,也可以是全局的。分配在棧上的塊可拷貝到堆里,這樣的話,就和標準的Objective-C 對象一樣,具備引用計數了;
塊所占的內存區域是分配在棧中的。這也就是說塊只在定義它的那個范圍內有效。
6.2 為常用的塊類型創建 typedef
- 以
typedef
重新定義塊類型,可令塊變量用起來更加簡單; - 定義新類型時應遵從現有的命名習慣,勿使其名稱與別的類型相沖突;
- 不妨為同一個塊簽名定義多個類型別名,如果要重構的代碼使用了塊類型的某個別名,那么只需修改相應
typedef
中的塊簽名,無須改動其他typedef
;
6.3 用 handler 塊降低代碼分散程度
- 在創建對象時,可以使用內聯的
handler
塊將相關業務邏輯一并聲明; - 在有多個實例需要監控時,如果采用委托模式,那么經常需要根據傳入的對象來切換,而若改用
handler
塊來實現,則可以直接將塊與相關對象放在一起; - 設計 API 時如果用到了
handler
塊,那么可以增加一個參數,使調用者可通過此參數來決定應該把塊安排在哪個隊列執行;
6.4 用塊引用其所屬對象時不要出現保留環
- 如果塊所捕獲的對象直接或間接地保留了塊本身,那么就得當心保留環的問題;
- 一定要找個適當的時機解除保留環,而不能把責任推給API的調用者;
6.5 多用派發隊列,少用同步鎖
- 派發隊列可用來表述同步語義
synchronization semantic
,這種做法要比使用@synchronized
塊或 NSLock 對象更簡單; - 將同步與異步派發結合起來,可以實現與普通加鎖機制一樣的同步行為,而這么做卻不會阻塞執行異步派發的線程;
- 使用同步隊列及柵欄塊,可以令同步更加高效;
6.6 多用 GCD,少用 @performSelector
系統方法
- performSelector 系統方法在內存管理方面容易有缺失。它無法確定將要執行的選擇子具體是什么;
- performSelector 系列方法所能處理的選擇子太過局限,選擇子的返回值類型及發送給方法的參數個數都受到限制;
- 如果想把任務放在另一個線程上執行,那么最好不要用 performSelector 系列方法,而是應該把任務封裝到塊里,然后調用GCD的相關方法來實現;
6.7 掌握 GCD 及操作隊列的使用時機
- 在解決多線程與任務管理問題時,派發隊列并非唯一實現方案;
- 操作隊列提供了一套高層的
Objective-C
API,能實現純 GCD 所具備的絕大部分功能,而且還能完成一些更為復雜的操作,那些操作若改用 GCD 來實現,則需另外編碼;
在執行后臺任務時,GCD 并不一定是最佳方式,還有一種技術叫 NSOperationQueue
,它雖然與 GCD 不同,但是卻與之相關,開發者可以把操作以 NSOperation
子類的形式放在隊列中,而這些操作也能夠并發執行;
使用NSOperation
和 NSOperationQueue
的好外:
- 取消某個操作,如果使用操作隊列,則想要取消操作是很容易的,只需要調用
cancel
就可以了;若使用 GCD 則沒有辦法取消; - 指定操作間的依賴關系;
- 通過鍵值觀測機制監控
NsOperation
對象的屬性; - 指定操作的優先級;
6.8 通過 Dispatch Group
機制,根據系統資源狀況來執行任務
- 一系列任務可歸入一個 dispatch group 之中,開發者可以在這組任務執行完畢時獲得通知;
- 通過
dispatch group
,可以在并發派發隊列里同時執行多項任務,此時 GCD 會根據系統資源來調度這些并發執行的任務;
dispatch group
是 GCD 的一項特性,能夠把任務分組。調用者可以等待這組任務執行完畢,也可以在提供回調函數之后繼續往下執行,這組任務完成時,調用者會得到通知;
6.9 使用diapatch_once
來執行只需要運行一次的線程安全代碼
- 經常需要編寫“只需執行一次的線程安全代碼”,通過GCD 所提供的
disptch_once
來實現;
-標記應該聲明在static
或global
作用域中,這樣的話,在把只需執行一次的塊傳給dispatch_once
函數時,傳進去的標記也是相同的;
常用 diapatch_once
來編寫線程安全的單例;
6.10 不要使用dispatch_get_current_queue
-
dispatch_get_current_queue
函數的行為常常與開發者所預期的不同,此函數已經廢棄,只應做為開發調試用; - 由于派發隊列是按層級來組織的,所以無法單用某個隊列對象來描述“當前隊列”這一概念;
-
dispatch_get_current_queue
函數用于解決由不可重入代碼所引發的死鎖,然而能用此函數解決的問題,也能改用“隊列待定數據”來解決;
7 系統框架
7.1 熟悉系統框架
- 許多系統框架都可以直接使用。其中最重要的是
Foundatoin
與CoreFoundation
,這兩個框架提供了構建應用程序所需的許多核心功能;
7.2 多用塊枚舉,少用 for 循環
- 遍歷
collection
有四種方式。最基本的是for
循環,其次是NSEnumerator
遍歷法及快速遍歷法,最新,最先進的方式則是"塊枚舉法"; - "塊枚舉"法,本身就能通過 GCD 來并發執行遍歷操作,無須額外編寫代碼。而采用其他遍歷方式則無法輕易實現這一點;
- 若提前知道等遍歷的
collectioin
含有何種對象,則應修改塊簽名,指出對象的具體類型;
NSEnumerator
遍歷法
NSEnumerator
是個抽象基類,其中只定義了兩個方法,供子類來實現:
- (NSArray*)allObjects
- (id)nextObject
想遍歷數組時,則可以這樣來寫代碼:
NSArray *anArray = .....;
NSEnumerator *enumerator = [anArray objectEnumeator];
id object;
while((object = [enumerator nextObject])!= nil) {
//dosomething with object
}
遍歷字典時,也可以使用類似的代碼。并且NSEnumerator
有多種枚舉器供選擇,如反向遍歷等,使用時可以根據需要選擇不同的枚舉器;
快速遍歷
快速遍歷其實就是在基本for
循環的基礎上加了個 in
關鍵字:
for(id object in anArray){}
基于塊的遍歷方式
NSArray
中定義了下面的方法,實現最基本的遍歷功能:
-(void)enumeratorObjetsUsingBlock:(void(^)(id object,NSUInteger idx,BOOL *stop))block;
還有其他類似的遍歷方法,可以接受各種選項來控制遍歷操作
7.3 對自定義其內存管理語義的 collection
使用無縫橋接
- 通過無縫橋接技術,可以在
Foundation
框架中的Object-C
對象與CoreFoundation
框架中的 c 語言數據結構之間來回轉換; - 在
CoreFoundation
層面創建collection
時,可以指定許多回調函數,這些函數表示此collection
應如何處理其元素;
無縫橋接就是用來對Foundation
框架和CoreFoundation
框架中的等價的類進行轉換,
簡單的無縫橋接:
NSArray *anArray = ......;
CFArrayRef afarray = (__bridge CFArrayRef)anArray;
關鍵字__bridge
告訴 ARC 如何處理轉換所涉及的 OC 對象,而反向轉換(CoreFoundation 類 轉換為等價的 Foundation 類 )則使用關鍵字__bridge_transfer
來實現;
7.4 構建緩存時選用 NSCache 而非 NSDictionary
- NSCache 可以提供更優雅的自動刪減功能,而且是線程安全的;
- 可以給 NSCache 對象設置上限,用于限制緩存中的對象總個數及“總成本”;
- 將
NSPurgeableData
與NSCache
搭配使用,可實現自動清除數據功能;
7.5 精簡 initialize 與 load 的實現代碼
- 在加載階段,如果類實現了
load
方法,那么系統就用調用它。分類里也可以定義此方法,類的 load 方法要比分類中的先調用,與其他方法不同,load
方法不參與覆寫機制; - 首次使用某個類之前。系統會向其發送
initialize
消息。由于此方法遵從普通的覆寫規范,所以通常應該在里面判斷當前要初始化的是哪個類; -
initialize
和load
方法應實現得精簡一點,有助于保持應用程序的響應能力; - 無法在編譯期設定的全局變量,可以放在
initialize
方法城初始化;
7.6 別忘了 NSTimer 會保留其目標對象
-
NSTimer
對象會保留其目標,直到計時器本身失效為止,調用invalidate
方法可令計時器失效; - 反復執行任務的計時器,很容易導致保留環;
- 可以擴充
NSTimer
的功能,用“塊”來打破保留環,不過,除非NSTimer
將來在公共接口里提供此功能,否則必須創建分類,將相關實現代碼加入其中;