標籤:
由於最近入職,公司安排自由學習,於是有時間將Effective Objective-C 2.0一書學習了一遍。由於個人知識面較窄,對於書中有些內容無法理解透徹,現將所學所理解內容做一遍梳理,將個人認為常用且重要的知識記錄下來,以供日後參考。
1.在類的標頭檔中盡量少引入其他標頭檔
將標頭檔引入的時機盡量延後,在確有需要的時才引入(比如.m檔案中)。因為標頭檔中引入其他類標頭檔,會增加編譯時間(可能是現在運行硬體比較好,所所以對此點沒啥感覺)。在標頭檔中若要使用其他類,則用"向前聲明"-->@class + 類名。
2.多用類型常量,少用#define預先處理指令
因為用預先處理指令定義出來的常量不含類型資訊,編譯器只會進行尋找替換,即使有人重新定義了常量值,編譯器也不會產生警告。建議使用方法如下
//將#define#define ANIMATION_DURATION 0.3//用句代碼錶示static const NSTimeInterval kAnimationDuration = 0.3;
這樣不僅可以知道常量類型 還可以將資料局限於本類檔案中使用
3.用枚舉表示狀態、選項、狀態代碼
初級編寫代碼人員可能直接寫出枚舉,編寫其狀態
typedef enum PSPConnectionState{ PSPConnectionStateDisconnected = 1, PSPConnectionStateConnecting, PSPConnectionStateConnected,}PSPConnectionState;
初看直線好像並無不妥,但如果改變一下編寫枚舉的方式,寫成如下所示
//單選狀態枚舉typedef NS_ENUM(NSUInteger, PSPConnectionState){ PSPConnectionStateDisconnected = 1, PSPConnectionStateConnecting, PSPConnectionStateConnected,};//複選狀態枚舉typedef NS_OPTIONS(NSUInteger, PSPConnectionState){ PSPConnectionStateDisconnected = 1 << 0, PSPConnectionStateConnecting = 1 << 1, PSPConnectionStateConnected = 1 << 2,};
這樣用NS_ENUM和NS_OPTIONS宏來定義枚舉類型,並指明器底層資料類型,除了可以確保枚舉是開發人員所選的底層資料類型實現出來外,還能夠方便其他開發人員查看和使用。另外注意在處理枚舉類型時可以盡量使用switch語句,並且不要實現default分支,這樣的話,在新加如枚舉之後,編譯器就會提醒開發人員switch語句沒有處理所有的枚舉。
4.在對象內部盡量直接存取執行個體變數
直接存取執行個體變數由於不經過Objective-C的"方法派發"步驟,所以訪問速度會比較快,但由於直接存取執行個體變數不會觸發"索引值觀測"(KVO),因此建議讀資料時直接通過執行個體變數來讀,而寫入資料的時候,則通過屬性來寫(點文法)。當然對於惰性載入的屬性,需要通過屬性來讀取資料。
5.理解訊息轉寄機制
訊息轉寄分為兩大階段:第一階段先徵詢接受者所屬類,看其是否能動態添加方法,以處理當前這個“未知的選取器”,這個叫做“動態方法解析”。第二階段涉及“完整的訊息轉寄機制”。訊息整體轉寄流程通過來表示
這裡類比在動態方法解析中添加方法 下面為程式碼範例
=======.h檔案中=========@interface BQAutoDictionary : NSObject//隨意建立的屬性@property (nonatomic, copy) NSString *string;@property (nonatomic, strong) NSNumber *number;@property (nonatomic, strong) NSDate *date;@property (nonatomic, strong) id opaqueObject;@end=======.m檔案中=========@interface BQAutoDictionary()@property (nonatomic, strong) NSMutableDictionary *backingStore;@end@implementation BQAutoDictionary
//不動態產生get,set方法@dynamic string, number, date, opaqueObject;- (instancetype)init{ self = [super init]; if (self) { _backingStore = [NSMutableDictionary new]; } return self;}+ (BOOL)resolveInstanceMethod:(SEL)sel{ //獲得無法處理訊息名字 NSString *selectorString = NSStringFromSelector(sel); NSLog(@"%s",__func__); //動態添加set和get方法 if ([selectorString hasPrefix:@"set"]) { /** * 動態添加set方法 * @param self 類別 * @param sel 方法選取器 * @param IMP 需要增加的方法 */ class_addMethod(self, sel, (IMP)autodictionarySetter, "[email protected]:@"); }else{ class_addMethod(self, sel, (IMP)autodictionaryGetter, "@@:"); } return YES; //若不能處理訊息時需要返回下面方法,進行訊息轉寄 //return [super resolveInstanceMethod:sel];}id autodictionaryGetter(id self, SEL _cmd){ //得到執行個體中的字典 BQAutoDictionary *typedSelf = (BQAutoDictionary *)self; NSMutableDictionary *backingStore = typedSelf.backingStore; //根據選取器擷取名字 NSString *key = NSStringFromSelector(_cmd); NSLog(@"Getter Name = %@",key); //傳回值 return [backingStore objectForKey:key];}void autodictionarySetter(id self, SEL _cmd, id value){ BQAutoDictionary *typedSelf = (BQAutoDictionary *)self; NSMutableDictionary *backingStore = typedSelf.backingStore; NSString *selectorString = NSStringFromSelector(_cmd); NSMutableString *key = [selectorString mutableCopy]; NSLog(@"Setter Name = %@",key); //移除‘:’字元 [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]; }}@end
6.用首碼避免命名空間衝突
由於開發人員檔案整合的時候經常出現命名重複的問題,但由於Objective-C沒有命名空間機制。因此避免檔案重新命名的辦法就是:為所有的名稱都加上適當的首碼。Apple宣城其保留使用所有“兩字母首碼”的權利,所以我們選用的首碼應該是三個字母的!
7.盡量使用不可變對象
屬性是read-write,這樣出來的類都是可變的。一般情況下我們要建模的資料未必都需要改變。比如網路服務的資料請求後,我們只是將資料就行展示。當然若某屬性僅可於對象內部修改,則可在延展中將其屬性由readonly擴充為readwrite。樣本如下
=====.h檔案======@interface BQAutoDictionary : NSObject@property (nonatomic, readonly) NSString *name;@end=====.m檔案======@interface BQAutoDictionary ()@property (nonatomic, readwrite, copy) NSString *name;@end
8.在dealloc方法中只是放引用並解除監聽
對象在經曆器生命週期後,最終會被系統回收,這時就要執行dealloc方法,因為此時對象已處於回收狀態,因此不應再此方法內再做其他事情。只需要在裡面釋放指向其他對象的引用,並取消原來訂閱的“索引值觀測”(KVO)或NSNotificationCenter等通知!注意對象所擁有的其他非Objective-C對象需要在這裏手動釋放,如果是手動管理記憶體,那麼在最後還需要調用[super dealloc]
9.多用派發隊列,少用同步鎖
在以前的代碼編寫中,對於安全執行緒問題通常採用的做法是直接加線程鎖。加線程鎖的方法很好不過也有其缺陷,比如說,在極端情況下,同步塊回導致死結,另外其效率也不夠高。這裡就需要引入一個簡單而高效的辦法就是使用“串列同步隊列”,用法如下
_syncQueue = dispatch_queue_create("PSP", 0);- (NSString *)name{ __block NSString *localName; dispatch_sync(_syncQueue, ^{ localName = _name; }); return localName;}- (void)setName:(NSString *)name{ dispatch_sync(_syncQueue, ^{ _name = name; });}
當然還有一種更高效的方法用柵欄(barrier),柵欄塊必須單獨執行,不能與其它塊並行(只對並發隊列有意義)
//並行隊列_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//用同步- (NSString *)name{ __block NSString *localName; dispatch_sync(_syncQueue, ^{ localName = _name; }); return localName;}//利用非同步柵欄塊- (void)setName:(NSString *)name{ dispatch_barrier_async(_syncQueue, ^{ _name = name; });}
10.構建緩衝時選用NSCache而非NSDictionary
在進行網路請求是如何緩衝,大部分程式員可能是直接使用NSDictionary,其實NSCache類更好,它是Foundation架構專為處理這種任務而設計的NScache勝過NSDicitionary之處在於,當系統資源將要耗盡時,它可以自動刪除緩衝。如果採用普通的字典,那麼就需要自己編寫掛鈎(我也不懂啥意思)。此外NSCache還會先行刪除“最久未使用的”對象。另外還有個類叫做NSPurgeableData(NSMutableData子類),和NSCache搭配起來使用效果很好。具體使用方法可以自行百度!
以上便是個人覺得需要整理的知識,若其中有什麼錯誤之處,請大家指出,謝謝!
Effective Objective-C 2.0 學習記錄