《Effective Objective-C 2.0: 編寫高質(zhì)量 iOS 與 OS X 代碼的 52 個(gè)有效方法》核心內(nèi)容總結(jié)文檔

這是一份《Effective Objective-C 2.0: 編寫高質(zhì)量 iOS 與 OS X 代碼的 52 個(gè)有效方法》(作者:Matt Galloway)的核心內(nèi)容總結(jié)文檔。這本書是 Objective-C 開發(fā)者的經(jīng)典讀物,深入探討了最佳實(shí)踐和常見陷阱。

核心目標(biāo): 幫助你編寫更清晰、更健壯、更高效、更易維護(hù)的 Objective-C 代碼。

核心思想總結(jié):

  1. 深入理解語言特性: Objective-C 基于 C,但核心是 Smalltalk 式的消息傳遞機(jī)制(非嚴(yán)格的方法調(diào)用)和動(dòng)態(tài)運(yùn)行時(shí)。理解 isa 指針、消息派發(fā) (objc_msgSend)、方法解析和轉(zhuǎn)發(fā)機(jī)制是基礎(chǔ)。
  2. 內(nèi)存管理是核心: 雖然 ARC 極大地簡(jiǎn)化了內(nèi)存管理,但理解引用計(jì)數(shù)(Retain, Release, Autorelease)的原理、所有權(quán)修飾符 (__strong, __weak, __unsafe_unretained, __autoreleasing) 以及循環(huán)引用的形成與避免(特別是 blockdelegate)至關(guān)重要。ARC 不是萬能的。
  3. 接口設(shè)計(jì)清晰化: 類、方法、變量的命名要清晰一致,使用前綴避免沖突,設(shè)計(jì)良好的 API,利用初始化方法存取方法保證對(duì)象狀態(tài)的有效性。
  4. 擁抱協(xié)議和分類: 協(xié)議 (@protocol) 定義清晰的行為契約,支持多態(tài)和委托模式。分類 (@category) 是向現(xiàn)有類添加方法的強(qiáng)大工具,但需謹(jǐn)慎避免命名沖突和覆蓋原有方法。
  5. 善用 Block 和 GCD: Block 是強(qiáng)大的閉包機(jī)制,簡(jiǎn)化回調(diào)、異步和函數(shù)式編程,但要特別注意捕獲語義和循環(huán)引用。Grand Central Dispatch (GCD) 是管理并發(fā)和異步任務(wù)的現(xiàn)代、高效方式,理解隊(duì)列 (dispatch_queue_t)、同步/異步派發(fā) (dispatch_sync/async) 和任務(wù)組 (dispatch_group_t) 是關(guān)鍵。
  6. 熟悉系統(tǒng)框架: Foundation 和 Core Foundation 框架提供了強(qiáng)大的基礎(chǔ)類 (NSString, NSArray, NSDictionary, NSSet, NSNumber 等) 和機(jī)制 (如 NSCopying, NSFastEnumeration)。理解它們的特性(可變性、類簇、toll-free bridging)和高效使用方法是提升代碼質(zhì)量的基礎(chǔ)。
  7. 性能考慮: 避免過早優(yōu)化,但需了解常見性能瓶頸(如消息發(fā)送開銷、對(duì)象創(chuàng)建銷毀、集合操作、I/O、不合理的鎖使用),并使用 Instruments 進(jìn)行性能分析。優(yōu)化應(yīng)基于測(cè)量結(jié)果。

52 個(gè)有效方法章節(jié)與核心要點(diǎn)總結(jié):

第 1 章:熟悉 Objective-C

  1. 了解 Objective-C 語言的起源: OC 是 C 的超集,核心是動(dòng)態(tài)消息結(jié)構(gòu)(非函數(shù)調(diào)用)。理解 id 類型和運(yùn)行時(shí)的重要性。
  2. 在類的頭文件中盡量少引入其他頭文件: 使用 @class 前向聲明減少編譯依賴,提高編譯速度,避免循環(huán)引用。在實(shí)現(xiàn)文件中 #import。
  3. 多用字面量語法,少用與之等價(jià)的方法: 使用 @"string", @42, @[], @{} 等使代碼更簡(jiǎn)潔、安全(nil 插入會(huì)拋出異常)。
  4. 多用類型常量,少用 #define 預(yù)處理指令: 使用 static const 聲明類型明確的常量,優(yōu)于無類型的 #define。全局常量應(yīng)在實(shí)現(xiàn)文件中定義,頭文件 extern 聲明。
  5. 用枚舉表示狀態(tài)、選項(xiàng)、狀態(tài)碼: 使用 NS_ENUM(通用枚舉)和 NS_OPTIONS(位掩碼選項(xiàng))明確枚舉類型和基礎(chǔ)類型,提高可讀性和類型安全。

第 2 章:對(duì)象、消息、運(yùn)行期

  1. 理解“屬性”的概念: 屬性 (@property) 自動(dòng)合成存取方法 (getter/setter)、實(shí)例變量 (_ivar),指定語義 (strong, weak, copy, assign, atomic/nonatomic) 和存取控制。@synthesize@dynamic 的用法。
  2. 在對(duì)象內(nèi)部盡量直接訪問實(shí)例變量:init、dealloc 和自定義的存取方法內(nèi)部直接訪問 _ivar 更安全高效(避免 KVO 和子類覆寫的影響)。其他情況通常通過屬性訪問。
  3. 理解“對(duì)象等同性”的重要性: 重寫 isEqual:hash 方法來判斷對(duì)象內(nèi)容的等價(jià)性(而非指針相等 ==)。確保等價(jià)對(duì)象擁有相同 hash 值。
  4. 以“類族模式”隱藏實(shí)現(xiàn)細(xì)節(jié): 使用抽象基類(如 NSArray, NSString)和工廠方法返回具體的私有子類實(shí)例,簡(jiǎn)化公共接口,隱藏實(shí)現(xiàn)。
  5. 在既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù): 使用 objc_setAssociatedObjectobjc_getAssociatedObject 在運(yùn)行時(shí)為現(xiàn)有類實(shí)例動(dòng)態(tài)添加數(shù)據(jù)(謹(jǐn)慎使用,可能引入難以調(diào)試的問題)。
  6. 理解 objc_msgSend 的作用: 深入理解消息發(fā)送機(jī)制(查找方法實(shí)現(xiàn) IMP 的過程)是理解 OC 動(dòng)態(tài)性的基礎(chǔ)。
  7. 理解消息轉(zhuǎn)發(fā)機(jī)制: 當(dāng)對(duì)象無法響應(yīng)消息時(shí),運(yùn)行時(shí)提供 resolveInstanceMethod: (動(dòng)態(tài)添加方法)、forwardingTargetForSelector: (備用接收者)、methodSignatureForSelector: + forwardInvocation: (完整轉(zhuǎn)發(fā)) 三次補(bǔ)救機(jī)會(huì)。
  8. 用“方法調(diào)配技術(shù)”調(diào)試“黑盒方法”: 運(yùn)行時(shí)使用 class_getInstanceMethod, method_exchangeImplementations 等函數(shù)交換方法實(shí)現(xiàn) (Method Swizzling),用于調(diào)試或擴(kuò)展,但極其危險(xiǎn),應(yīng)謹(jǐn)慎并僅作最后手段。
  9. 理解“類對(duì)象”的用意: Class 類型本身也是對(duì)象(元類實(shí)例),理解 isa 指針鏈 (實(shí)例 -> 類 -> 元類 -> 根元類 -> 自身),使用 classsuperclass 方法。

第 3 章:接口與 API 設(shè)計(jì)

  1. 用前綴避免命名空間沖突: 為類名、分類名、全局函數(shù)/常量使用獨(dú)特的三字母前綴(如 ABC),防止與系統(tǒng)庫(kù)或其他第三方庫(kù)沖突。
  2. 提供“全能初始化方法”: 指定一個(gè)主要的初始化方法(通常參數(shù)最全),其他初始化方法最終調(diào)用它。確保繼承鏈上的全能初始化方法一致。
  3. 實(shí)現(xiàn) description 方法: 重寫 description 方法返回人類可讀的對(duì)象描述,對(duì)調(diào)試非常有幫助。debugDescription 用于調(diào)試器 (LLDB po 命令)。
  4. 盡量使用不可變對(duì)象: 設(shè)計(jì)類時(shí),優(yōu)先將屬性聲明為 readonly,僅在必要時(shí)暴露為 readwrite(通常在類擴(kuò)展中)。不可變對(duì)象更易理解、線程安全。
  5. 使用清晰而協(xié)調(diào)的命名方式: 方法名應(yīng)清晰表達(dá)意圖和參數(shù)作用(長(zhǎng)命名是 OC 風(fēng)格),遵循駝峰命名法,保持一致性。避免歧義。
  6. 為私有方法名加前綴: 使用前綴(如 p_)標(biāo)記私有方法,避免與父類或子類方法意外覆蓋,提高可讀性。
  7. 理解 Objective-C 錯(cuò)誤模型: OC 偏向使用 NSError ** 模式處理可恢復(fù)的錯(cuò)誤(預(yù)期可能發(fā)生的錯(cuò)誤)。Exceptions (異常) 僅用于不可恢復(fù)的編程錯(cuò)誤(如數(shù)組越界),不應(yīng)用于常規(guī)錯(cuò)誤處理流程。@throw 在 OC 中極少用。

第 4 章:協(xié)議與分類

  1. 理解 NSCopying 協(xié)議: 若自定義對(duì)象需要支持拷貝 (copy 方法),需實(shí)現(xiàn) NSCopying 協(xié)議中的 copyWithZone: 方法。區(qū)分淺拷貝(新容器,元素引用不變)和深拷貝(新容器,新元素)。通常提供專門的深拷貝方法。
  2. 通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象間通信: 使用委托模式 (Delegate) 向相關(guān)對(duì)象通知事件或請(qǐng)求數(shù)據(jù)。定義清晰的協(xié)議,屬性用 weak 避免循環(huán)引用。數(shù)據(jù)源模式 (DataSource) 專門用于為對(duì)象提供數(shù)據(jù)。
  3. 將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中: 使用分類將大型類按功能模塊拆分到不同文件中。創(chuàng)建名為 Private 的分類隱藏私有方法聲明(在.m文件中實(shí)現(xiàn))。
  4. 總是為第三方類的分類名稱加前綴: 向系統(tǒng)類或第三方庫(kù)的類添加分類時(shí),務(wù)必在分類名和方法名前加前綴,避免命名沖突。
  5. 勿在分類中聲明屬性: 分類原則上不能添加實(shí)例變量(除非使用關(guān)聯(lián)對(duì)象)。在分類中聲明屬性,只會(huì)生成 getter/setter 方法的聲明,不會(huì)生成實(shí)例變量和實(shí)現(xiàn),編譯器會(huì)警告。如需“屬性”,需手動(dòng)實(shí)現(xiàn)存取方法(通常借助關(guān)聯(lián)對(duì)象,但這破壞了封裝性,非首選)。

第 5 章:內(nèi)存管理

  1. 理解引用計(jì)數(shù): 核心原理:retain (+1), release (-1), autorelease (延遲 -1)。對(duì)象計(jì)數(shù)為 0 時(shí)銷毀。理解自動(dòng)釋放池 (@autoreleasepool) 的作用。
  2. 理解 ARC 如何解決引用計(jì)數(shù)問題: ARC 在編譯期自動(dòng)插入 retain, release, autorelease 調(diào)用。理解 ARC 規(guī)則:變量默認(rèn) __strong,方法名約定(alloc/new/copy/mutableCopy 返回的對(duì)象調(diào)用者持有)。
  3. dealloc 方法中只釋放引用并解除監(jiān)聽: dealloc 中應(yīng)調(diào)用 [_resource release] (MRC) 或置 nil (ARC),移除 KVO 觀察者和通知監(jiān)聽。不要調(diào)用異步方法、屬性存取器(可能觸發(fā) KVO)、或 [self method](對(duì)象已部分銷毀)。
  4. 編寫“異常安全代碼”時(shí)留意內(nèi)存管理問題: MRC 下,@try 塊中創(chuàng)建的對(duì)象可能因異常跳過 release,需在 @finally 釋放。ARC 下默認(rèn)開啟 -fobjc-arc-exceptions,會(huì)生成額外代碼處理異常路徑的內(nèi)存,但有開銷。C++ 異常與 OC 異常交互復(fù)雜。
  5. 以弱引用避免循環(huán)引用: 相互強(qiáng)引用導(dǎo)致對(duì)象無法釋放。使用 __weak (ARC) 或 weak 屬性打破強(qiáng)引用環(huán)(如 delegate, block 內(nèi)部捕獲 self 時(shí))。
  6. 以“自動(dòng)釋放池塊”降低內(nèi)存峰值: 在循環(huán)中創(chuàng)建大量臨時(shí)對(duì)象時(shí),用 @autoreleasepool {} 包裹循環(huán)體,讓臨時(shí)對(duì)象在池排干時(shí)釋放,降低內(nèi)存峰值。
  7. 用“僵尸對(duì)象”調(diào)試內(nèi)存管理問題: 開啟 NSZombieEnabled 環(huán)境變量,使已釋放對(duì)象變成“僵尸”,再次向其發(fā)送消息時(shí)會(huì)拋出明確異常(而非野指針崩潰),有助于定位過度釋放問題。僅在調(diào)試時(shí)使用。

第 6 章:塊與大中樞派發(fā) (Blocks & Grand Central Dispatch)

  1. 理解“塊”這一概念: Block 是閉包(函數(shù) + 捕獲的上下文變量)。語法:^returnType (parameters) {...}。內(nèi)存布局:isa 指針、標(biāo)志位、函數(shù)指針、捕獲變量描述符/值。
  2. 為常用的塊類型創(chuàng)建 typedef 使用 typedef 定義復(fù)雜的塊類型,提高代碼可讀性和可維護(hù)性。例如:typedef void (^CompletionHandler)(NSData *data, NSError *error);
  3. Handler Block 降低代碼分散程度: 使用 Block 代替委托 (Delegate) 或回調(diào)函數(shù) (Callback Function),將業(yè)務(wù)邏輯集中在一起(如網(wǎng)絡(luò)請(qǐng)求完成后的處理),避免狀態(tài)分散。
  4. 用 Block 引用其所屬對(duì)象時(shí)不要出現(xiàn)循環(huán)引用: Block 會(huì)強(qiáng)引用其捕獲的所有對(duì)象(包括 self)。若 Block 又被 self 強(qiáng)引用(如 self.blockProperty = aBlock;),則形成循環(huán)引用。解決方案:在 Block 外使用 __weak 引用 self,在 Block 內(nèi)使用 __strong 引用弱引用來保證執(zhí)行期間 self 存活。
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf; // 避免 weakSelf 在 block 執(zhí)行過程中被釋放
        [strongSelf doSomething];
    };
    
  5. 多用 GCD,少用同步鎖: 優(yōu)先使用 GCD 隊(duì)列 (dispatch_queue_t) 實(shí)現(xiàn)同步:
    • 串行隊(duì)列同步訪問: 將讀寫任務(wù)都派發(fā)到同一個(gè)串行隊(duì)列 (dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_SERIAL))。
    • 并發(fā)隊(duì)列與柵欄塊 (dispatch_barrier_async): 對(duì)于讀寫頻繁,讀可并發(fā),寫需獨(dú)占的場(chǎng)景。讀操作使用 dispatch_sync (并發(fā)執(zhí)行),寫操作使用 dispatch_barrier_async (確保寫時(shí)獨(dú)占)。
    • 避免低效的 @synchronizedNSLock(性能較差且易出錯(cuò))。
  6. 使用 GCD 隊(duì)列及 Block 實(shí)現(xiàn)操作同步,而非鎖: 強(qiáng)調(diào)同上(第 38 條)。用隊(duì)列管理任務(wù)執(zhí)行的順序。
  7. 使用 dispatch_group 來執(zhí)行并行任務(wù),并在任務(wù)完成后得到通知: 使用 dispatch_group_async 將多個(gè)并發(fā)任務(wù)關(guān)聯(lián)到同一個(gè)組 (dispatch_group_t),用 dispatch_group_notify 在所有任務(wù)完成后執(zhí)行回調(diào)。dispatch_group_enter/leave 管理非 Block API 的任務(wù)。
  8. 使用 dispatch_once 來執(zhí)行只需運(yùn)行一次的線程安全代碼: 實(shí)現(xiàn)單例模式或其他一次性初始化。標(biāo)準(zhǔn)、線程安全的單例模板:
    + (instancetype)sharedInstance {
        static MyClass *sharedInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [[self alloc] init];
        });
        return sharedInstance;
    }
    
  9. 掌握 GCD 及操作隊(duì)列的使用時(shí)機(jī): GCD 是輕量級(jí)的 C API,適合大多數(shù)并發(fā)和同步任務(wù)。NSOperationQueue 基于 GCD 構(gòu)建,提供更高級(jí)特性:操作依賴 (addDependency:)、取消 (cancel)、優(yōu)先級(jí) (queuePriority)、KVO 監(jiān)聽 (isFinished, isCancelled)。需要這些特性時(shí)選用 NSOperationQueue。

第 7 章:系統(tǒng)框架

  1. 熟悉系統(tǒng)框架: 首要掌握 Foundation (NS 前綴) 和 CoreFoundation (CF 前綴, C API),它們是 Cocoa/Cocoa Touch 的基石。了解 UIKit/AppKit, Core Animation, Core Graphics 等。
  2. 多用 Block 枚舉,少用 for 循環(huán): 使用容器類 (NSArray, NSSet, NSDictionary) 的基于 Block 的枚舉方法 (enumerateObjectsUsingBlock:, enumerateKeysAndObjectsUsingBlock:):
    • 可并行枚舉 (NSEnumerationConcurrent)
    • 可反向枚舉 (NSEnumerationReverse)
    • 可安全修改集合(某些情況下)
    • 代碼更集中。性能通常接近或優(yōu)于 for 循環(huán)。
  3. 對(duì)自定義其內(nèi)存管理語義的 collection 使用無縫橋接: 理解 Toll-Free Bridging:某些 Foundation 對(duì)象 (NSArray, NSString, NSDictionary) 與對(duì)應(yīng)的 CoreFoundation 對(duì)象 (CFArrayRef, CFStringRef, CFDictionaryRef) 在內(nèi)存層面等價(jià),可強(qiáng)制類型轉(zhuǎn)換 (__bridge, __bridge_retained, __bridge_transfer)。
  4. 構(gòu)建緩存時(shí)選用 NSCache 而非 NSDictionary NSCache 是專為緩存設(shè)計(jì)的類:
    • 自動(dòng)刪除策略:內(nèi)存不足時(shí)自動(dòng)清除,可設(shè)置成本 (countLimit, totalCostLimit)。
    • 線程安全。
    • 不會(huì)拷貝鍵 (Key),而是保留 (retain)。
  5. 精簡(jiǎn) initializeload 的實(shí)現(xiàn)代碼:
    • +(void)load: 類或分類被加載到運(yùn)行時(shí)時(shí)調(diào)用(非常早)。方法應(yīng)精簡(jiǎn),避免調(diào)用其他可能未加載的類。分類的 load 也會(huì)執(zhí)行。
    • +(void)initialize: 類在首次接收消息前(通常是首次使用)由運(yùn)行時(shí)調(diào)用一次(線程安全)。父類先于子類調(diào)用。應(yīng)精簡(jiǎn),避免復(fù)雜邏輯或調(diào)用子類可能覆寫的方法。
  6. 不要使用 dispatch_get_current_queue 此函數(shù)行為復(fù)雜且易導(dǎo)致死鎖(尤其涉及隊(duì)列層級(jí)時(shí)),已被廢棄。調(diào)試可使用隊(duì)列特有數(shù)據(jù) (dispatch_queue_set_specific/dispatch_get_specific) 替代。

關(guān)鍵建議回顧:

  • 命名清晰、一致、帶前綴。
  • 理解內(nèi)存管理 (ARC/引用計(jì)數(shù)) 和循環(huán)引用。
  • 優(yōu)先使用不可變對(duì)象。
  • 設(shè)計(jì)清晰的全能初始化方法和 API。
  • 善用協(xié)議 (委托/數(shù)據(jù)源) 和分類 (模塊化)。
  • 深入理解 Block 的捕獲語義和循環(huán)引用風(fēng)險(xiǎn)。
  • 優(yōu)先使用 GCD 隊(duì)列 (串行、并發(fā)+柵欄、、once) 進(jìn)行并發(fā)和同步,避免鎖。
  • 使用 NSCache 做緩存。
  • 熟悉 Foundation/CoreFoundation 的核心類及其特性(字面量、Block 枚舉、橋接)。
  • 利用運(yùn)行時(shí)特性 (description, 消息轉(zhuǎn)發(fā)),但謹(jǐn)慎使用關(guān)聯(lián)對(duì)象和方法調(diào)配。

這份總結(jié)提煉了書中的核心精髓和最佳實(shí)踐。要真正掌握,強(qiáng)烈建議仔細(xì)閱讀原書每個(gè)條款的詳細(xì)解釋、示例代碼和背后的原理分析。Objective-C 雖然逐漸被 Swift 取代,但其設(shè)計(jì)思想和與 Cocoa/Cocoa Touch 框架的深度集成,對(duì)于理解 iOS/macOS 開發(fā)生態(tài)仍有重要價(jià)值。在維護(hù)舊代碼或深入理解底層機(jī)制時(shí),這些知識(shí)尤為重要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容