(iOS) Effective Objective-C 2.0 讀書筆記 (二)

第三章

15. 用前綴避免命名空間沖突

  • 選擇與你的公司、應用程序或二者皆有關聯之名稱作為類名的前綴,并在所有代碼中均使用這一前綴
  • 若自己所開發的程序庫中用到了第三方庫,則應為其中的名稱加上前綴

16. 提供“全能初始化方法”

  • 在類中提供一個全能初始化方法,并于文檔里指明。其他初始化方法均應調用此方法
  • 若全能初始化方法與超類不同,則需覆寫超類中的對應方法
  • 如果超類的初始化方法不適用于子類,那么應該覆寫這個超類方法,并在其中拋出異常

17. 實現description方法

  • 實現description方法返回一個有意義的字符串,用以描述該實例
  • 若想在調試時打印更詳盡的對象描述信息,則應實現debugDescription方法

18. 盡量使用不可變對象

  • 盡量創建不可變對象
  • 若某屬性僅可于對象內部修改,則在“class_continuation分類”中將其由readonly屬性拓展為readwrite屬性
  • 不要把可變的collection作為屬性公開,而應提供相關方法,以此修改對象中的可變collection。(eg,外部申明array,內部使用mutableArray,返回array里面用mutableArray copy來返回,通過提供add delete方法對array增減,實際則是操作mutableArray)

19. 使用清晰而協調的命名方式

  • 起名時應遵從標準的Objective-C命名規范,這樣創建出來的接口更容易為開發者所理解
  • 方法名要言簡意賅,從左至右讀起來要像個日常用語中的句子才好
  • 方法名里不要使用縮略后的類型名稱
  • 給方法起名時的第一要務就是確保其風格與你自己的代碼或索要集成的框架相符

20. 為私有方法名加前綴

  • 給私有方法的名稱加上前綴,這樣可以很容易地將其同公共方法區分開(書里建議使用p_來開頭)
  • 不要單用一個下劃線做私有方法的前綴,因為這種做法是預留給蘋果公司用的

21. 理解0bjective-C錯誤模型

  • 只有發生了可使整個應用程序崩潰的嚴重錯誤時,才應使用異常
    *在錯誤不那么嚴重的情況下,可以指派“委托方法”(delegate method)來處理錯誤,也可以把錯誤信息放在NSError對象里,經由“輸出參數”返回給調用者

22. 理解NSCopying協議

  • 若想令自己所寫的對象具有拷貝功能,則需實現NSCopying協議
  • 如果自定義的對象分為可變版本與不可變版本,那么就要同時實現NSCopying與NSMutableCopying協議
  • 復制對象時所需決定采用淺拷貝還是深拷貝,一般情況下應該盡量執行淺拷貝
  • 如果你寫的對象需要深拷貝,那么可考慮新增一個專門執行深拷貝的方法

第四章 協議與分類

23. 通過委托與數據源協議進行對象間通訊

  • 委托模式為對象提供了一套接口,使其可由此將相關事件告知其他對象
  • 將委托對象應該支持的接口定義成協議,在協議中把可能需要處理的事件定義成方法
  • 當某對象需要從另外一個對象中獲取數據時候,可以使用委托模式。這種情境下,該模式亦稱為“數據源協議"
  • 若有必要,可實現含有位段的結構體,將委托對象是否能響應相關協議這一信息緩存至其中

24. 將類的實現代碼分散到便于管理的數個分類之中

  • 使用分類機制把類的實現代碼劃分成易于管理的小塊
  • 將應該視為“私有”的方法歸入名為Private的分類中,以隱藏實現細節

25. 總是向第三方的分類名稱加前綴

  • 向第三方類中添加分類時,總應給其名稱加上你專用的前綴
  • 向第三方類中添加分類時,總應給其中的方法名加上你專用的前綴

26. 勿在分類中聲明屬性

  • 把封裝數據所用的全部屬性都定義在主接口里
  • 在“class-continuation分類"之外的其他分類中,可以定義存取方法,但盡量不要定義屬性

27. 使用”class-continuation分類“隱藏實現細節

  • 通過”class-continuation分類"向類中新增實例變量(其實就是使用延展)
  • 如果某屬性在主接口中聲明為“只讀",而類的內部又要用設置方法修改此屬性,那么就在"class-continuation分類"中將其拓展為"可讀寫"
  • 若私有方法的原型聲明在"class-continuation分類”里面
  • 若想使類所遵循的協議不為人所知,則可于“class-continuation分類"中聲明

28. 通過協議提供匿名對象

  • 協議可在某種程度上提供匿名類型,具體的對象類型可以淡化成遵從某協議的id類型,協議里規定了對象所應實現的方法
  • 使用匿名對象來隱藏類型名稱
  • 如果具體類型不重要,重要的是對象能夠響應(定義在協議里的)特定方法,那么可以使用匿名對象來表示

第五章 內存管理

29. 理解引用計數

  • 引用計數機制通過可以遞增遞減的計數器來管理內存。對象創建好之后,保留其計數至少為1.若保留計數為正,則對象繼續存活。當保留計數降為0時,對象就被銷毀了
  • 在對象生命期中,其余對象通過引用來保留或釋放此對象。保留與釋放操作分別會遞增及遞減保留計數。

30. 以ARC簡化引用計數

  • 有ARC之后,程序員就無須擔心內存管理問題了。使用ARC來編程,可省去類中的許多“樣板代碼”
  • ARC管理對象生命期的辦法基本上就是:在合適的地方插入“保留”及“釋放”操作。在ARC環境下,變量的內存管理語義可以通過修飾符指明,而原來則需要手工執行“保留”及“釋放操作
  • 由方法所返回的對象,其內存管理語義總是通過方法名來體現。ARC將此確定為開發者必須遵守的規則
  • ARC只負責管理Objective-C對象的內存。尤其要注意:CoreFoundation對象不歸ARC管理,開發者必須適時調用CFRetain/CFRelease

31. 在dealloc方法中只釋放引用并解除監聽

  • 在dealloc方法里,應該做的事情就是釋放指向其他對象的引用,并取消原來訂閱的”鍵值觀測“(KVO)或NSNotificationCenter等通知,不要做其他事情
  • 如果對象持有文件描述符等系統資源,那么應該專門編寫一個方法來釋放此種資源
  • 執行異步任務的方法不應再dealloc里調用;只能在正常狀態下執行的那些方法也不應再dealloc里調用,因為此時對象已處于正在回收的狀態了

32. 編寫“異常安全代碼”時留意內存管理問題

  • 捕獲異常時,一定要注意將try塊內所創立的對象清理干凈
  • 在默認情況下,ARC不生成安全處理異常所需的清理代碼。開啟編譯器標識后,可以生成這種代碼,不過會導致應用程序變大,而且會降低運行效率

33. 以弱引用避免保留環

  • 將某些引用設為weak,可避免出現“保留環”
  • weak引用可以自動清空,也可以不自動清空。自動清空是隨著ARC而引入的新特性,有運行期系統來實現。在具備自動清空功能的弱引用上,可以隨意讀取其數據,因為這種引用不會指向已經回收過的對象

34. 以“自動釋放池塊”降低內存峰值

  • 自動釋放池排布的棧中,對象收到autorelease消息后,系統將其放入最頂端的池里
  • 合理運用自動釋放池,可降低應用程序的內存峰值
  • @autoreleasepool這種新式寫法能創建出更為輕便的自動釋放池

35. 用“僵尸對象”調試內存管理問題

  • 系統在回收對象時,可以不將其真的回收,而是把它轉化為僵尸對象。通過環境變量NSZombieEnabled可開啟此功能
  • 系統會修改對象的isa指針,令其指向特殊的僵尸類,從而使該對象變為僵尸對象。僵尸類能夠響應所有的選擇器,響應方式為:打印一條包含消息內容及其接收者的消息,然后終止應用程序

36. 不要使用retainCount

  • 對象的保留計數看似有用,實則不然,因為任何給定時間點上的“絕對保留計數”都無法反映對象生命期的全貌
  • 引入ARC之后,retainCount方法就正式廢止了,在ARC下調用該方法會導致編譯器報錯

第六章 塊與大中樞派發

37. 理解“塊”這一概念

block會把它鎖捕獲的所有變量都拷貝一份,但是,拷貝的并不是對象本身,而是指向這些對象的指針變量。

定義block的時候,其所占的內存區域是分配在棧中的。這就是說,塊只在定義它的那個范圍內有效。例如,下面這段代碼就有危險:


代碼示例

定義在if及else語句中的兩個塊都分配在棧內存中。編譯器會給每個塊分配好棧內存,然而等離開了相應的范圍之后,編譯器有可能把分配給塊的內存覆寫掉。于是,這兩個塊只能保證在對應的if或else語句范圍內有效。這樣寫出來的代碼可以編譯,但是運行起來時而正確,時而錯誤。若編譯器未覆寫待執行的塊,則程序照常運行,若覆寫,則程序崩潰。
為解決此問題,可給塊對象發送copy消息以拷貝之。這樣的話,就可以把塊從棧復制到堆了。這樣它就變成“堆塊"??截惡蟮膲K,就可以在定義它的范圍之外使用。而且一旦復制,它就變成帶引用計數的對象了。

  • block是C、C++、Objective-C中的語法閉包
  • block可接受參數,也可返回值
  • block可以分配在?;蚨焉?,也可以是全局的。分配在棧上的塊可拷貝到堆里,這樣的話,就和標準的Objective-C對象一樣,具備引用計數了

38. 為常用的塊類型創建typedef

  • 以typedef重新定義塊類型,可令塊變量用起來更簡單
  • 定義新類型時應遵從現有的命名習慣,勿使用其名稱與別的類型相沖突
  • 不妨為同一個block簽名定義多個類型的別名。如果要重構代碼使用了塊類型的某個別名,那么只需修改相應typedef中的塊簽名即可,無須改動其他typedef

39. 用handler塊降低代碼分散程度

  • 在創建對象時,可以使用內聯的handler塊將相關業務邏輯一并聲明
  • 在有多個實例需要監控時,如果采用委托模式,那么精彩需要更具傳入的對象來切換,若改用handler塊來實現,則可直接將塊與相關對象放在一起
  • 設計API時如果用到了handler塊,那么可以增加一個參數,使調用者可通過此參數來決定應該把塊安排在哪個隊列上執行

40. 用塊引用其所屬對象時不要出現保留環

  • 如果塊所捕獲的對象直接或間接地保留了塊本身,那么就得當心保留環問題
  • 一定要找個適當的時機解除保留環,而不能把責任推給API的調用者

41. 多用派發隊列,少用同步鎖

  • 派發隊列可用來表述同步語義,這種做法要比使用@synchronized塊或NSLock對象更簡單
  • 將同步與異步派發結合起來,可以實現與普通加鎖機制一樣的同步行為,而這么做卻不會阻塞執行異步派發的線程
  • 使用同步隊列及柵欄塊,可以令同步行為更加高效

42. 多用GCD,少用performSelector系列方法

  • performSelector系列方法在內存管理方面容易有疏失,它無法確定將要執行的選取器具體是什么,因為ARC編譯器也就無法插入適當的內存管理方法
  • performSelector系列方法所能處理的選取器泰國局限,選取器的返回值類型及發送給方法的參數個數都收到限制
  • 如果想把任務放在一個線程上執行,那么最好不要用performSelector系列方法,而是應該把任務封裝到block里,然后調用GCD相關方法來實現

43. 掌握GCD及操作隊列的使用時機

  • 在解決多線程與任務管理問題時,GCD并非唯一方案
  • 操作隊列提供了一套高層的Objective-C API,能實現純GCD所具備的絕大部分功能,而且還能完成一些更為復雜的操作,那些操作若改用GCD來實現,則需另外編寫代碼(NSOperation可以取消,可以指定操作間的依賴關系,可以通過鍵值觀察其屬性,可以非常方便的指定優先級)

44. 通過Dispatch Group機制,根據系統資源來執行狀況

  • 一系列任務可歸入一個dispatch group之中,開發者可以在這組任務執行完畢時獲得通知
  • 通過dispatch group,可以在并發式派發隊列里同事執行多項任務。此時GCD會根據系統資源狀況來調度這些并發執行的任務。開發者若自己來實現此功能,則需編寫大量代碼

45. 使用dispatch_once來執行只需運行一次的線程安全代碼

  • 經常需要編寫“只需要執行一次的線程安全代碼”。通過GCD所提供的dispatch_once函數,很容易就能實現此功能
  • 標記應該聲明在static或global的作用域中,這樣的話,在把只需執行一次的block傳給dispatch_once函數時,傳進去的標記也是相同的

46. 不要使用dispatch_get_current_queue

  • dispatch_get_current_queue函數的行為常常與開發者所預期的不同,此函數已經廢棄,只應做調試之用
  • 由于派發隊列是按層級來組織的,所以無法單用某個隊列對象來描述“當前隊列”這一概念
  • dispatch_get_current_queue函數用于解決由不可重入的代碼所引發的死鎖,然而能用此函數解決的問題,通常也能改用“隊列特性數據”來解決

第七章 系統框架

47. 新呼吸系統框架

將一系列代碼封裝為動態庫,并在其中放入描述其接口的頭文件,這樣做出來的東西就叫框架

  • 許多系統框架都可以直接使用。其中最重要的是Foundation與CoreFoundation
  • 許多常見任務都能用框架來做,例如音頻視頻處理、網絡通信、數據管理等
  • 純C寫成的框架與用Objective-C寫成的一樣重要,若想成為優秀的OC開發者,應該掌握C語言的核心概念

48. 多余塊枚舉,少用for循環

如果想讓自定義類實現for...in的取值方式,可以實現NSFastEnumeration協議

  • 遍歷collection有四種方式。最基本的辦法就是for循環,其次是NSEnumerator遍歷法及快速遍歷法,最新、最先進的就是“塊枚舉法”
  • “塊枚舉法”本身就能通過GCD來并發執行遍歷操作,無須另行編寫代碼。而采用其它遍歷方式無法輕易實現
  • 若提前知道待遍歷的collection含有何種對象,則應修改塊簽名,指出對象的具體類型

49. 對自定義其內存管理語義的collection使用使用無縫橋接

  • 通過無縫橋接技術,可以在Foundation框架中的Objective-C對象與CoreFoundation框架中的C語言數據結構之間來回轉換
  • 在CoreFoundation層面創建collection時,可以指定許多回調函數,這些函數表示此collection應如何處理其元素。然后,可運用無縫橋接技術,將其轉換成具備特殊內存管理語義的Objective-C collection

50. 構建緩存時選用NSCache而非NSDictionary

  • 實現緩存時應選用NSCache而非NSDictionary對象,因為NSCache可以提供優雅的自動刪減功能,而且是“線程安全的”,此外,它與字典不同,并不會拷貝鍵
  • 可以給NSCache對象設置上限,用以限制緩存中的對象總個數及“總成本”,而這些尺度則定義了緩存刪減其中對象的時機。但是絕對不要把這些尺度當成可靠的“硬限制”,它們僅對NSCache起指導作用
  • 將NSPurgeableData與NSCache搭配使用,可實現自動清除數據的功能,也就是說,當NSPurgesableData,對象所占用的內存為系統所丟棄時,改對象自身也會從緩存中移除
  • 如果緩存得當,那么應用程序的響應速度就能提高。只有那種“重新計算起來很費事的“數據,才值得放入緩存,比如那些需要從網絡獲取或從磁盤讀取的數據

51. 精簡initialize與load的實現代碼

  • 在加載階段,如果類實現了load方法,那么系統就會調用它。分類里也可以定義此方法,類的load方法要比分類中先調用。與其他方法不同,load方法不參與覆寫機制
  • 首次使用某個類之前,系統會向其發送initialize消息。由于此方法遵從普通的覆寫規則,所以通常應該在里面判斷當前要初始化的是哪個類
  • 無法再編譯器設定的全局常量,可以放在initiallize方法里初始化

52. 別忘了NSTimer會保留其目標對象

  • NSTimer對象會保留其目標,直到計時器本身失效位置,調用invalidate方法可令計時器失效,另外,一次性的計時器在觸發完任務之后也會失效
  • 反復執行任務的計時器,很容易引入保留環,如果這種計時器的目標對象又保留了計時器本身,那肯定會導致保留環。這種環狀保留關系,可能是直接發生的,也可以是通過對象圖例的其他對象間接發生的
  • 可以擴充NTimer的功能,用block來打破保留環。不過,除非NTimer將來在公共接口里提供此功能,否則必須創建分類,將相關代碼加入其中
.h
/**
 *  使用的時候,在外面weak下,在block里strong一個,在調用方法。無須invalidate timer就可以釋放對象
 *
 *  @param interval 間隔時間
 *  @param block    方法回調
 *  @param repeats  是否重復
 *
 *  @return 返回timer
 */
+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;
.m
+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void (^)())block
                                       repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(eoc_blockInvoke:) userInfo:[block copy] repeats:repeats];
}

+ (void)eoc_blockInvoke:(NSTimer *)timer{
    void(^block)() = timer.userInfo;
    if (block) {
        block();
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容