Effective Objective-C 2.0 學習記錄

  因爲最近入職,公司安排自由學習,因而有時間將Effective Objective-C 2.0一書學習了一遍。因爲我的知識面較窄,對於書中有些內容沒法理解透徹,現將所學所理解內容作一遍梳理,將我的認爲經常使用且重要的知識記錄下來,以供往後參考。程序員

  1.在類的頭文件中儘可能少引入其餘頭文件緩存

  將頭文件引入的時機儘可能延後,在確有須要的時才引入(好比.m文件中)。由於頭文件中引入其餘類頭文件,會增長編譯時間(多是如今運行硬件比較好,因此對此點沒啥感受)。在頭文件中若要使用其餘類,則用"向前聲明"-->@class + 類名。安全

  2.多用類型常量,少用#define預處理指令網絡

  由於用預處理指令定義出來的常量不含類型信息,編譯器只會進行查找替換,即便有人從新定義了常量值,編譯器也不會產生警告。建議使用方法以下併發

//將#define
#define ANIMATION_DURATION 0.3
//用此句代碼表示
static const NSTimeInterval kAnimationDuration = 0.3;

這樣不只能夠知道常量類型 還能夠將數據侷限於本類文件中使用框架

  3.用枚舉表示狀態、選項、狀態碼異步

  初級編寫代碼人員可能直接寫出枚舉,編寫其狀態async

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語句沒有處理全部的枚舉。atom

  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, "v@:@"); }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搭配起來使用效果很好。具體使用方法能夠自行百度!

  以上即是我的以爲須要整理的知識,若其中有什麼錯誤之處,請你們指出,謝謝!

相關文章
相關標籤/搜索