47.熟悉系統(tǒng)框架
框架:將一系列代碼封裝為動(dòng)態(tài)庫(dynamic library),并在其中放入描述其接口的頭文件班挖,這樣做出來的東西叫做框架。
在為Mac OS X或iOS系統(tǒng)開發(fā)“帶圖形界面的應(yīng)用程序”時(shí)芯砸,會(huì)用到名為Cocoa的框架萧芙,在iOS上稱為Cocoa Touch。其實(shí)Cocoa本身并不是框架假丧,但是里面集成了一批創(chuàng)建應(yīng)用程序時(shí)經(jīng)常會(huì)用到的框架双揪。
要點(diǎn):
- 許多系統(tǒng)框架都可以直接使用。其中最重要的是Foundation與CoreFoundation,這兩個(gè)框架提供了構(gòu)建應(yīng)用程序所需要的許多核心功能包帚;
- 很多常見任務(wù)都能用框架來做渔期,例如音頻與視頻處理、網(wǎng)絡(luò)通信渴邦,數(shù)據(jù)管理等疯趟;
- 請(qǐng)記住:用純C寫成的框架與ObjectiveC寫成的一樣重要谋梭,若想成為優(yōu)秀的Objective-C開發(fā)者信峻,應(yīng)該掌握C語言的核心概念。
48.多用塊枚舉瓮床,少用for循環(huán)
//三個(gè)舉例集合
NSArray *array = @[@1,@2,@3];
NSDictionary *dict = @{
@"key1":@"value1",
@"key2":@"value2",
@"key3":@"value3"
};
NSSet *set = [[NSSet alloc] initWithObjects:@"str1",@"str2",@"str3", nil];
//for 循環(huán)
for (int i = 0; i < array.count; i++) {
id object = array[i];
//do something with object
}
//對(duì)于字典中間做一個(gè)allkeys的轉(zhuǎn)換
NSArray *keys = [dict allKeys];
//對(duì)于set集合中間做一個(gè)allobjects的轉(zhuǎn)換
NSArray *objects = [set allObjects];
//Objective-C 1.0的NSEnumerator來遍歷
- (NSArray *)allObjects;
- (id)nextObject;
//Objective-C 1.0的NSEnumerator來遍歷
NSEnumerator *enumerator = [array objectEnumerator];
id object;//若知道具體類型盹舞,可直接寫成具體類型
while ((object = [enumerator nextObject]) != nil) {
//do something with object
}
NSEnumerator *dicEnumerator = [dict keyEnumerator];
id key;//若知道具體類型产镐,可直接寫成具體類型
while ((key = [dicEnumerator nextObject]) != nil) {
id value = [dict objectForKey:key];//若知道具體類型,可直接寫成具體類型
// do something with value
}
NSEnumerator *setEnumerator = [set objectEnumerator];
id setObject;//若知道具體類型矾策,可直接寫成具體類型
while ((setObject = [setEnumerator nextObject]) != nil) {
// do something with object
}
#warning !!!若需要反向遍歷磷账,則可以使用反向枚舉器
[array reverseObjectEnumerator];
//Objective-C 2.0的快速遍歷 for in
for (id object in array) {
// do something with object
}
for (id key in dict) {
id object = [dict objectForKey:key];
// do something with object
}
for (id object in set) {
// do something with object
}
//基于塊的遍歷方式
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// do something with object
}];
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// do something with object
}];
[set enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
// do something with object
}];
//若要反向遍歷,則可使用帶有“選項(xiàng)掩碼”(option mask)的方法
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// do something with object
}];
要點(diǎn):
- 遍歷collection有四種方式贾虽。最基本的辦法是for循環(huán)逃糟,其次是NSEnumeratior遍歷法及快速遍歷法,最新蓬豁、最先進(jìn)的方式則是“塊枚舉法”绰咽。
- “塊枚舉法”本身就能通過GCD來并發(fā)執(zhí)行遍歷操作,無須另行編寫代碼地粪。而采用其他遍歷方式則無法輕易實(shí)現(xiàn)這一點(diǎn)取募。
- 若提前知道待遍歷的collection含有何種對(duì)象,則應(yīng)修改塊簽名蟆技,指出對(duì)象的具體類型玩敏。
49.對(duì)自定義其內(nèi)存管理語義的collection使用無縫橋接
要點(diǎn):
- 通過無縫橋接技術(shù),可以在Foundation框架中的Objective-C對(duì)象與CoreFoundation框架中的C語言數(shù)據(jù)結(jié)構(gòu)之間來回轉(zhuǎn)換质礼。
- 在CoreFoundation層面創(chuàng)建collection時(shí)旺聚,可以指定許多回調(diào)函數(shù),這些函數(shù)表示此collection應(yīng)如何處理其元素眶蕉。然后砰粹,可運(yùn)用無縫橋接技術(shù),將其轉(zhuǎn)換成具備特殊內(nèi)存管理語義的Objective-C collection造挽。
50.構(gòu)建緩存時(shí)碱璃,選用NSCache而非NSDictionary
之前在閱讀SDWebImage源碼時(shí),有看到里面緩存使用的就是NSCache,其使用可查看文章
關(guān)于緩存類NSCache
要點(diǎn):
- 實(shí)現(xiàn)緩存時(shí)應(yīng)選用NSCache而非NSDictionary對(duì)象饭入。因?yàn)镹SCache可以提供優(yōu)雅的自動(dòng)刪減功能嵌器,而且是“線程安全的”,此外谐丢,它與字典不同嘴秸,并不會(huì)拷貝鍵。
- 可以給NSCache對(duì)象設(shè)置上限庇谆,用以限制緩存中的對(duì)象總個(gè)數(shù)及“總成本”岳掐,而這些尺度則定義了緩存刪減其中對(duì)象的時(shí)機(jī)。但是絕對(duì)不要把這些尺度當(dāng)成可靠的“硬限制”饭耳,他們僅對(duì)NSCache起知道作業(yè)
- 將NSPurgeableData與NSCache搭配使用串述,可實(shí)現(xiàn)自動(dòng)清除數(shù)據(jù)的功能,也就是說寞肖,當(dāng)NSPurgeableData對(duì)象所占內(nèi)存為系統(tǒng)所丟棄時(shí)纲酗,該對(duì)象自身也會(huì)從緩存中移除衰腌。
- 如果緩存使用得當(dāng),那么應(yīng)用程序的響應(yīng)速度就能提高觅赊。只有那種“重新計(jì)算起來很費(fèi)事的”數(shù)據(jù)右蕊,才值得放入緩存,比如那些需要從網(wǎng)絡(luò)獲取或從磁盤讀取的數(shù)據(jù)
51.精簡(jiǎn)initialize與load的實(shí)現(xiàn)代碼
+ (void)load; //(加載進(jìn)系統(tǒng)時(shí)調(diào)用)
+ (void)initialize;//(類在使用前的初始化調(diào)用吮螺,系統(tǒng)調(diào)用)
// + (void)load
對(duì)于加入運(yùn)行期系統(tǒng)中的每個(gè)類(class)及分類(category)來說饶囚,必定會(huì)調(diào)用此方法,而且僅調(diào)用一次。當(dāng)包含類或分類的程序庫載入系統(tǒng)時(shí)鸠补,就會(huì)執(zhí)行此方法萝风,而這通常就是指應(yīng)用程序啟動(dòng)的時(shí)候,若程序是為iOS平臺(tái)設(shè)計(jì)的紫岩,則肯定會(huì)在此時(shí)執(zhí)行规惰。如果分類和其所屬的類都定義了load方法,則先調(diào)用類里面的歇万,再調(diào)用分類里的,因?yàn)榉诸惱锩婵赡軙?huì)使用到類里面的方法勋陪。
有個(gè)重要的事情需注意堕花,那就是load方法并不像普通的方法那樣粥鞋,它并不遵從那套繼承規(guī)則。如果某個(gè)類本身沒實(shí)現(xiàn)load方法瞄崇,那么不管其各級(jí)超類是否實(shí)現(xiàn)此方法呻粹,系統(tǒng)都不會(huì)調(diào)用。
而且load方法務(wù)必實(shí)現(xiàn)的精簡(jiǎn)一些苏研,也就是要盡量減少其所執(zhí)行的操作等浊,因?yàn)檎麄€(gè)應(yīng)用程序在執(zhí)行l(wèi)oad方法時(shí)都會(huì)阻塞。如果load方法中包含繁雜的代碼摹蘑,那么應(yīng)用程序在執(zhí)行期間就會(huì)變得無響應(yīng)筹燕。不要在里面等待鎖,也不用調(diào)用可能加鎖的方法衅鹿。
//+ (void)initialize;
對(duì)于每個(gè)類來說撒踪,該方法會(huì)在程序首次使用該類之前調(diào)用,且只調(diào)用一次大渤。它是由運(yùn)行期系統(tǒng)來調(diào)用的制妄,絕不應(yīng)該通過代碼直接調(diào)用。
首先泵三,它是“惰性調(diào)用的”耕捞,也就是說衔掸,只有當(dāng)程序用到了相關(guān)的類時(shí),才會(huì)調(diào)用俺抽。因此敞映,如果某個(gè)類一直沒有使用,那么其initialize方法就一直不會(huì)運(yùn)行磷斧。這也就是說振愿,應(yīng)用程序無須先把每個(gè)類的initialize都執(zhí)行一遍,這與load方法不同瞳抓,對(duì)于load來說埃疫,應(yīng)用程序必須阻塞并等著所有類的load都執(zhí)行完,才能繼續(xù)孩哑。
其次栓霜,此方法與load方法還有個(gè)區(qū)別,就是運(yùn)行期系統(tǒng)在執(zhí)行該方法時(shí)横蜒,是處于正常狀態(tài)的胳蛮,因此,從運(yùn)行期系統(tǒng)完整度上來講丛晌,此時(shí)可以安全使用并調(diào)用任意類中的任意方法仅炊。而且,運(yùn)行期系統(tǒng)也能確保initialize方法一定會(huì)在“線程安全的環(huán)境”中執(zhí)行澎蛛,這就是說抚垄,只有執(zhí)行initialize的那個(gè)線程可以操作類或類實(shí)例。其他線程都要先阻塞谋逻,等著initialize執(zhí)行完呆馁。
最后,initialize方法與其他消息一樣毁兆,如果某個(gè)類未實(shí)現(xiàn)它浙滤,而其超類實(shí)現(xiàn)了,那么就會(huì)運(yùn)行超類的實(shí)現(xiàn)代碼气堕。
要點(diǎn):
- 在加載階段纺腊,如果類實(shí)現(xiàn)了load方法,那么系統(tǒng)就會(huì)調(diào)用它茎芭。分類里也可以定義此方法揖膜,類的load方法要比分類中的先調(diào)用。與其他方法不同梅桩,load方法不參與覆寫機(jī)制次氨。
- 首次使用某個(gè)類之前,系統(tǒng)會(huì)向其發(fā)生initialize消息摘投。由于此方法遵從普通的覆寫規(guī)則煮寡,所以通常應(yīng)該在里面判斷當(dāng)前要初始化的是哪個(gè)類虹蓄。
- load與initialize方法都應(yīng)該實(shí)現(xiàn)的精簡(jiǎn)一些,這有助于保持應(yīng)用程序的響應(yīng)能力幸撕,也能減少引入“依賴環(huán)”的幾率薇组。
- 無法在編譯期設(shè)定的全局常量,可以放在initialize方法里初始化坐儿。
52.別忘了NSTimer會(huì)保留其目標(biāo)對(duì)象
#import <Foundation/Foundation.h>
@interface NSTimer (BlockTimer)
+ (NSTimer *)timer_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)(void))block repeats:(BOOL)repeats;
@end
#import "NSTimer+BlockTimer.h"
@implementation NSTimer (BlockTimer)
+ (NSTimer *)timer_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)(void))block repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(timer_blockInvoke:) userInfo:[block copy] repeats:repeats];
}
+ (void)timer_blockInvoke:(NSTimer *)timer
{
void (^block)(void) = timer.userInfo;
if (block) {
block();
}
}
@end
//use
//本類擁有實(shí)例變量 _timer
- (void)startTimer
{
__weak typeof(self) weakSelf = self;
_myTimer = [NSTimer timer_scheduledTimerWithTimeInterval:5.0 block:^{
ViewController *strongSelf = weakSelf;
[strongSelf dosomething];
} repeats:YES];
}
- (void)dosomething
{
}
- (void)stopTimer
{
[_myTimer invalidate];
_myTimer = nil;
}
- (void)dealloc
{
[_myTimer invalidate];
}
要點(diǎn):
- NSTimer對(duì)象會(huì)保留其目標(biāo)律胀,直到計(jì)時(shí)器本身失效為止,調(diào)用invalidate方法可令計(jì)時(shí)器失效貌矿,另外炭菌,一次性的計(jì)時(shí)器在觸發(fā)任務(wù)之后也會(huì)失效。
- 反復(fù)執(zhí)行任務(wù)的計(jì)時(shí)器逛漫,很容易引入保環(huán)黑低,如果這種計(jì)時(shí)器的目標(biāo)對(duì)象又保留了計(jì)時(shí)器本身,那肯定會(huì)導(dǎo)致保留環(huán)酌毡。這種環(huán)狀保留關(guān)系克握,可能是直接發(fā)生的,也可能是通過其他對(duì)象間接發(fā)生的枷踏。
- 可以擴(kuò)充NSTimer的功能菩暗,用“塊”來打破保留環(huán)。不過旭蠕,除非NSTimer將來在公共接口里提供此功能停团,否則必須創(chuàng)建分類,將相關(guān)實(shí)現(xiàn)代碼加入其中掏熬。
PDF格式的資料來自iOS開發(fā)交流群佑稠、感覺作者的貢獻(xiàn),對(duì)于知識(shí)的系統(tǒng)歸納總結(jié)很有幫助孽江。
編寫高質(zhì)量代碼的52個(gè)有效方法
編寫高質(zhì)量代碼的52個(gè)有效方法(一)—熟悉OC
編寫高質(zhì)量代碼的52個(gè)有效方法(二)—對(duì)象、消息番电、運(yùn)行期
編寫高質(zhì)量代碼的52個(gè)有效方法(三)—接口與API設(shè)計(jì)
編寫高質(zhì)量代碼的52個(gè)有效方法(四)—協(xié)議與分類
編寫高質(zhì)量代碼的52個(gè)有效方法(五)—內(nèi)存管理
編寫高質(zhì)量代碼的52個(gè)有效方法(六)—塊與大中樞派發(fā)
編寫高質(zhì)量代碼的52個(gè)有效方法(七)---系統(tǒng)框架