這是一份《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é):
-
深入理解語言特性: 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ǔ)。 -
內(nèi)存管理是核心: 雖然 ARC 極大地簡(jiǎn)化了內(nèi)存管理,但理解引用計(jì)數(shù)(Retain, Release, Autorelease)的原理、所有權(quán)修飾符 (
__strong,__weak,__unsafe_unretained,__autoreleasing) 以及循環(huán)引用的形成與避免(特別是block和delegate)至關(guān)重要。ARC 不是萬能的。 -
接口設(shè)計(jì)清晰化: 類、方法、變量的命名要清晰一致,使用前綴避免沖突,設(shè)計(jì)良好的
API,利用初始化方法和存取方法保證對(duì)象狀態(tài)的有效性。 -
擁抱協(xié)議和分類: 協(xié)議 (
@protocol) 定義清晰的行為契約,支持多態(tài)和委托模式。分類 (@category) 是向現(xiàn)有類添加方法的強(qiáng)大工具,但需謹(jǐn)慎避免命名沖突和覆蓋原有方法。 -
善用 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)鍵。 -
熟悉系統(tǒng)框架: Foundation 和 Core Foundation 框架提供了強(qiáng)大的基礎(chǔ)類 (
NSString,NSArray,NSDictionary,NSSet,NSNumber等) 和機(jī)制 (如NSCopying,NSFastEnumeration)。理解它們的特性(可變性、類簇、toll-free bridging)和高效使用方法是提升代碼質(zhì)量的基礎(chǔ)。 - 性能考慮: 避免過早優(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
-
了解 Objective-C 語言的起源: OC 是 C 的超集,核心是動(dòng)態(tài)消息結(jié)構(gòu)(非函數(shù)調(diào)用)。理解
id類型和運(yùn)行時(shí)的重要性。 -
在類的頭文件中盡量少引入其他頭文件: 使用
@class前向聲明減少編譯依賴,提高編譯速度,避免循環(huán)引用。在實(shí)現(xiàn)文件中#import。 -
多用字面量語法,少用與之等價(jià)的方法: 使用
@"string",@42,@[], @{}等使代碼更簡(jiǎn)潔、安全(nil插入會(huì)拋出異常)。 -
多用類型常量,少用 #define 預(yù)處理指令: 使用
static const聲明類型明確的常量,優(yōu)于無類型的#define。全局常量應(yīng)在實(shí)現(xiàn)文件中定義,頭文件extern聲明。 -
用枚舉表示狀態(tài)、選項(xiàng)、狀態(tài)碼: 使用
NS_ENUM(通用枚舉)和NS_OPTIONS(位掩碼選項(xiàng))明確枚舉類型和基礎(chǔ)類型,提高可讀性和類型安全。
第 2 章:對(duì)象、消息、運(yùn)行期
-
理解“屬性”的概念: 屬性 (
@property) 自動(dòng)合成存取方法 (getter/setter)、實(shí)例變量 (_ivar),指定語義 (strong,weak,copy,assign,atomic/nonatomic) 和存取控制。@synthesize和@dynamic的用法。 -
在對(duì)象內(nèi)部盡量直接訪問實(shí)例變量: 在
init、dealloc和自定義的存取方法內(nèi)部直接訪問_ivar更安全高效(避免 KVO 和子類覆寫的影響)。其他情況通常通過屬性訪問。 -
理解“對(duì)象等同性”的重要性: 重寫
isEqual:和hash方法來判斷對(duì)象內(nèi)容的等價(jià)性(而非指針相等==)。確保等價(jià)對(duì)象擁有相同hash值。 -
以“類族模式”隱藏實(shí)現(xiàn)細(xì)節(jié): 使用抽象基類(如
NSArray,NSString)和工廠方法返回具體的私有子類實(shí)例,簡(jiǎn)化公共接口,隱藏實(shí)現(xiàn)。 -
在既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù): 使用
objc_setAssociatedObject和objc_getAssociatedObject在運(yùn)行時(shí)為現(xiàn)有類實(shí)例動(dòng)態(tài)添加數(shù)據(jù)(謹(jǐn)慎使用,可能引入難以調(diào)試的問題)。 -
理解
objc_msgSend的作用: 深入理解消息發(fā)送機(jī)制(查找方法實(shí)現(xiàn) IMP 的過程)是理解 OC 動(dòng)態(tài)性的基礎(chǔ)。 -
理解消息轉(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ì)。 -
用“方法調(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)慎并僅作最后手段。 -
理解“類對(duì)象”的用意:
Class類型本身也是對(duì)象(元類實(shí)例),理解isa指針鏈 (實(shí)例 -> 類 -> 元類 -> 根元類 -> 自身),使用class和superclass方法。
第 3 章:接口與 API 設(shè)計(jì)
-
用前綴避免命名空間沖突: 為類名、分類名、全局函數(shù)/常量使用獨(dú)特的三字母前綴(如
ABC),防止與系統(tǒng)庫(kù)或其他第三方庫(kù)沖突。 - 提供“全能初始化方法”: 指定一個(gè)主要的初始化方法(通常參數(shù)最全),其他初始化方法最終調(diào)用它。確保繼承鏈上的全能初始化方法一致。
-
實(shí)現(xiàn)
description方法: 重寫description方法返回人類可讀的對(duì)象描述,對(duì)調(diào)試非常有幫助。debugDescription用于調(diào)試器 (LLDBpo命令)。 -
盡量使用不可變對(duì)象: 設(shè)計(jì)類時(shí),優(yōu)先將屬性聲明為
readonly,僅在必要時(shí)暴露為readwrite(通常在類擴(kuò)展中)。不可變對(duì)象更易理解、線程安全。 - 使用清晰而協(xié)調(diào)的命名方式: 方法名應(yīng)清晰表達(dá)意圖和參數(shù)作用(長(zhǎng)命名是 OC 風(fēng)格),遵循駝峰命名法,保持一致性。避免歧義。
-
為私有方法名加前綴: 使用前綴(如
p_)標(biāo)記私有方法,避免與父類或子類方法意外覆蓋,提高可讀性。 -
理解 Objective-C 錯(cuò)誤模型: OC 偏向使用
NSError **模式處理可恢復(fù)的錯(cuò)誤(預(yù)期可能發(fā)生的錯(cuò)誤)。Exceptions(異常) 僅用于不可恢復(fù)的編程錯(cuò)誤(如數(shù)組越界),不應(yīng)用于常規(guī)錯(cuò)誤處理流程。@throw在 OC 中極少用。
第 4 章:協(xié)議與分類
-
理解
NSCopying協(xié)議: 若自定義對(duì)象需要支持拷貝 (copy方法),需實(shí)現(xiàn)NSCopying協(xié)議中的copyWithZone:方法。區(qū)分淺拷貝(新容器,元素引用不變)和深拷貝(新容器,新元素)。通常提供專門的深拷貝方法。 -
通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象間通信: 使用委托模式 (
Delegate) 向相關(guān)對(duì)象通知事件或請(qǐng)求數(shù)據(jù)。定義清晰的協(xié)議,屬性用weak避免循環(huán)引用。數(shù)據(jù)源模式 (DataSource) 專門用于為對(duì)象提供數(shù)據(jù)。 -
將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中: 使用分類將大型類按功能模塊拆分到不同文件中。創(chuàng)建名為
Private的分類隱藏私有方法聲明(在.m文件中實(shí)現(xiàn))。 - 總是為第三方類的分類名稱加前綴: 向系統(tǒng)類或第三方庫(kù)的類添加分類時(shí),務(wù)必在分類名和方法名前加前綴,避免命名沖突。
-
勿在分類中聲明屬性: 分類原則上不能添加實(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)存管理
-
理解引用計(jì)數(shù): 核心原理:
retain(+1),release(-1),autorelease(延遲 -1)。對(duì)象計(jì)數(shù)為 0 時(shí)銷毀。理解自動(dòng)釋放池 (@autoreleasepool) 的作用。 -
理解 ARC 如何解決引用計(jì)數(shù)問題: ARC 在編譯期自動(dòng)插入
retain,release,autorelease調(diào)用。理解 ARC 規(guī)則:變量默認(rèn)__strong,方法名約定(alloc/new/copy/mutableCopy返回的對(duì)象調(diào)用者持有)。 -
在
dealloc方法中只釋放引用并解除監(jiān)聽:dealloc中應(yīng)調(diào)用[_resource release](MRC) 或置nil(ARC),移除 KVO 觀察者和通知監(jiān)聽。不要調(diào)用異步方法、屬性存取器(可能觸發(fā) KVO)、或[self method](對(duì)象已部分銷毀)。 -
編寫“異常安全代碼”時(shí)留意內(nèi)存管理問題: MRC 下,
@try塊中創(chuàng)建的對(duì)象可能因異常跳過release,需在@finally釋放。ARC 下默認(rèn)開啟-fobjc-arc-exceptions,會(huì)生成額外代碼處理異常路徑的內(nèi)存,但有開銷。C++ 異常與 OC 異常交互復(fù)雜。 -
以弱引用避免循環(huán)引用: 相互強(qiáng)引用導(dǎo)致對(duì)象無法釋放。使用
__weak(ARC) 或weak屬性打破強(qiáng)引用環(huán)(如delegate,block內(nèi)部捕獲self時(shí))。 -
以“自動(dòng)釋放池塊”降低內(nèi)存峰值: 在循環(huán)中創(chuàng)建大量臨時(shí)對(duì)象時(shí),用
@autoreleasepool {}包裹循環(huán)體,讓臨時(shí)對(duì)象在池排干時(shí)釋放,降低內(nèi)存峰值。 -
用“僵尸對(duì)象”調(diào)試內(nèi)存管理問題: 開啟
NSZombieEnabled環(huán)境變量,使已釋放對(duì)象變成“僵尸”,再次向其發(fā)送消息時(shí)會(huì)拋出明確異常(而非野指針崩潰),有助于定位過度釋放問題。僅在調(diào)試時(shí)使用。
第 6 章:塊與大中樞派發(fā) (Blocks & Grand Central Dispatch)
-
理解“塊”這一概念: Block 是閉包(函數(shù) + 捕獲的上下文變量)。語法:
^returnType (parameters) {...}。內(nèi)存布局:isa指針、標(biāo)志位、函數(shù)指針、捕獲變量描述符/值。 -
為常用的塊類型創(chuàng)建
typedef: 使用typedef定義復(fù)雜的塊類型,提高代碼可讀性和可維護(hù)性。例如:typedef void (^CompletionHandler)(NSData *data, NSError *error); -
用
Handler Block降低代碼分散程度: 使用 Block 代替委托 (Delegate) 或回調(diào)函數(shù) (Callback Function),將業(yè)務(wù)邏輯集中在一起(如網(wǎng)絡(luò)請(qǐng)求完成后的處理),避免狀態(tài)分散。 -
用 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]; }; -
多用 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ú)占)。 - 避免低效的
@synchronized或NSLock(性能較差且易出錯(cuò))。
-
串行隊(duì)列同步訪問: 將讀寫任務(wù)都派發(fā)到同一個(gè)串行隊(duì)列 (
- 使用 GCD 隊(duì)列及 Block 實(shí)現(xiàn)操作同步,而非鎖: 強(qiáng)調(diào)同上(第 38 條)。用隊(duì)列管理任務(wù)執(zhí)行的順序。
-
使用
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ù)。 -
使用
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; } -
掌握 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)框架
-
熟悉系統(tǒng)框架: 首要掌握 Foundation (
NS前綴) 和 CoreFoundation (CF前綴, C API),它們是 Cocoa/Cocoa Touch 的基石。了解 UIKit/AppKit, Core Animation, Core Graphics 等。 -
多用 Block 枚舉,少用
for循環(huán): 使用容器類 (NSArray,NSSet,NSDictionary) 的基于 Block 的枚舉方法 (enumerateObjectsUsingBlock:,enumerateKeysAndObjectsUsingBlock:):- 可并行枚舉 (
NSEnumerationConcurrent) - 可反向枚舉 (
NSEnumerationReverse) - 可安全修改集合(某些情況下)
- 代碼更集中。性能通常接近或優(yōu)于
for循環(huán)。
- 可并行枚舉 (
-
對(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)。 -
構(gòu)建緩存時(shí)選用
NSCache而非NSDictionary:NSCache是專為緩存設(shè)計(jì)的類:- 自動(dòng)刪除策略:內(nèi)存不足時(shí)自動(dòng)清除,可設(shè)置成本 (
countLimit,totalCostLimit)。 - 線程安全。
- 不會(huì)拷貝鍵 (
Key),而是保留 (retain)。
- 自動(dòng)刪除策略:內(nèi)存不足時(shí)自動(dòng)清除,可設(shè)置成本 (
-
精簡(jiǎn)
initialize與load的實(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)用子類可能覆寫的方法。
-
-
不要使用
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í)尤為重要。