重讀 Effective Objective-C 2.0 小記

最近再次拜讀了<<Effective Objective-C 2.0>>這本書(shū), 經(jīng)典的書(shū)確實(shí)值得閱讀, 并且里面的很多東西, 并不過(guò)時(shí), 書(shū)中有52條建議, 但這里筆者只是選取了其中的幾條來(lái)分享, 這幾條可能是我們?cè)陂_(kāi)發(fā)中比較常用的, 還有就是因?yàn)槠渌牟皇悄苡煤芏痰恼Z(yǔ)言寫(xiě)出來(lái)的, 如果你沒(méi)有讀過(guò)這本經(jīng)典的書(shū), 還是建議閱讀一下原書(shū).


  1. 對(duì)于OC中的對(duì)象聲明例如NSObject *obj1 = [NSObject new];, obj1這個(gè)指針變量是分配在棧上的, 他指向的是這一個(gè)分配在堆上面的實(shí)例對(duì)象, 如果進(jìn)行下面的賦值操作NSObject *obj2 = obj1;,那么并沒(méi)有新生成一個(gè)實(shí)例對(duì)象, 只是在棧上分配了一個(gè)新的指針變量obj2, 而obj2和obj1指向的實(shí)例對(duì)象是同一個(gè).
  • 關(guān)于文件頭文件的引入問(wèn)題, 一般情況下不建議在A.h文件中引入其他的B.h文件, 因?yàn)樵趧e人引入A.h的時(shí)候, 同時(shí)也引入了B.h文件, 增加不必要的文件耦合和編譯時(shí)間, 一般在.h文件中使用前向聲明@class B, 而在.m文件中才真的引入頭文件, 當(dāng)然對(duì)于protocol不能使用前向聲明, 如果將protocol放在了另一個(gè).h文件中, 那么就必須要引入這個(gè)頭文件了.
  • 盡量使用字面量語(yǔ)法來(lái)初始化字符串, 數(shù)組, 字典等, 因?yàn)樽置媪空Z(yǔ)法其實(shí)是一種語(yǔ)法糖, 使用它可以讓代碼可讀性更高, 當(dāng)然對(duì)于一些必須要使用到初始化方法的時(shí)候字面量語(yǔ)法就不好用了.例如:
 NSString *str = @"string";
 NSArray *arr = @[obj1, obj2];
arr[1]// 讀取使用下標(biāo)而盡量不使用對(duì)應(yīng)的函數(shù)...
[array setObject:<#(nonnull id)#> atIndexedSubscript:<#(NSUInteger)#>] 
  • 少用#define來(lái)定義常量, 因?yàn)楹甓x只是簡(jiǎn)單的代碼替換, 并沒(méi)有類(lèi)型判斷, 不便于我們閱讀判斷, 同時(shí)宏定義可以被覆蓋, 當(dāng)別人引入了我們的頭文件的時(shí)候, 可能會(huì)覆蓋我們里面定義的宏, 帶來(lái)很麻煩的調(diào)試, 我們應(yīng)該使用C語(yǔ)言風(fēng)格的 const, static, extern相結(jié)合來(lái)定義常量
/// 使用static 和const 定義文件內(nèi)部的常量 一般使用k開(kāi)頭命名
static float const kAnimationTime = 2.0f;
/// 使用const定義全局的常量, 在其他文件中可以通過(guò) extern float const kAnimationTime引入使用, 一般不用k開(kāi)頭命名, 而使用class名字
float const CustomAnimationTime = 2.0f;
  • 用好枚舉, 使用枚舉來(lái)表示選項(xiàng), 狀態(tài)碼, 可以讓代碼更清晰, 這個(gè)在系統(tǒng)的API中也經(jīng)常看到, 比如按鈕的狀態(tài), autoresizing... , 例如如果你需要用一些狀態(tài)碼來(lái)表示網(wǎng)絡(luò)請(qǐng)求的結(jié)果: 你可能會(huì)有兩種方法
    1. 定義一個(gè)整形變量, 然后說(shuō)明, 不同的整數(shù)代表不同的狀態(tài), 那么這樣對(duì)于開(kāi)發(fā)就很不方便, 必須得很清楚并且很正確的輸入對(duì)應(yīng)的整數(shù)才能表示相應(yīng)的狀態(tài), 那么就很容易出錯(cuò), 和不便于維護(hù)
    int statusCode;
    if (statusCode == 200) { }/// 請(qǐng)求成功
    else if () ....
    2. 使用枚舉, 對(duì)不同的狀態(tài)定義不同的名字, 這樣就很清晰方便了, 當(dāng)然定義的時(shí)候使用NS_ENUM比使用enum要`好`
typedef NS_ENUM(NSInteger, ErrorCode) {
    ErrorCodeNotFind,
    ErrorCodeLostConnection,
    ErrorCodeUnknow
};

顯然上面你應(yīng)該選用枚舉, 同時(shí)還有一種情況就是, 定義多選項(xiàng), 這個(gè)你是會(huì)把他們都放進(jìn)一個(gè)數(shù)組中么?? 當(dāng)然不要這樣做, 這個(gè)時(shí)候也應(yīng)該使用枚舉來(lái)定義, 不過(guò)會(huì)有一點(diǎn)的小技巧, Apple對(duì)這種進(jìn)行了一個(gè)包裝, 使用NS_OPTIONS而不是enum

typedef NS_OPTIONS(NSInteger, ErrorOptions) {
    ErrorOptionsNone = 0,
    ErrorOptionsOne = 1 << 0, ///左移操作    --- 1 --- 0001
    ErrorOptionsTwo = 1 << 1,               --- 2 --- 0010
    ErrorOptionsThree = 1 << 2              --- 4 --- 0100
};

因?yàn)樯厦娑x的枚舉值都為2的整數(shù)次冪值, 所以后面就可以使用位操作符 與(&)和或(|)來(lái)進(jìn)行選項(xiàng)的篩選

  ErrorOptions options = ErrorOptionsOne | ErrorOptionsTwo; //--- 0011
    if (options & ErrorOptionsOne) {// ErrorOptionsOne
        // 結(jié)束判斷后面
    }
    else if (options & ErrorOptionsTwo) {// ErrorOptionsTwo
        // ...
    }
    else {
        // ...
    }
  • 需要遍歷操作的時(shí)候, 盡量不要用C語(yǔ)言風(fēng)格的for遍歷, 而是采用OC的 for-in方式的快速枚舉, 當(dāng)然使用block的方式來(lái)遍歷未必不是更好的一種方式, 尤其是遍歷字典的時(shí)候.
  • 需要緩存的時(shí)候使用NSCache而不要使用NSArray或者NSDictionary, 因?yàn)槭褂肗SCache來(lái)進(jìn)行緩存當(dāng)內(nèi)存不足的時(shí)候系統(tǒng)會(huì)自動(dòng)清理緩存, 并且會(huì)首先清理緩存時(shí)間較長(zhǎng)的東西, 如果使用NSArray或者NSDictionary就沒(méi)有這個(gè)福利了
  • 不要在load方法里面執(zhí)行耗時(shí)的操作, 因?yàn)檫@個(gè)時(shí)候會(huì)阻塞當(dāng)前的線(xiàn)程, 如果是主線(xiàn)程被阻塞, 那么...就不能接受用戶(hù)的響應(yīng), 同時(shí)不要在load方法里面使用其他的類(lèi)和調(diào)用函數(shù), 因?yàn)檫@個(gè)時(shí)候程序是脆弱的, 有可能使用的class還沒(méi)有被加載到系統(tǒng)中來(lái), 當(dāng)然使用Foundation里面的NSString...這些是沒(méi)有問(wèn)題的
  • initialize這個(gè)方法在文檔中寫(xiě)明了是在第一次使用這個(gè)類(lèi)的時(shí)候才會(huì)調(diào)用一次(懶加載), 但是需要注意的是, 如果父類(lèi)中實(shí)現(xiàn)了initialize這個(gè)方法, 而子類(lèi)中沒(méi)有實(shí)現(xiàn)這個(gè)方法, 當(dāng)初始化子類(lèi)的時(shí)候, 父類(lèi)的這個(gè)initialize方法是會(huì)被調(diào)用多次的(消息轉(zhuǎn)發(fā)機(jī)制), 比如
      ZJChildClass類(lèi)里面沒(méi)有重寫(xiě)initialize方法, 但是他的父類(lèi)重寫(xiě)了, 所以在初始化ZJChildClass的時(shí)候, 父類(lèi)的initialize會(huì)被調(diào)用兩次, 即會(huì)打印兩條
    @interface ZJBaseClass : NSObject
    @end
    @implementation ZJBaseClass
    + (void)initialize {
        NSLog(@"加載一次-----");
    }
    @end
    @interface ZJChildClass : ZJBaseClass
    @end

所以一般都是這樣來(lái)重寫(xiě)initialize方法的, 保證只會(huì)像我們期望的那樣調(diào)用一次

    + (void)initialize {
        if (self == [ZJBaseClass class]) { /// 不能用 [self class]
            NSLog(@"加載一次-----");
       }
    }
  • 對(duì)只需要執(zhí)行一次的代碼使用dispatch_once, 這樣可以保證線(xiàn)程安全, 并且只執(zhí)行一次, 最常見(jiàn)的是用來(lái)實(shí)現(xiàn)單例
 + (instancetype)sharedInstance {
    static Object *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [self new];
    });
    return sharedInstance;
}
  • 多用GCD少用NSObject的一些performSelector等方法, 因?yàn)槭褂胮erformSelector這種方式可能會(huì)造成內(nèi)存泄漏, 一般情況下使用GCD都可以完成, 比如dispatch_after來(lái)實(shí)現(xiàn)延時(shí)后執(zhí)行
  • 使用NSTimer的時(shí)候要特別注意內(nèi)存泄漏的問(wèn)題, 因?yàn)镹STimer會(huì)持有目標(biāo)對(duì)象, 很容易造成循環(huán)引用的問(wèn)題, 也許你會(huì)想到在這個(gè)目標(biāo)對(duì)象的dealloc里面讓NSTimer失效(調(diào)用 invalidation 并且置為nil), 但是這根本就沒(méi)有用, 因?yàn)檠h(huán)引用的原因, 根本就不會(huì)調(diào)用dealloc方法, 所以在里面銷(xiāo)毀是沒(méi)有用的, 需要在對(duì)象被銷(xiāo)毀之前手動(dòng)銷(xiāo)毀計(jì)時(shí)器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末虏辫,一起剝皮案震驚了整個(gè)濱河市习寸,隨后出現(xiàn)的幾起案子佛纫,更是在濱河造成了極大的恐慌,老刑警劉巖冯乘,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拄衰,居然都是意外死亡络拌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)擂送,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悦荒,“玉大人,你說(shuō)我怎么就攤上這事嘹吨“嵛叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵蟀拷,是天一觀的道長(zhǎng)碰纬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)问芬,這世上最難降的妖魔是什么悦析? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮愈诚,結(jié)果婚禮上她按,老公的妹妹穿的比我還像新娘牛隅。我一直安慰自己炕柔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布媒佣。 她就那樣靜靜地躺著匕累,像睡著了一般。 火紅的嫁衣襯著肌膚如雪默伍。 梳的紋絲不亂的頭發(fā)上欢嘿,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音也糊,去河邊找鬼炼蹦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狸剃,可吹牛的內(nèi)容都是我干的掐隐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼钞馁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虑省!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起僧凰,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤探颈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后训措,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伪节,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡光羞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怀大。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狞山。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叉寂,靈堂內(nèi)的尸體忽然破棺而出萍启,到底是詐尸還是另有隱情,我是刑警寧澤屏鳍,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布勘纯,位于F島的核電站,受9級(jí)特大地震影響钓瞭,放射性物質(zhì)發(fā)生泄漏驳遵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一山涡、第九天 我趴在偏房一處隱蔽的房頂上張望堤结。 院中可真熱鬧,春花似錦鸭丛、人聲如沸竞穷。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘾带。三九已至,卻和暖如春熟菲,著一層夾襖步出監(jiān)牢的瞬間看政,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工抄罕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留允蚣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓呆贿,卻偏偏與公主長(zhǎng)得像嚷兔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榨崩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容