iOS 讀 Effective Objective-C 2.0 52個有效方法
第一條:熟悉Objective-C
Objective-C為C語言添加了面向物件特性,是其超集。Objective-C使用動態繫結的訊息結構。
第二條:在類的標頭檔案中儘量少引用其他標頭檔案
一般可以使用 @class前置宣告。
第三條:多用字面量語法
其實就是 NSArray *array = @[@“1”]; 這樣的寫法。應該通過取下標操作來訪問陣列下標或字典中的鍵所對應的元素。
用字面量語法建立陣列或字典時,若值中有nil,則會丟擲異常。因此,務必確保值裡不含nil。
第四條:多用型別常量,少用#define預處理指令
static const Type name = value;包含型別資訊,清除的描述了常量的含義。命名規範:如果只是在該類的實現中使用,只需要以 k 開頭,如果是在類之外可見,通常使用 類名作為字首。如果這裡不使用 static 則編譯器會為這個變數新增一個外部引用的符號。如果想讓外部使用,則要先在標頭檔案中宣告為外部符號extern NSString *const classNameName;在實現檔案中去定義NSString *const classNameName = value。
第五條:用列舉表示狀態、選項、狀態碼
應該用列舉來表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字。
如果把傳遞給某個方法的選項表示為列舉型別,而多個選項又可同時使用,那麼就將各選項值定義為2的冪,以便通過按位與操作將其組合起來。
用NS_ENUM與NS_OPTIONS巨集來定義列舉型別,並指明其底層資料型別。這樣做可以確保列舉是用開發者所選的底層資料型別實現出來的,而不會採用編譯器所選的型別。(需要相護組合的使用NS_OPTIONS,不要相互組合的使用NS_ENUM)
在處理列舉型別的switch語句中不要實現default分支。這樣的話,加入新列舉之後,編譯器會提示開發者未處理新的列舉型別
第六條:理解“屬性”這一概念
可以用@property語法來定義物件中所封裝的資料。
通過“特質”來指定儲存資料所需的正確語義。
在設定屬性所對應的例項變數時,一定要遵從該屬性所宣告的語義。
開發iOS程式時應該使用nonatomic屬性,因為atomic屬性會嚴重影響效能。
第七條:在物件內部儘量直接訪問例項變數
讀資料用例項變數來讀,寫資料用屬性來寫。
初始化或者dealloc方法中應該直接使用例項變數來讀。
懶載入要用屬性來讀取。
第八條:理解“物件等同性”這一概念
若想監測物件的等同性,請提供“isEqual:”與hash方法。
相同的物件必須具有相同的雜湊碼,但是兩個雜湊碼相同的物件卻未必相同。
不要盲目地逐個監測每條屬性,而是應該依照具體需求來制定監測方案。
編寫hash方法時,應該使用計算速度快而且雜湊碼碰撞機率低的演算法。
第九條:以“類族模式”隱藏實現細節
類族模式可以把實現細節隱藏在一套簡單的公共介面後面。
系統框架中經常使用類族。
從類族的公共抽象基類中繼承子類時要當心,若有開發文件,則應首先閱讀。
第十條:在既有類中使用關聯物件存放自定義資料
// 使用objc_setAssociatedObject函式可以給某個物件關聯其他的物件。 void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) // 獲取關聯的物件 // 使用objc_getAssociatedObject函式可以通過鍵來取出某個物件的關聯物件。 id objc_getAssociatedObject(id object, const void *key) // 移除關聯的物件 // 使用objc_removeAssociatedObjects函式可以移除某個物件身上的所有關聯的物件。 void objc_removeAssociatedObjects(id object) OBJC_ASSOCIATION_ASSIGN 相當於@property的assign OBJC_ASSOCIATION_RETAIN_NONATOMIC相當於@property的nonatomic + retain OBJC_ASSOCIATION_COPY_NONATOMIC相當於@property的nonatomic + copy OBJC_ASSOCIATION_RETAIN相當於@property的retain OBJC_ASSOCIATION_COPY相當於@property的copy
第十一條:理解objc_msgSend的作用
訊息由接收者、選擇器、引數構成。
訊息由“動態訊息派發系統”來處理。
第十二條:理解訊息轉發機制
若物件無法響應某個選擇子,則進入訊息轉發流程。
通過執行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中。
物件可以把其無法解讀的某些選擇子轉交給其他物件來處理。
經過上述兩步之後,如果還是沒辦法處理選擇子,那就啟動完整的訊息轉發機制。
//找不到實現方法先走這裡,然後是forwardingTargetForSelector(備援接收者),最後是forwardInvocation(無法處理) + (BOOL)resolveInstanceMethod:(SEL)sel;
比如設定獲取方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selString = NSStringFromSelector(sel); if ([selString hasPrefix:@"set"]) { class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@"); } else { class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:"); } return YES; } id autoDictionaryGetter(id self, SEL _cmd){ AutoDictionary *typeSelf = (AutoDictionary *)self; NSMutableDictionary *backingStore = typeSelf.backingStore; NSString *key = NSStringFromSelector(_cmd); return [backingStore objectForKey:key]; } void autoDictionarySetter(id self, SEL _cmd, id value){ AutoDictionary *typeSelf = (AutoDictionary *)self; NSMutableDictionary *backingStore = typeSelf.backingStore; NSString *selString = NSStringFromSelector(_cmd); NSMutableString *key = [selString mutableCopy]; //刪除: [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)]; //刪除set [key deleteCharactersInRange:NSMakeRange(0, 3)]; //大寫轉小寫 NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString]; [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar]; if (value) { [backingStore setObject:value forKey:key]; } else { [backingStore removeObjectForKey:key]; } }
第十三條:用“方法調配技術”除錯“黑盒方法”
在執行期,可以向類中新增或替換選擇子所對應的方法實現。
使用另一份實習來替換原有的方法實現,這道工序叫做“方法調配”,開發者常用此技術向原有實現中新增新功能。
一般來說,只有除錯程式的時候才需要在執行期修改方法實現,這種做法不宜濫用。
//通常就是在某些方法中替換個方法之類的,比如在load中替換 method_exchangeImp....
第十四條:理解“類物件”的用意
每個例項都有一個指向Class物件的指標,用以表明其型別,而這些Class物件則構成了類的繼承體系。
如果物件型別無法在編譯器確定,那麼就應該使用型別資訊查詢方法來探知。
儘量使用型別資訊查詢方法來確定物件型別,而不要直接比較類物件,因為某些物件可能實現了訊息轉發功能。
isa super_class isKindOfClass isMemberOfClass
第十五條:用字首避免名稱空間衝突
很簡單,就是字首。
第十六條:提供“全能初始化方法”
在類中提供一個全能初始化方法,並於文件裡指明。其他初始化方法均應呼叫次方法。
若全能初始化方法與超類不同,則需覆寫超類中的對應方法。
如果超類的初始化方法不適用於自類,那麼應該覆寫這個超類方法,並在其中丟擲異常。
第十七條:實現description方法
實現description方法返回一個有意義的字串,用以描述該例項。
若想在除錯時打印出更詳盡的物件描述資訊,則應實行debugDescription方法。
第十八條:儘量使用不可變物件
對外標頭檔案宣告
@property (nonatomic, copy, readonly) NSString *readOnlyString;
內部可改為
@property (nonatomic, copy, readwrite) NSString *readOnlyString;
第十九條:使用清晰而協調的命名方式
駝峰,有意義
第二十條:為私有方法名加字首
只有內部使用的方法加個字首,比如 p_createUI(){}
第二十一條:理解OC錯誤模型
只有發生了可使整個應用程式崩潰的嚴重錯誤時,才應使用異常,異常是會讓程式崩潰的。
在錯誤不那麼嚴重的情況下,可以指派“委託辦法”(delegate method)來處理錯誤,也可以把錯誤資訊放在NSError物件裡,經由“輸出引數”返回給呼叫者。
第二十二條:理解NSCopying協議
若想令自己所寫的物件具有拷貝功能,則需要實行NSCopying協議。
如果自定義的物件分為可變版本與不可變版本,那麼就要同時實現NSCopying與NSMutableCopying協議。
複製物件時需決定採用淺拷貝還是深拷貝,一般情況下應該儘量執行淺拷貝。
如果你所寫的物件需要深拷貝,那麼可考慮新增一個專門執行深拷貝的方法。
<NSCopying> - (id)copyWithZone:(NSZone *)zone{ AutoDictionary *copy = [[[self class] allocWithZone:zone] initWithString:_readOnlyString]; copy->_friends = [_friends mutableCopy]; return copy; } <NSMutableCopying> - (id)mutableCopyWithZone:(NSZone *)zone{ }
第二十三條:通過委託與資料來源協議進行物件間通訊
代理,資料來源
快取狀態
@protocol AutoDictionaryDelegate <NSObject> @optional - (void)testA; - (void)testB; - (void)testC; - (void)testD; @end @interface AutoDictionary : NSObject{ struct { unsigned int delegateFlagA : 1; //只表示0和1兩個值,類似於BOOL了 unsigned int delegateFlagB : 1; //只表示0和1兩個值,類似於BOOL了 unsigned int delegateFlagC : 1; //只表示0和1兩個值,類似於BOOL了 unsigned int delegateFlagD : 1; //只表示0和1兩個值,類似於BOOL了 } __delegateFlags; } @property (nonatomic, weak) id<AutoDictionaryDelegate> delegate; @end - (void)setDelegate:(id<AutoDictionaryDelegate>)delegate{ _delegate = delegate; //快取是否實現了代理,後面就不用判斷了 __delegateFlags.delegateFlagA = [delegate respondsToSelector:@selector(testA)]; __delegateFlags.delegateFlagB = [delegate respondsToSelector:@selector(testB)]; __delegateFlags.delegateFlagC = [delegate respondsToSelector:@selector(testC)]; __delegateFlags.delegateFlagD = [delegate respondsToSelector:@selector(testD)]; }
第二十四條:將類的實現程式碼分散到便於管理的數個分類之中
使用粉類機制把類的實現程式碼劃分成易於管理的小塊。
將應該視為“私有”的方法歸入名叫Private的分類中,以隱藏實現細節。
第二十五條:總是為第三方類的分類名稱加字首
向第三方類中新增分類時,總應給其名稱加上你專用的字首。
向第三方類中新增分類時,總應給其中的方法名加上你專用的字首。
第二十六條:勿在分類中宣告屬性
把封裝資料所用的全部屬性都定義在主接口裡。
在“class-continuation分類”之外的其它分類中,可以定義存取方法,但儘量不要定義屬性。
第二十七條:使用“class-continuation分類”隱藏實現細節
通過“class-continuation分類”向類中新增例項變數。
如果某屬性在主介面中宣告為“只讀”,而類的內部又要用設定方法修改此屬性,那麼就在“class-continuation分類”中將其擴充套件為“可讀寫”。
把私有方法的原型宣告在“class-continuation分類”裡面。
若想使類所遵循的協議不為人所知,則可於“class-continuation分類”中宣告。
說這麼多其實就是.m檔案中的同名 @interface
第二十八條:通過協議提供匿名物件
協議可在某種程度上提供匿名型別。具體的物件型別可以淡化成遵從某協議的id型別,協議裡規定了物件所應實現的方法。
使用匿名物件來隱藏型別名稱(或類名)。
如果具體型別不重要,重要的是物件能夠響應(定義在協議裡的)特定方法,那麼可使用匿名物件來表示。
第二十九條:理解引用計數
引用計數機制通過可以遞增遞減的計數器來管理記憶體。物件建立好之後,其保留計數至少為1。若保留計數為正,則物件繼續存活。當保留計數降為0時,物件就被銷燬了。
在物件生命期中,其餘物件通過引用來保留或釋放此物件。保留與釋放操作分別會遞增及遞減保留計數。
第三十條:以ARC簡化引用計數
有ARC之後,程式設計師就無須擔心記憶體管理問題了。使用ARC來程式設計,可省去類中的許多“樣板程式碼”。
ARC管理物件生命期的辦法基本上就是:在合適的地方插入“保留”及“釋放”操作。在ARC環境下,變數的記憶體管理語義可以通過修飾符指明,而原來則需要手工執行“保留”及“釋放”操作。
由方法所返回的物件,其記憶體管理語義總是通過方法名來體現。ARC將此確定為開發者必須遵守的規則。
ARC只負責管理Objective-C物件的記憶體。尤其要注意:CoreFoundation物件不歸ARC管理,開發者必須適時呼叫CFRetain/CFRelease。
第三十一條:在dealloc方法中只釋放引用並解除監聽
在dealloc方法裡,應該做的事情就是釋放指向其它物件的引用,並取消原來訂閱的“鍵值觀測”(KVO)或NSNotificationCenter等通知,不要做其它事情。
如果物件持有檔案描述符等系統資源,那麼應該專門編寫一個方法來釋放此種資源。這樣的類要和其使用者約定:用完資源後必須呼叫close方法。
執行非同步任務的方法不應在dealloc裡呼叫;只能在正常狀態下執行的那些方法也不應在dealloc裡呼叫,因為此時物件已處於正在回收的狀態了。
第三十二條:編寫“異常安全程式碼”時留意記憶體管理問題
捕獲異常時,一定要注意將try塊哪所創立的物件清理乾淨。
在預設情況下,ARC不生成安全處理異常所需的清理程式碼。開啟編譯器標誌後,可生成這種程式碼,不過會導致應用程式變大,而且會降低執行效率。
第三十三條:以弱引用避免保留環
將某些引用設為weak,可避免出現“保留環”。
weak引用可以自動清空,也可以不自動清空。自動清空是隨著ARC而引入的新特性,由執行期系統來實現。在具備自動清空功能的弱引用上,可以隨意讀取其資料,因為這種引用不會指向已經回收過的物件。
第三十四條:以“自動釋放池塊”降低記憶體峰值
自動釋放池排布在棧中,物件收到autorelease訊息後,系統將其放入最頂端的池裡。
合理運用自動釋放池,可降低應用程式的記憶體峰值。
@autoreleasepool這種新式寫法能創建出更為輕便的自動釋放池。
第三十五條:用“殭屍物件”除錯記憶體管理問題
系統在回收物件時,可以不將其真的回收,而是把它轉化為殭屍物件。通過環境變數NSZombieEnabled可開啟此功能。
系統會修改物件的isa指標,令其指向特殊的殭屍類,從而使該物件變為殭屍物件。殭屍類能夠響應所有的選擇子,響應方式為:列印一條包含訊息內容及其接收者的訊息,然後終止應用程式。
其實就是開啟殭屍模式檢查記憶體問題。
第三十六條:不要使用 retainCount
物件的保留計數看似有用,實則不然,因為任何給定時間點上的“絕對保留計數”都無法反映物件生命期的全貌。
引入ARC之後,retainCount方法就正式廢止了,在ARC下呼叫該方法會導致編譯器報錯。
第三十七條:理解“塊”這一概念
塊是C、C++、Objective-C中的詞法閉包。
塊可接受引數,也可返回值。
塊可以分配在棧或堆上,也可以是全域性的。分配在棧上的塊可拷貝到堆裡,這樣的話,就和標準的Objective-C物件一樣,具備引用計數了。
就是block。
第三十八條:為常用的塊型別建立 typedef
以typedef重新定義塊型別,可令塊變數用起來更加簡單。
定義新型別時應遵從現有的命名習慣,勿使其名稱與別的型別相沖突。
不妨為同一個塊簽名定義多個類型別名。如果要重構的程式碼使用了塊型別的某個別名,那麼只需修改相應typedef中的塊簽名即可,無須改動其他typedef。
typedef void(^BtnClickBlock)(NSInteger index); @interface UIAlertView (Block) - (void)showWithBlock:(BtnClickBlock)block; @end
第三十九條:用 handler 塊降低程式碼分散程度
在建立物件時,可以使用內聯handler塊將相關業務邏輯一併宣告。
在有多個例項需要監控時,如果採用委託模式,那麼經常需要根據傳入的物件來切換,而若該用handler塊來實現,則可直接講塊與相關物件放在一起。
設計API時如果用到了handler塊,那麼可以增加一個引數,使呼叫者通過此引數來決定應該把塊安排在哪個佇列上執行。
與代理的對比:
程式碼更整潔,塊宣告在建立獲取器的範圍內 可以訪問此範圍你的所有變數。
代理中如果類要分別使用多個獲取器下載不同資料,那就需要在代理回撥方法里根據傳入的獲取器引數來切換,想一下多個輸入框的代理判斷。
第四十條:用塊引用其所屬物件時不要出現保留環
如果塊所捕獲的物件直接或間接地保留了塊本身,那麼就得當心保留環問題。
一定要找個適當的時機解除保留環,而不能把責任推給API的呼叫者。
第四十一條:多用派發佇列,少用同步鎖
派發佇列可用來表述同步語義,這種做法要比使用@synchronized塊或NSLock物件更簡單。
將同步與非同步派發結合起來,可以實現與普通加鎖機制一樣的同步行為,而這麼做卻不會阻塞執行非同步派發的執行緒。
使用同步佇列及柵欄塊,可以令同步行為更加高效。
_syncQueue = dispatch_queue_create("com.duicode.xxxx", NULL); //序列佇列 _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //併發佇列 dispatch_sync(_syncQueue, ^{ }); //寫入操作用 柵欄塊 dispatch_barrier_async(_syncQueue, ^{ });
第四十二條:多用GCD,少用 performSelector 系列方法
performSelector系列方法在記憶體管理方法容易有疏失。它無法確定將要執行的選擇子具體是什麼,因而ARC編譯器也就無法插入適當的記憶體管理方法。
performSelector系列方法所能處理的選擇子太過侷限了,選擇子的返回值型別及傳送給方法的引數個數都受到限制。
如果想把任務放在另一個執行緒上執行,那麼最好不要用performSelector系列方法,而是把任務封裝到塊裡,然後呼叫大中樞派發機制的相關方法來實現。
[self performSelector:@selector(blockTest) withObject:nil afterDelay:5.0]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
第四十三條:掌握GCD及操作佇列的使用時機
在解決多執行緒與任務管理問題時,派發佇列並非唯一方案。
操作佇列提供了一套高層的Objective-C API,能實現純GCD所具備的絕大部份功能,而且還能完成一些更為複雜的操作,那些操作若改用GCD來實現,則需另外編寫程式碼。
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperation *operation1 = [[NSOperation alloc] init]; operation1.completionBlock = ^{ NSLog(@"1111111111"); }; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2222222222"); }]; [queue addOperation:operation1]; [queue addOperation:operation2]; [operation1 addDependency:operation2]; //佇列操作依賴
第四十四條:通過Dispatch Group機制,根據系統資源狀況來執行任務
一系列任務可歸入一個dispatch group 之中。開發者可以在這組任務執行完畢時獲得通知。
通過dispatch group,可以在併發式派發佇列裡同時執行多項任務。此時GCD會根據系統資源狀況來排程這些併發執行的任務。開發者若自己來實現此功能,則需編寫大量程式碼。
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); dispatch_group_enter(group); //模擬多執行緒耗時操作 dispatch_group_async(group, globalQueue, ^{ sleep(3); NSLog(@"%@---block1結束。。。",[NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_enter(group); //模擬多執行緒耗時操作 dispatch_group_async(group, globalQueue, ^{ sleep(3); NSLog(@"%@---block2結束。。。",[NSThread currentThread]); dispatch_group_leave(group); }); NSLog(@"%@---2結束。。。",[NSThread currentThread]); dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@---全部結束。。。",[NSThread currentThread]); });
第四十五條:使用dispatch_once來執行只需執行一次的執行緒安全程式碼
經常需要編寫“只需執行一次的執行緒安全程式碼”。通過GCD所提供的dispatch_once函式,很容易就能實現此功能。
標記應該宣告在static或global作用域中,這樣的話,在把只需執行一次的塊傳給dispatch_once函式時,傳進去的標記也是相同的。
比如單例類
+ (id)shareInstance{ static AutoTestClass *instance = nil; static dispatch_once_t predicate; dispatch_once(&predicate, ^{ instance = [[[self class] alloc] init]; }); return instance; }
第四十六條:不要使用dispatch_get_current_queue
dispatch_get_current_queue函式的行為常常與開發者所預期的不同。此函式已經廢棄,只應做除錯之用。
由於派發佇列是按層級來組織的,所以無法單用某個佇列物件來描述“當前佇列”這一概念。
dispatch_get_current_queue函式用於解決由不可重入的程式碼所引發的死鎖,然而能用此函式解決的問題,通常也能改用“佇列特定資料”來解決。
第四十七條:熟悉系統框架
許多系統框架都可以直接使用。其中最重要的是Foundation與CoreFoundation,這兩個框架提供了構建應用程式所需的許多核心功能。
很多常見任務都能用框架來做,例如音訊與視訊處理、網路通訊、資料管理等。
請記住:用純C寫成的框架與用Objective-C寫成的一樣重要,若想成為優秀的Objective-C開發者,應該掌握C語言的核心概念。
第四十八條:多用塊列舉,少用for迴圈
遍歷collection由四種方式。最基本的辦法是for迴圈,其次是NSEnumerator遍歷法及快速遍歷法,最新、最先進的方式則是“塊列舉法”。
“塊列舉法”本身就能通過GCD來併發執行遍歷操作,無須另行編寫程式碼。而採用其他遍歷方式則無法輕易實現這一點。
若提前知道待遍歷的collection含有何種物件,則應修改塊簽名,指出物件的具體型別。
NSArray *array = @[@"1",@"2",@"3",@"4",@"5",@"6"]; NSEnumerator *enumerator = [array objectEnumerator]; //1.0的方法 id obj; while ((obj = [enumerator nextObject]) != nil) { NSLog(@"----%@",obj); } //快速遍歷 for(id obj in [array reverseObjectEnumerator]){ NSLog(@"----%@",obj); } //基於塊的遍歷 [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"----%@",obj); }];
第四十九條:對自定義其記憶體管理語義的collection使用無縫橋接
通過無縫橋接技術,可以在Foundation框架中的Objective-C物件與CoreFoundation框架中的C語言資料結構之間來回轉換。
在CoreFoundation層面建立collection時,可以指定許多回調函式,這些函式表示此collection應如何處理其元素。然後,可運用無縫橋接技術,將其轉換成具備特殊記憶體管理語義的Objective-C collection。
看到這個 腦殼疼,改天再刷
第五十條:構建快取是選用NSCachae而非NSDictionary
實現快取時應選用NSCache而非NSDictionary物件。因為NSCache可以提供優雅的自動刪減功能,而且是“執行緒安全的”,此外,它與字典不同,並不會拷貝鍵。
可以給NSCache物件設定上限,用以限制快取中的物件總個數及“總成本”,而這些尺度則定義了快取刪減其中物件的時機。但是絕對不要把這些尺度當成可靠的“硬限制”,它們僅對NSCache起指導作用。
將NSPurgeableData與NSCache搭配使用,可實現自動清除資料的功能,也就是說,當NSPurgeableData物件所佔記憶體為系統所丟棄時,該物件自身也會從快取中移除。
如果快取使用得當,那麼應用程式的響應速度就能提高。只有那種“重新計算起來很費事的”資料,才值得放入快取,比如那些需要從網路獲取或從磁碟讀取的資料。
第五十一條:精簡initialize與load的實現程式碼
在載入階段,如果實現了load方法,那麼系統就會呼叫它。分類裡也可以定義此方法,類的load方法要比分類中的先呼叫。與其他方法不同,load方法不參與覆寫機制。
首次使用某個類之前,系統會向其傳送initialize訊息。由於此方法遵從普通的覆寫規則,所以通常應該在裡面判斷當前要初始化的是哪個類。
load與initialize方法都應該實現得精簡一些,這有助於保持應用程式的響應能力,也能減少引入“依賴環”的機率。
無法在編譯器設定的全域性常量,可以放在initialize方法裡初始化。
第五十二條:別忘了NSTimer會保留其目標物件
NSTimer物件會保留其目標,直到計時器本身失效為止,呼叫invalidate方法可令計時器失效,另外,一次性的計時器在觸發完任務之後也會失效。
反覆執行任務的計時器,很容易引入保留環,如果這種計時器的目標物件又保留了計時器本身,那肯定會導致保留環。這種環狀保留關係,可能是直接發生的,也可能是通過物件圖裡的其他物件間接發生的。
可以擴充NSTimer的功能,用“塊”來打破保留環。不過,除非NSTimer將來在公共接口裡提供此功能,否則必須建立分類,將相關實現程式碼加入其中。