讀《Effective Objective-C 2.0:編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》有感
1. Objective-C使用動(dòng)態(tài)綁定的消息結(jié)構(gòu),在運(yùn)行時(shí)才會(huì)檢查對(duì)象類型。
這種動(dòng)態(tài)消息工作方式?jīng)Q定了其不可能實(shí)現(xiàn)真正的私有方法或私有實(shí)例變量。
2. 在類的頭文件中盡量少引入其他頭文件
- 使用
@class xxx;(向前聲明)的方式在頭文件導(dǎo)入新類,并在實(shí)現(xiàn)文件中再#import該類的頭文件。這樣將引入頭文件的時(shí)機(jī)盡量延后,只在確有需要時(shí)才引入,既盡可能的降低了類之間的耦合,又減少了類的使用者所需引入的頭文件數(shù)量從而減少編譯時(shí)間。 - 向前聲明解決了兩個(gè)類互相引用的問題。
3. 多用字面量語法,少用與之等價(jià)的方法
- 使用字面量語法可以縮減源代碼長(zhǎng)度,使其更為易讀。
- 字面量語法創(chuàng)建數(shù)組、字典對(duì)象更為安全,因?yàn)槿舸嬖诳罩祵?duì)象則會(huì)拋出異常,有助于查錯(cuò)。
- 字面量語法創(chuàng)建出的字符串、數(shù)組、字典對(duì)象都是不可變的,要?jiǎng)?chuàng)建可變對(duì)象還需額外調(diào)用
[obj mutableCopy]方法。
4. 多用類型常量,少用#define預(yù)處理命令
static+const聲明的變量作用相當(dāng)于#define,但會(huì)帶有類型信息-
const右邊的總不能被修改,所以正確寫法是:static NSString * const coder = @"Hello world!"; static const NSTimeInterval duration = .3f; -
extern+const聲明并定義全局常量的寫法是://.h文件中公開,也可只定義在引入其頭文件的類中 extern NSString *const NotificationUserLogout; //.m NSString *const NotificationUserLogout = @"NotificationUserLogout";
5. 用枚舉表示狀態(tài)、選項(xiàng)、狀態(tài)碼
- 使用
NS_OPTIONS宏定義枚舉類型,可以通過按位或操作將多個(gè)選項(xiàng)同時(shí)組合起來 - 處理枚舉類型的
switch語句中不要實(shí)現(xiàn)default分支,應(yīng)覆蓋所有可能出現(xiàn)的情況
6. 理解“屬性特質(zhì)”
- 屬性特質(zhì)
- 原子性
-
atomic(defalut):原子的,內(nèi)部有自旋鎖,理論線程安全(對(duì)setter方法加鎖),耗資源 -
nonatomic:非原子屬性,非線程安全,常用
-
- 讀寫權(quán)限
-
readwrite(defalut):可讀可寫,合成getter和setter方法 -
readonly:只讀,只合成getter方法
-
- 內(nèi)存管理語義
-
assign:設(shè)置方法只會(huì)執(zhí)行針對(duì)“純量類型”(如CGFloat或NSInteger等)的簡(jiǎn)單賦值操作 -
strong:“擁有關(guān)系”。設(shè)置新值時(shí),設(shè)置方法先保留新值,并釋放舊值,再設(shè)置新值。 -
weak:“非擁有關(guān)系”。設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。所指對(duì)象被銷毀時(shí)自動(dòng)將指針置為nil -
unsafe_unretained:類似于weak,但對(duì)象被銷毀時(shí)不會(huì)將指針清空,會(huì)出現(xiàn)“懸掛指針” -
copy:類似于strong,但不保留新值,而是對(duì)其拷貝。常用于修飾“存在可變集合”的不可變集合對(duì)象(如NSString,NSArray,NSDictionary,NSSet)
-
- 方法名
-
getter=<name>:指定“獲取方法”的方法名 -
setter=<name>:指定“設(shè)置方法”的方法名,不常用
-
- 對(duì)應(yīng)關(guān)鍵字
-
@synthesize:未手動(dòng)實(shí)現(xiàn)setter和getter方法時(shí)編譯器會(huì)自動(dòng)生成,也可用于修改實(shí)例變量名(去_,不推薦) -
@dynamic:禁止自動(dòng)生成getter和setter方法
7. 在對(duì)象內(nèi)部盡量訪問實(shí)例變量
- 由于不經(jīng)過Objective-C的方法派發(fā),直接訪問實(shí)例變量的的速度更快
- 直接訪問實(shí)例變量不會(huì)調(diào)用其設(shè)置方法,故繞過了內(nèi)存管理語義,也不會(huì)觸發(fā)KVO通知
- 在對(duì)象內(nèi)部讀取數(shù)據(jù)時(shí),應(yīng)該直接通過實(shí)例變量來讀,而寫入數(shù)據(jù)時(shí),則應(yīng)通過屬性來寫
- 在初始化方法及
dealloc方法中,總是應(yīng)該直接通過實(shí)例變量(_xx)來讀寫數(shù)據(jù) - 使用懶加載時(shí),要通過屬性(
self.xx)來讀取數(shù)據(jù)
8. 理解“對(duì)象等同性”
==比較的是指針本身isEqual:默認(rèn)比較的是指針地址
- 先判
self == object,指針相等則返回YES- 再判
[self class] != [object class],若所屬類不相同則直接返回NO(注:需要考慮繼承情況,可使用isKindOfClass:)- 依次分別對(duì)每個(gè)屬性做值判斷,一旦有一個(gè)不等則返回
NO- 最后默認(rèn)返回
YES- 等同性約定:若
isEqual:判定兩對(duì)象相等,則hash一定相等;但hash相同的兩對(duì)象未必相等
- 若要檢測(cè)對(duì)象的等同性,請(qǐng)?zhí)峁?code>isEqual:與
hash方法 - 重寫
hash方法時(shí),應(yīng)提供計(jì)算速度快而且哈希碼碰撞幾率低的算法,不要?jiǎng)?chuàng)建額外的開銷
9. 以“類簇模式”隱藏實(shí)現(xiàn)細(xì)節(jié)
類簇模式可以把實(shí)現(xiàn)細(xì)節(jié)隱藏在公共接口之后,在系統(tǒng)框架中經(jīng)常使用。
Objective-C語言無法指明某個(gè)基類是“抽象的”,所以子類是不知道必須要實(shí)現(xiàn)父類的某個(gè)方法的。開發(fā)中常常是在超類的開發(fā)文檔中注明。
個(gè)人的做法是在父類抽象方法實(shí)現(xiàn)中加
#define MustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
宏,這樣一旦子類實(shí)例將消息發(fā)送到了父類該方法就會(huì)拋出異常
10. 在既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù)
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy)
id objc_getAssociatedObject(id object, void *key)
void objc_removeAssociatedObjects(id object)
關(guān)聯(lián)對(duì)象涉及到runtime的一些用法,需要引入#import <objc/runtime.h>,常常用于給分類(category)增加屬性
11. 理解消息發(fā)送(objc_msgSend)的作用
關(guān)于消息發(fā)送的相關(guān)知識(shí),具體可查閱《招聘一個(gè)靠譜的iOS》面試題參考答案(上):objc中向一個(gè)對(duì)象發(fā)送消息obj-foo和objc_msgsend函數(shù)之間有什么關(guān)系
12. 理解消息轉(zhuǎn)發(fā)(objc_msgForward)機(jī)制
關(guān)于消息發(fā)送的相關(guān)知識(shí),具體可查閱《招聘一個(gè)靠譜的iOS》面試題參考答案(下):objc_msgforward函數(shù)是做什么的直接調(diào)用它將會(huì)發(fā)生什么
13. “方法調(diào)配技術(shù)”(method swizzling)
實(shí)質(zhì)就是交換兩個(gè)方法的實(shí)現(xiàn),可用來在運(yùn)行期為已有類新增或替換選擇子(@selector)所對(duì)應(yīng)的方法。比如在分類的load方法中,執(zhí)行方法交換,以修改替換原有的方法實(shí)現(xiàn)。
14. 理解“類對(duì)象”結(jié)構(gòu)
typedef struct objc_class *Class;
struct objc_class {
Class isa; //isa指針,指向metaclass(該類的元類)
Class super_class; //指向objc_class(該類)的super_class(父類)
const char *name; //objc_class(該類)的類名
long version; //objc_class(該類)的版本信息,初始化為0,可以通過runtime函數(shù)class_setVersion和class_getVersion進(jìn)行修改和讀取
long info; //一些標(biāo)識(shí)信息,如CLS_CLASS表示objc_class(該類)為普通類。ClS_CLASS表示objc_class(該類)為metaclass(元類)
long instance_size; //objc_class(該類)的實(shí)例變量的大小
struct objc_ivar_list *ivars; //用于存儲(chǔ)每個(gè)成員變量的地址
struct objc_method_list **methodLists; //方法列表,與info標(biāo)識(shí)關(guān)聯(lián)
struct objc_cache *cache; //指向最近使用的方法的指針,用于提升效率
struct objc_protocol_list *protocols; //存儲(chǔ)objc_class(該類)的一些協(xié)議
};
15. 用前綴避免命名空間沖突
Objective-C沒有內(nèi)置命名空間(namespace)機(jī)制。
16. 提供“全能”(指定,designated)初始化方法
指定初始化方法應(yīng)覆蓋所有其他初始化方法,并覆寫超類對(duì)應(yīng)方法。
17. 實(shí)現(xiàn)description方法
#import <objc/runtime.h> //導(dǎo)入runtime頭文件
//重寫description方法同下,NSLog打印model詳細(xì)信息
//重寫debugDescription, lldb打印model詳細(xì)信息
- (NSString *)debugDescription {
//聲明一個(gè)字典
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
//得到當(dāng)前class的所有屬性
uint count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
//循環(huán)并用KVC得到每個(gè)屬性的值
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = @(property_getName(property));
id value = [self valueForKey:name]?:@"nil";//默認(rèn)值為nil字符串
[dictionary setObject:value forKey:name];//裝載到字典里
}
//釋放
free(properties);
return [NSString stringWithFormat:@"<%@: %p> -- %@",[self class],self,dictionary];
}
18. 盡量使用不可變對(duì)象
- 不希望外部修改的屬性用
readonly語義聲明,若內(nèi)部可修改在“class-continuation類“設(shè)置對(duì)應(yīng)的readwrite屬性 - 不宜公開可變的collection屬性,封裝方法返回collection也最好先轉(zhuǎn)成對(duì)應(yīng)的不可變類型
19. 使用清晰而協(xié)調(diào)的命名方式
代碼潔癖的自我修養(yǎng)
20. 為私有方法名加前綴
有助于區(qū)分于公開方法,一目了然
21. 理解Objectiove-C錯(cuò)誤模型
- 非嚴(yán)重錯(cuò)誤無須使用異常,必須使用時(shí)參考第9條添加異常宏
- 使用
NSError對(duì)象封裝錯(cuò)誤信息,并返回給調(diào)用者
22. 理解NSCopying協(xié)議
這是一個(gè)嚴(yán)謹(jǐn)的單例模式宏,支持ARC和MRC
#if __has_feature(objc_arc) // ARC
#define DEF_SINGLETON(name) \
static id _instance; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)sharedInstance \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
});\
return _instance; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instance; \
}
#else // 非ARC
#define DEF_SINGLETON(name) \
static id _instance; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)sharedInstance \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)autorelease \
{ \
return _instance; \
} \
\
- (id)retain \
{ \
return _instance; \
} \
\
- (NSUInteger)retainCount \
{ \
return 1; \
} \
\
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instance; \
}
#endif
23. 通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象間通信
- 屬性使用
weak修飾,防止保留環(huán) - 對(duì)于經(jīng)常要使用到的代理和公共數(shù)據(jù)源(如
UITextFieldDelegate,UITextViewDelegate,UITableViewDataSource等),可交由一個(gè)封裝的對(duì)象專門去實(shí)現(xiàn)這些方法,以節(jié)省重復(fù)代碼的書寫。 -
進(jìn)一步優(yōu)化:如需頻繁判斷
[_delegate respondsToSelector:@selector(xx)]的結(jié)果,可考慮實(shí)現(xiàn)含有位段的結(jié)構(gòu)體,將這一判斷結(jié)果緩存至其中
24. 將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中
現(xiàn)在已采用的方法,為大文件"減負(fù)",如AppDelegate+Configuration
25. 總是為第三方類的分類名稱加前綴
分類名和方法名都宜添加前綴,防沖突
26. 勿在分類中聲明屬性
有必要的時(shí)候還是用關(guān)聯(lián)對(duì)象實(shí)現(xiàn)吧,雖然作者不推薦
27. 使用"class-continuation分類"隱藏實(shí)現(xiàn)細(xì)節(jié)
其實(shí)也就是在.m文件中聲明私有屬性,但并不是真的私有,仍然可以用runtime遍歷屬性列表找到該屬性。
28. 通過協(xié)議提供匿名對(duì)象
通過id<xxxDelegate>這種方式聲明匿名對(duì)象,這樣使用者無須關(guān)心這種對(duì)象的具體類型,只專注于它能去做的事即可。
架構(gòu)設(shè)計(jì)
比如要同時(shí)支持百度地圖,高德地圖,谷歌地圖,這時(shí)由于這些第三方庫來自不同的類,所以無法繼承自同一基類,但它們都具有類似的特征,故可以采用這種協(xié)議方法將類似功能的方法提取公開,結(jié)合類簇模式思想調(diào)取對(duì)應(yīng)第三方庫的實(shí)現(xiàn)。
29. 理解引用計(jì)數(shù)
retain: 遞增保留計(jì)數(shù)release: 遞減保留計(jì)數(shù)autorelease: 待稍后(通常是下一次"事件循環(huán)",event loop)清理自動(dòng)釋放池(autorelease pool)時(shí),再遞減保留計(jì)數(shù)
保留計(jì)數(shù)至少為1。若保留計(jì)數(shù)為正,則對(duì)象繼續(xù)存活;若保留計(jì)數(shù)降為0,對(duì)象就被銷毀。
30. 以ARC簡(jiǎn)化引用計(jì)數(shù)
- ARC->MRC:
-fno-objc-arc- MRC->ARC:
-fobjc-arc
以alloc,new,copy,mutableCopy作為前綴的方法(這里要注意的是,后面接的詞語首位必須是大寫字母,即符合駝峰命名規(guī)則,詳見《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》此書),其返回的對(duì)象"歸調(diào)用者所有"(調(diào)用這四種方法的那段代碼要負(fù)責(zé)釋放方法所返回的對(duì)象,即autorelease不生效,需要手動(dòng)額外抵消一次保留操作)。
31. 在dealloc方法中只釋放引用并解除監(jiān)聽
ARC下dealloc方法中應(yīng)該做的
- 打印銷毀的日志(判斷此方法是否執(zhí)行):
NSLog(@"dealloc: %@",[self class]); - 移除KVO:
[_scrollView removeObserver:self forKeyPath:@"contentOffset"]; - 移除通知:
[[NSNotificationCenter defaultCenter] removeObserver:self]; - 釋放CoreFoundation對(duì)象:
CFRelease(coreFoundationObject) - 不推薦清理文件資源,常見的如數(shù)據(jù)庫、播放器等等,關(guān)閉它們更應(yīng)該明確其生命期進(jìn)行管理,而不是延遲到并不一定會(huì)執(zhí)行的
dealloc方法 - 不推薦在此關(guān)閉定時(shí)器(正確的定時(shí)器移除應(yīng)該由生命周期方法控制,打破循環(huán)引用):
[_timer invalidate](保險(xiǎn)做法見第52條)
32. 編寫“異常安全代碼”時(shí)留意內(nèi)存管理問題
- ARC下使用
@try...@catch...@finally實(shí)際上會(huì)造成try塊內(nèi)所創(chuàng)立的對(duì)象內(nèi)存泄漏,所以往往不這么干 - ARC下打開異常需打開編譯器的
-fobjc-arc-exceptions標(biāo)志,副作用是使應(yīng)用程序變大且降低運(yùn)行效率 - 異常的處理方法是使用
NSError傳回錯(cuò)誤信息,調(diào)用者根據(jù)情況作相應(yīng)處理
33. 以弱引用避免保留環(huán)
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
// use strongSelf
}
};
Weak-Strong-Dance防止block和對(duì)象間的循環(huán)引用不用多說,偷懶的寫法是使用宏:
#ifndef weakify
#if __has_feature(objc_arc)
#define weakify( x ) \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wshadow\\"") \\
autoreleasepool{} __weak __typeof__(x) __weak_##x##__ = x; \\
_Pragma("clang diagnostic pop")
#else
#define weakify( x ) \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wshadow\\"") \\
autoreleasepool{} __block __typeof__(x) __block_##x##__ = x; \\
_Pragma("clang diagnostic pop")
#endif
#endif
#ifndef strongify
#if __has_feature(objc_arc)
#define strongify( x ) \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wshadow\\"") \\
try{} @finally{} __typeof__(x) x = __weak_##x##__; \\
_Pragma("clang diagnostic pop")
#else
#define strongify( x ) \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wshadow\\"") \\
try{} @finally{} __typeof__(x) x = __block_##x##__; \\
_Pragma("clang diagnostic pop")
#endif
#endif
RAC里的高級(jí)寫法:
#define weakify(...) \\
autoreleasepool {} \\
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
#define strongify(...) \\
try {} @finally {} \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wshadow\\"") \\
metamacro_foreach(rac_strongify_,, __VA_ARGS__) \\
_Pragma("clang diagnostic pop")
34. 以“自動(dòng)釋放池塊”降低內(nèi)存峰值
常用于for循環(huán)中,將循環(huán)體代碼用@autoreleasepool{...}包裹起來,可以及時(shí)在每次循環(huán)后降低內(nèi)存峰值,而不用等到線程執(zhí)行下一次事件循環(huán)時(shí)
35. 用“僵尸對(duì)象”調(diào)試內(nèi)存管理問題
- 開啟方式:Xcode -> Edit Scheme -> Run -> Diagnostics -> Enable Zombie Objects<br/
- 原理:修改對(duì)象的isa指針,令其指向特殊的僵尸類,此僵尸類能夠響應(yīng)所有的選擇子(通過“完整的消息轉(zhuǎn)發(fā)機(jī)制”)。
- 響應(yīng)方式:如果消息接收者是僵尸對(duì)象(名稱前綴為_NSZombie_),此時(shí)打印一條包含消息內(nèi)容及其接收者的消息,然后終止程序。
調(diào)試時(shí)通過環(huán)境變量NSZombieEnabled開啟此功能,系統(tǒng)將不再回收對(duì)象,而是將其轉(zhuǎn)化為僵尸對(duì)象。
36. 不要使用retainCount
retainCount可能永遠(yuǎn)不返回0。因?yàn)橛袝r(shí)系統(tǒng)會(huì)優(yōu)化對(duì)象的釋放行為,當(dāng)保留計(jì)數(shù)為1且執(zhí)行了遞減操作,這時(shí)為了節(jié)省對(duì)象引用計(jì)數(shù)-1的開銷,會(huì)在稍后的某個(gè)時(shí)間點(diǎn)直接回收。
- 系統(tǒng)會(huì)盡可能把
NSString實(shí)現(xiàn)成單例對(duì)象。如果是編譯期常量,編譯器會(huì)把NSString對(duì)象所表示的數(shù)據(jù)放到應(yīng)用程序的二進(jìn)制文件里,這樣運(yùn)行時(shí)無需再創(chuàng)建NSString對(duì)象。NSNumber類似使用了“標(biāo)簽指針”(把與數(shù)值有關(guān)的全部消息都放在指針值里面)來標(biāo)注特定類型(不包括浮點(diǎn)數(shù)對(duì)象)的數(shù)值,同樣在運(yùn)行時(shí)就無需再創(chuàng)建NSNumber對(duì)象。- 進(jìn)行了以上兩種方式優(yōu)化的單例對(duì)象,其保留計(jì)數(shù)絕對(duì)不會(huì)變(很大的值)。這種對(duì)象的保留及釋放都是空操作。
37. 理解“塊”
語法結(jié)構(gòu):
return_type (^block_name)(parameters)
- 塊是一種詞法閉包,與python中的匿名函數(shù)
lambda不一樣,它可以無返回值,也可以作為傳參使用 - 默認(rèn)情況下塊所捕獲的變量是不可以在塊里修改的
- 使用
__block關(guān)鍵字修飾的變量可以在塊內(nèi)部被修改 -
塊總能修改實(shí)例變量,所以聲明時(shí)無須加
__block - 通過讀取或?qū)懭氩僮魇箟K捕獲了實(shí)例變量,此時(shí)
self變量也自動(dòng)被捕獲了(同時(shí)也會(huì)將其保留),如果此時(shí)self所指代的那個(gè)對(duì)象同時(shí)保留了塊,這種情況就導(dǎo)致了“保留環(huán)”(retain cycle)。
38. 為常用的塊類型創(chuàng)建typedef
使用簡(jiǎn)單,作為屬性時(shí)應(yīng)使用copy修飾
39. 用handler塊降低代碼分散程度
建議用同一個(gè)handler塊來處理網(wǎng)絡(luò)成功與失敗情況,比如雖然網(wǎng)絡(luò)請(qǐng)求成功了,但數(shù)據(jù)不符合預(yù)期,這時(shí)方便將錯(cuò)誤信息用NSError一并返回
40. 用塊引用其所屬對(duì)象時(shí)不要出現(xiàn)保留環(huán)
- 正常使用單例模式的網(wǎng)絡(luò)獲取器一般不會(huì)出現(xiàn)保留環(huán),它運(yùn)行在調(diào)用者所在的隊(duì)列上,所以不用過于擔(dān)心的在返回的塊內(nèi)加
@weakify...@strongify...或dispatch_async(dispatch_get_main_queue(), ^{...})這樣的代碼; - 但如果在調(diào)用者內(nèi)部使用
self.completionHandler = completion這樣的代碼將塊持有了(之所有這樣做的原因是為了稍后使用),這時(shí)往往要考慮保留環(huán)的問題,可在_completionHandler()執(zhí)行后使用self.completionHandler = nil方式將其清除。
41. 多用派發(fā)隊(duì)列,少用同步鎖
關(guān)于鎖的相關(guān)知識(shí)可以查看iOS中的鎖——由屬性atomic想到的線程安全
-
GCD是可以高效的代替同步塊或鎖對(duì)象。屬性通常不設(shè)置
atomic的原因在于它并非真正的線程安全,讀取操作可以并行,但寫入操作應(yīng)該限定單獨(dú)執(zhí)行。//指定全局并發(fā)隊(duì)列,若是跑在主線程上會(huì)出現(xiàn)死鎖 _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); - (NSUInteger)ticketCount { __block NSUInteger localTicketCount; //不開新線程,順序執(zhí)行 dispatch_sync(_syncQueue, ^{ localTicketCount = _ticketCount; }); return localTicketCount; } - (void)setTicketCount:(NSUInteger)ticketCount { //等待所有并發(fā)塊都執(zhí)行完畢,才會(huì)單獨(dú)執(zhí)行塊內(nèi)代碼 dispatch_barrier_async(_syncQueue, ^{ _ticketCount = ticketCount; }); }
42. 多用GCD,少用performSelector方法
-
performSelector系列方法使得編譯器無法在編譯器就確定選擇子和返回值,故ARC下并沒有插入內(nèi)存管理方法,易造成內(nèi)存泄漏。 -
performSelector系列方法參數(shù)的局限性太大,都有可替代的方案。
43. 掌握GCD及操作隊(duì)列的使用時(shí)機(jī)
NSOperation是對(duì)GCD更高層次的封裝,也提供了GCD無法實(shí)現(xiàn)(很難實(shí)現(xiàn))的特性。關(guān)于它及NSOperationQueue的用法可參照 AFNetworking
44. 通過Dispatch Group機(jī)制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)
通過dispatch group可以在并發(fā)隊(duì)列同時(shí)執(zhí)行多項(xiàng)任務(wù),并在這組任務(wù)執(zhí)行完畢時(shí)獲得通知。
45. 使用dispatch_once來執(zhí)行只需運(yùn)行一次的線程安全代碼
+ (instancetype)sharedInstance {
static ManageClass *sharedInstance = nil; //每次執(zhí)行會(huì)復(fù)用變量
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
編寫“只需執(zhí)行一次的線程安全代碼”,dispatch_once方式實(shí)現(xiàn)單例性能顯然要高于同步鎖機(jī)制@synchronized
46. 不要使用dispatch_get_current_queue
- 理解死鎖相關(guān)的模型
-
dispatch_get_current_queue僅應(yīng)該作為調(diào)試使用 - 隊(duì)列間有層級(jí)關(guān)系,排在某條隊(duì)列中的塊會(huì)在其上級(jí)隊(duì)列里執(zhí)行。
47. 熟悉系統(tǒng)框架
Foundation、CoreFoundation提供的API多了解一下
48. 多用塊枚舉,少用for循環(huán)
for循環(huán):優(yōu)點(diǎn)是方便取下標(biāo),缺點(diǎn)是會(huì)多創(chuàng)建一個(gè)中介數(shù)組,額外增加對(duì)附加對(duì)象的創(chuàng)建和釋放操作
快速枚舉:高效,不允許對(duì)遍歷集合修改,獲取當(dāng)前下標(biāo)還需額外定義變量
-
塊枚舉:提供更完善的塊信息,也可修改塊簽名,無須另行編碼就能并發(fā)執(zhí)行遍歷操作
- (void)enumerateObjectsUsingBlock:(void(^)(id object, NSUInteger idx, BOOL *stop))block - (void)enumerateKeysAndObjectsUsingBlock:(void(^)(id key, id object, BOOL *stop))block
49. 對(duì)自定義其內(nèi)存管理語義的collection使用無縫橋接
-
__bridge:OC和CF對(duì)象轉(zhuǎn)化時(shí)只涉及對(duì)象類型不涉及對(duì)象所有權(quán)轉(zhuǎn)化,由發(fā)起轉(zhuǎn)換對(duì)象控制。 -
__bridge_transfer:CF對(duì)象轉(zhuǎn)化成OC對(duì)象時(shí),移交其所有權(quán),由ARC管理,作用同CFBridgingRelease() -
__bridge_retained:OC對(duì)象轉(zhuǎn)化成CF對(duì)象,移交其所有權(quán),由CF對(duì)象調(diào)用相應(yīng)的CFRelease()方法銷毀,作用同CFBridgingRetain()
50. 構(gòu)建緩存時(shí)使用NSCache而非NSDictionary
NSCache優(yōu)點(diǎn)在于當(dāng)系統(tǒng)資源將要耗盡時(shí),它會(huì)自動(dòng)刪減“最久未使用的”(LRU)緩存。
所以我選擇用 YYCache
51. 精簡(jiǎn)initialize與load的實(shí)現(xiàn)代碼
-
+ (void)load:運(yùn)行期(通常是應(yīng)用程序啟動(dòng))每個(gè)類及分類必定且只有一次會(huì)調(diào)用此方法。無法覆寫,常用于method swizzling,盡量不在里面做多余操作 -
+ (void)initialize:懶加載模式,首次使用該類之前調(diào)用且只調(diào)用一次。其未覆寫此方法的子類也會(huì)收到超類消息
52. 別忘了NSTimer會(huì)保留其目標(biāo)對(duì)象
NSTimer會(huì)保留目標(biāo),反復(fù)執(zhí)行的計(jì)時(shí)器會(huì)造成保留環(huán)是個(gè)老生常談的問題了,常常用的方法時(shí)創(chuàng)建一個(gè)分類,使用“塊”來打破保留環(huán)
+ (NSTimer *)weak_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
repeats:(BOOL)repeats
handlerBlock:(void(^)(void))handler {
return [self scheduledTimerWithTimeInterval:timeInterval
target:self
selector:@selector(handlerBlockInvoke:)
userInfo:[handler copy]
repeats:repeats];
}
+ (NSTimer *)weak_timerWithTimeInterval:(NSTimeInterval)timeInterval
repeats:(BOOL)repeats
handlerBlock:(void (^)(NSTimer *timer))handler {
return [NSTimer timerWithTimeInterval:timeInterval
target:self
selector:@selector(handlerBlockInvoke:)
userInfo:[handler copy]
repeats:repeats];
}
+ (void)handlerBlockInvoke:(NSTimer *)timer {
void (^block)(void) = timer.userInfo;
if (block) {
block();
}
}
筆記??GitHub鏈接
讀《Effective Objective-C 2.0:編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》有感