iOS如何寫出高質(zhì)量的代碼筆記

前言

最近在學(xué)習(xí)這本書,Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個有效方法,我會記錄一些我不知道或者平時忽略的東西把它記下來.看了一部分感覺收獲還是挺大的.

熟悉OC

1,了解OC

所有的OC對象都是在堆中,我們在棧中記錄對象分配的地址.

只保存非對象類型的數(shù)據(jù),我們可以使用結(jié)構(gòu)體,避免了分配內(nèi)存和釋放內(nèi)存所造成的額外開銷.

2,在類的頭文件中,盡量少引入其他頭文件

將引入頭文件的時機,盡量延后,只有在真正去使用它的時候再去引入頭文件,這樣可以縮短編譯時間.如果一定要在頭文件使用到這個類,但并不需要去管他具體的細節(jié),我們可以使用向前聲明(@class)來告訴編譯器這是一個類.

有時無法使用向前聲明,而我們卻又需要遵循這個協(xié)議時,我們可以將它延后至延展時再去遵守.

協(xié)議最好寫成一個文件,委托協(xié)議是不需要去單獨寫一個文件的,協(xié)議中如果需要使用了其他類的話請使用向前聲明(@class)來標(biāo)識,導(dǎo)入類的頭文件請在實現(xiàn)文件導(dǎo)入.

3,多用字面量語法,少用與之等價的方法

NSString,NSNumber,NSArray,NSDictionary等創(chuàng)建時我們可以直接使用字面量語法來創(chuàng)建,如下圖所示:

而不是這樣子了來創(chuàng)建:

這樣子創(chuàng)建可讀性高,另外當(dāng)我們給數(shù)組中或者字典中傳入nil對象時,XCode會給我們警告,而如果使用第二種方法,當(dāng)object2為nil時,那這個數(shù)組就已經(jīng)創(chuàng)建完畢了,此時數(shù)組中就只有一個對象了.這樣子的異常在平時編碼中不是好確定的,如果我們用字面量去創(chuàng)建,異??赡芫秃冒l(fā)現(xiàn)的多了.

4盡量不要或者少使用宏這種預(yù)編譯命令,而是用類型常量代替

我們用宏來代替字符串,常量,url都是這么做的:

然而這樣子做是對宏的濫用,宏其實只是起到了替換的功能,他并沒有縮短代碼實際的運行時間,也沒有檢測機制,所以宏這種東西盡量的去少用.

我們在類的內(nèi)部使用類型常量一般是這么去使用的:

常量名前方要加k,static表示該變量僅在定義此變量的編譯單元中可見,注意要寫在實現(xiàn)文件頭部.

如果這個常量我們要在全局去使用,例如通知的名字:

在這里常量名前一般是需要添加類名的,避免全局使用時混淆,常量的定義一般是從右至左解讀,例如上面的意思是:EOCStringConstant是一個常量,而這個常量是一個指針,指向了一個字符串對象.extern關(guān)鍵字表示全局符號表中將會有一個名叫EOCStringConstant的符號.

5,用枚舉表示狀態(tài),選項,狀態(tài)碼

將int和NSIntger代表的狀態(tài)全部使用枚舉來代替,這樣的代碼可讀性更高,枚舉的名字應(yīng)該通俗易懂.

如果將傳遞給某個方法的選項表示為枚舉類型,而多個枚舉又可以同時使用,那么將各選項值定義為2的冪,以便通過按位或操作組合起來,也稱作位移枚舉.

在編寫枚舉時切記要使用NS_ENUM和NS_OPTIONS來定義枚舉,確保枚舉是用開發(fā)者所選的底層數(shù)據(jù)類型實現(xiàn)出來的.一般位移枚舉使用的是后者.

當(dāng)處理枚舉類型時要寫switch語句時,不要實現(xiàn)default分支,這樣加入新的枚舉時,編譯器會自動提示我們我們沒有實現(xiàn)這個枚舉.

對象,消息,運行期

6,理解屬性

iOS開發(fā)時使用nonatomic,因為就算是用atomic也不能完全保證線程安全,還需要采用更為深層的鎖定機制才可以,而且使用同步鎖對性能的消耗更大.例如,一個線程連續(xù)多次讀取某屬性值的過程中有別的線程在同事改寫該值,即便使用了同步鎖,還是會讀到不同的值.如果線程 A 調(diào)了 getter,與此同時線程 B 、線程 C 都調(diào)了 setter——那最后線程 A get 到的值,3種都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同時,最終這個屬性的值,可能是 B set 的值,也有可能是 C set 的值。Mac開發(fā)一般使用atomic是不會有性能個方面的考慮的.

類中對象的屬性,當(dāng)編譯器遇到了訪問成員變量的代碼時,他會將其替換成偏移量,表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠,當(dāng)然并不是簡單的這樣子處理的,否則會出現(xiàn)地址錯亂的問題,具體的剖析看下這篇文章,書上講的也不是很清晰.文章鏈接

7,對象內(nèi)部盡量直接訪問實例變量

  • 讀取數(shù)據(jù)時應(yīng)該直接通過實例變量來讀取,寫入數(shù)據(jù)時則應(yīng)該通過屬性來寫入.

直接訪問實例變量的速度快;直接訪問實例變量時,不會調(diào)用其設(shè)置方法,這就繞過了相關(guān)屬性所定義的內(nèi)存管理語義;直接訪問實例變量不會觸發(fā)KVO,當(dāng)然要看具體情況決定;通過屬性來讀寫可以在set&&get方法中打斷點,方便調(diào)試.

  • 在初始化和dealloc方法中,全部使用實例變量.因為子類很可能覆寫設(shè)置方法,所以如果使用屬性可能會有問題.

8,理解"對象等同性"

  • 我們在判斷對象是否是相等的時候,盡量不要用"=="這個操作符,有時判斷的結(jié)果并不是我們所期望的.因為該操作比較的是兩個指針本身,這里應(yīng)該使用"isEqual:"這個方法,當(dāng)然如果你要判斷的對象是字符串,字典,數(shù)組,他們作為NSObject的子類,都實現(xiàn)了相應(yīng)的方法,例如NSString的"isEqualToString:".這樣子效率會比較高一些.

  • 相同的對象它的哈希值必定是相同的,但哈希值相同的對象未必是相同的.因為哈希算法我們并不清楚,這里作者推薦了一種算法,在編寫哈希算法的時候,需要多次試驗,以便減少碰撞與降低運算復(fù)雜程度之間取舍.

這種做法既能保持高效率,又能使生成的哈希值在一定長度范圍內(nèi)
  • 不要盲目的逐個檢測每條屬性,在適當(dāng)?shù)沫h(huán)境下使用最優(yōu)解.例如判斷數(shù)組相等,我們可以先去判斷元素的個數(shù),再去逐個比較;例如比較兩個根據(jù)數(shù)據(jù)庫創(chuàng)建而來的只讀實例,我們可以直接對他的主鍵進行判斷.

  • 將對象放入集合中時,就不應(yīng)該再改變其中的哈希值了(改變內(nèi)容),否則會造成一些我們無法想到的奇怪問題.例如:

此時就會發(fā)現(xiàn)set中有兩個相同的元素了.

  • 這里有一個作者寫的判斷兩個對象是否相等的例子,可以參考學(xué)習(xí)一下:

首先判斷指針是否相等,再然后判斷屬性是否相等,若不為同一個類,則判斷交由超類執(zhí)行.

9,以"類族模式"隱藏實現(xiàn)細節(jié)

類族模式最大的好處就是用戶不用關(guān)心具體是如何去實現(xiàn)的,保持著接口的簡潔性,下面用一個例子來解釋一下吧.

Person類的頭文件
Person類的實現(xiàn)文件

我們可以看到,這邊的創(chuàng)建對象的類方法其中只需要傳入性別枚舉,我們就會自動的給他創(chuàng)造相應(yīng)的對象,并且在父類(Person)中,我們并沒有對doHouseWork這個對象方法添加實質(zhì)性的內(nèi)容.具體的內(nèi)容我們交給了Person的子類Man和Woman去分別實現(xiàn).這樣就可以做到在調(diào)用同一個方法時,卻執(zhí)行了不同的內(nèi)容,算是一種比較優(yōu)雅的寫法,常見于系統(tǒng)的一些類,如:NSArray等等.

調(diào)用文件

10,在既有類中使用關(guān)聯(lián)對象存放自定義數(shù)據(jù)

在繼承這種添加屬性不可行時我們才會使用"關(guān)聯(lián)對象這種做法",當(dāng)然這種行為應(yīng)該盡可能少的去使用,因為調(diào)試比較困難通??赡軙驗?保留環(huán)"等造成一些自己想象不到的問題,常用的地方比如在分類中動態(tài)的添加屬性.

關(guān)聯(lián)對象的策略類型

如果關(guān)聯(lián)對象成為了屬性,那么他就具備相應(yīng)的語義.

關(guān)聯(lián)對象的三個方法

這里需要注意的是,當(dāng)在設(shè)置關(guān)聯(lián)對象值時,我們通常使用靜態(tài)全局變量做鍵.因為若想用兩個鍵匹配到同一個值,則二者必須是完全相同的指針才可以!

11,理解objc_msgSend的作用

OC是一門動態(tài)語言,函數(shù)的調(diào)用并不是在編譯期決定的,類似如下圖所示:

OC向?qū)ο蟀l(fā)送消息的函數(shù):


第一個參數(shù)代表接收者,第二個參數(shù)代表選擇子,后續(xù)參數(shù)就是函數(shù)中的那些參數(shù),其順序不變.選擇子就是方法的名字,編譯器會轉(zhuǎn)換為如下函數(shù):

objc_msgSend函數(shù)會依據(jù)接收者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒?為了完成此操作該方法需要在接收者所屬的類中搜尋其"方法列表",如果能找到就跳至其實現(xiàn)代碼,如果找不到就沿著集成體系向上查找,最終依舊找不到的話就會執(zhí)行消息轉(zhuǎn)發(fā).

12,消息轉(zhuǎn)發(fā)

對象在收到一個無法解讀的消息是會怎么做呢?OC有一個消息轉(zhuǎn)發(fā)機制,第一步先征詢接收者看能否動態(tài)地添加方法來處理這個未知的選擇子,若不行則進入第二步看能不能被其他接收者處理.若依舊不行就只能走完整的轉(zhuǎn)發(fā)流程了.

這三種消息轉(zhuǎn)發(fā)有什么區(qū)別或者我們什么情況下去使用呢?

運行時處理消息我們使用第一步,動態(tài)方法解析,轉(zhuǎn)發(fā)給另一個對象我們使用第二部,被援接收者,需要轉(zhuǎn)發(fā)給多個對象時,我們使用第三步,完整的消息轉(zhuǎn)發(fā),這里,我根據(jù)書本上寫了一個例子,用來實現(xiàn)當(dāng)不實現(xiàn)屬性的set和get方法時,如何將屬性值存入字典,并從字典中讀取.

實現(xiàn)文件:

    #import "Human.h"
    #import <objc/runtime.h>

    @interface Human ()

    @property (nonatomic, strong)NSMutableDictionary *dict;

    @end

    @implementation Human

    @dynamic name;

    - (instancetype)init{
        
        if (self = [super init]) {
            _dict = [NSMutableDictionary new];
        }
        return self;
    }

    + (BOOL)resolveInstanceMethod:(SEL)sel{

        NSString *selectorString = NSStringFromSelector(sel);
        if ([selectorString hasPrefix:@"set"]) {
            class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
        } else {
            class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
        }
        return YES;
    }

    - (id)forwardingTargetForSelector:(SEL)aSelector{
        return self;
    }

    //get 函數(shù)
    id autoDictionaryGetter(id self,SEL _cmd){
        Human *typedSelf = (Human*)self;
        NSMutableDictionary *humanName = typedSelf.dict;
        NSString *key = NSStringFromSelector(_cmd);
        return [humanName objectForKey:key];
    }

    //set 函數(shù)
    void autoDictionarySetter(id self, SEL _cmd, id value){
        Human *typedSelf = (Human*)self;
        NSMutableDictionary *humanName = typedSelf.dict;
        NSString *selectorString = NSStringFromSelector(_cmd);
        NSMutableString *key = [selectorString mutableCopy];
        //刪除冒號
        [key deleteCharactersInRange:NSMakeRange(key.length-1, 1)];
        //刪除set
        [key deleteCharactersInRange:NSMakeRange(0, 3)];
        //將第一個字母小寫
        NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
        [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
        if (value) {
            [humanName setObject:value forKey:key];
        } else {
            [humanName removeObjectForKey:key];
        }
    }
    @end

13,用"方法調(diào)配技術(shù)"調(diào)試"黑盒方法"

書上的例子實質(zhì)上就是使用運行時交換方法,在新方法中增加一些日志等功能,切記不可濫用.

14,理解類對象

類本身其實是一個結(jié)構(gòu)體,結(jié)構(gòu)體的第一個變量是isa指針,該變量定義了對象所屬的類,結(jié)構(gòu)體里面還有super_class定義了本類的超類.類對象所屬的類型,也就是isa指向的類型是另外一個類,被稱為元類,用來描述類對象,類方法就位于此處,每個類僅有一個類對象,這里可以變相的理解為單例,而每個類對象僅有一個與之相關(guān)的元類.假設(shè)有個someClass從NSObject中繼承而來,如下圖所示:

在類繼承體系中查詢類型信息是,使用"isMemberOfClass:"判斷對象是否是某個類的實例,使用"isKindOfClass:"判斷是否為某個類或其派生類的實例,之所以不使用"=="來判斷,因為有的類實現(xiàn)了消息轉(zhuǎn)發(fā),使用"class"返回的是發(fā)起代理的對象而非接受代理的對象.

接口與API設(shè)計

15,用前綴避免命名空間沖突

  • 類名,方法名前應(yīng)加上公司或項目前綴,最好為三位英文字母.
  • 若自己開發(fā)的程序庫中使用了第三方庫,應(yīng)為其添加前綴.

16,提供"全能初始化方法"

  • 在類中提供一個全能初始化方法,并在文檔中指出,其他初始化方法應(yīng)該調(diào)用此方法.

    - (instancetype)initPerson{
          if (self = [super init]) {
              _name = @"Peter";
          }
          return self;
      }
    
      - (instancetype)init{
          return [self initPerson];
      }
    
  • 如果全能初始化方法不同,那么在子類中應(yīng)該重寫這個方法的內(nèi)容.
    *如果超類的初始化方法不適用子類,我們應(yīng)該在子類中重寫并拋出異常!

    - (instancetype)init{
          
          @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"必須用initPerson來初始化" userInfo:nil];
      }
    

17,實現(xiàn)description方法

  • 自己自定義的類實現(xiàn)description方法,返回一個有意義的字符串.
  • 我們調(diào)試的時候可以重寫這個方法

18,盡量使用不可變對象

  • 要盡量創(chuàng)建不可變對象

  • 若某屬性僅在外部讀取,內(nèi)部用來修改,我們可以在頭文件將它定義為只讀,在擴展里可以重新定義為可讀可寫.

  • 不要把可變的集合作為屬性公開,而應(yīng)該提供相應(yīng)的方法使其修改可變集合

19,使用清晰而協(xié)調(diào)的命名方式

  • 給方法命名
  • 要點

20,為私有方法名加前綴

  • 給私有方法名前加上前綴,以便于將其和共有方法區(qū)分開,避免隨意改動公共方法.

21,理解OC的錯誤模型

  • 只有發(fā)生了嚴重錯誤時我們才應(yīng)該去使用異常

  • 在錯誤不嚴重時,我們可以用委托方法來處理錯誤,也可以將錯誤放在NSError對象里,經(jīng)過輸出參數(shù)傳遞給調(diào)用者.

22,理解NSCopying協(xié)議

  • 如果想令自己所寫的對象具有拷貝功能,那么需要實現(xiàn)NSCopying協(xié)議,我們copy的時候需要實現(xiàn)copyWithZone:這個方法,實現(xiàn)不可變->可變的copy需要實現(xiàn)NSMutableCopying協(xié)議中的mutableCopyWithZone:這個方法.
  • Foundation框架中的所有集合類在默認情況下都執(zhí)行淺拷貝

  • 復(fù)制對象時一般情況下盡力執(zhí)行淺拷貝

  • 如果需要深拷貝.可以另外寫一個深拷貝的方法:

協(xié)議與分類

23通過委托與數(shù)據(jù)源協(xié)議進行對象間通訊

  • 如果有必要,可實現(xiàn)含有位段的結(jié)構(gòu)體,將委托協(xié)議是否能響應(yīng)相關(guān)協(xié)議方法緩存到其中

      @interface MRCLoginController (){
          struct {
              unsigned int didReceiveData       :  1;
              unsigned int didFailWithError     :  1;
              unsigned int didUpdateProgressTo  :  1;
          }_delegateFlages;
      }
    

在協(xié)議的delegate的set方法中對是否實現(xiàn)了方法的判斷進行一次緩存:

調(diào)用的時候直接從緩存判斷即可

24,將類的實現(xiàn)代碼分散到便于管理的數(shù)個分類之中

  • 使用分類機制,將類的實現(xiàn)代碼劃分為易于管理的小塊.
  • 將私有的方法應(yīng)該歸為private分類中,可以有效地隱藏細節(jié)

25,總是為第三方類的分類名稱增加前綴

  • 給第三方添加分類時總應(yīng)該將類名以及方法名添加前綴,避免覆寫.

26,勿在分類中聲明屬性

  • 把封裝數(shù)據(jù)所用的屬性全都放在主接口文件中
  • 在擴展之外的分類中,盡量只定義方法,不要定義屬性

27,使用"class-continuation分類",隱藏實現(xiàn)細節(jié)

  • 通過擴展向類中增加實例變量
  • 如果某屬性在外部為可讀,內(nèi)部又想寫入,可在擴展中將屬性改為可讀寫
  • 將私有方法聲明在擴展中,但我覺得這樣子做沒有必要
  • 在延展中遵從協(xié)議

28,通過協(xié)議提供匿名對象

內(nèi)存管理

29,理解引用計數(shù)

  • 保留和釋放引用計數(shù)只能說引用計數(shù)遞增或者遞減
  • 屬性存取方法中應(yīng)該先保留舊值再釋放新值

30,以ARC簡化引用計數(shù)

  • ARC只負責(zé)管理OC對象的內(nèi)存,尤其要注意:CoreFoundation對象和由malloc()函數(shù)分配在堆中的內(nèi)存不歸ARC管理,開發(fā)者需要適時調(diào)用CFRetainCFRelease.

31,在dealloc方法中只釋放引用并解除監(jiān)聽

  • 若對象持有文件描述符等系統(tǒng)資源,應(yīng)該編寫一個方法來釋放此種資源,若沒有調(diào)用應(yīng)該去主動提示開發(fā)者

32,編寫"異常安全代碼"時,留意內(nèi)存安全問題

  • 捕獲異常時,一定要將try塊內(nèi)創(chuàng)建的對象清理干凈
  • 默認情況下,ARC不生成處理異常所需要的代碼,因為在OC中,只有當(dāng)應(yīng)用程序因為異常而終止時此時才會拋出異常,但此時處理異常也沒有太大的意義.
  • Objective-C++模式下我們應(yīng)該編寫對異常的處理代碼,此時性能損失不大
  • 默認情況下ARC不生成安全處理異常所需要的代碼.開啟編譯器標(biāo)志后,可以生成這種代碼,但會導(dǎo)致應(yīng)用程序變大,降低運行效率

33,以弱引用避免保留環(huán)

  • unsafe_unretainedweak的區(qū)別,對象被回收以后,unsafe_unretained依然指向被釋放的對象,而weak則指向了nil.

34,以"自動釋放池塊",降低內(nèi)存峰值

  • 自動釋放池排布在棧中,對象收到autorelease后,系統(tǒng)將其放入最頂端的池里
  • 合理運用自動釋放池,能夠降低應(yīng)用程序的內(nèi)存峰值
自動釋放池會等下一次事件循環(huán)時才釋放,在這里加一個可以有效避免內(nèi)存峰值,看需求
  • 一般不需要去關(guān)注自動釋放池這個東西

35,用"僵尸對象"調(diào)試內(nèi)存管理問題

  • 系統(tǒng)回收對象時,可以不將其真的回收,而是轉(zhuǎn)化為僵尸對象.通過Scheme中的Enable Zombie Objects選項進行配置
  • 釋放時,系統(tǒng)會修改對象的isa指針,令其指向特殊的僵尸類,從而使該對象變?yōu)榻┦瑢ο?僵尸可以響應(yīng)所有的選擇子,響應(yīng)方式:打印一條包含消息內(nèi)容及其接收者的信息,然后終止應(yīng)用程序

36,不要使用"retainCount"

塊與大中樞派發(fā)

37,理解"塊"這一概念

  • 塊是C,CPP.OC中的語法閉包;
  • 塊能夠捕獲他聲明范圍中的對象,使用成員變量時也能捕獲self,注意保留環(huán)問題
  • 塊在聲明的時候一般是分配在內(nèi)存中的棧中的,過了使用范圍后,可能會被回收,因為棧是具有內(nèi)存自動回收機制的,此時我們可以將其拷貝到堆中,那么塊就具有像對象那樣的內(nèi)存管理機制了.

38,為常用的塊類型創(chuàng)建typedef

  • 用typedef重新定義塊類型

39,用handler塊降低代碼

  • 在創(chuàng)建對象時,可以使用內(nèi)聯(lián)的handler塊將相關(guān)業(yè)務(wù)邏輯一并聲明
  • 有多個實例需要監(jiān)控時,如果采用委托模式,經(jīng)常需要根據(jù)傳入的對象來回切換,而如果采用handler塊來實現(xiàn),那么可以將塊與相關(guān)對象放在一起
  • 設(shè)計API時如果用到了handler塊,那么可以增加一個參數(shù),用來決定這個塊在什么線程下執(zhí)行

40,用塊引用其所屬對象時不要出現(xiàn)保留環(huán)

  • 在調(diào)用者使用了weakSelf時,我們也應(yīng)該在合適的地方解除保留環(huán),不能講所有的責(zé)任都推給調(diào)用者

41,多用派發(fā)隊列,少用同步鎖

  • 使用同步隊列及柵欄塊,可以令同步行為更加高效.例如屬性的讀取可以設(shè)為并行,而寫入則用柵欄塊來實現(xiàn),并發(fā)隊列如果發(fā)現(xiàn)接下來需要執(zhí)行的是一個柵欄塊,那么他會等所有的并發(fā)塊執(zhí)行完畢以后再去執(zhí)行這個柵欄塊

42,多用GCD,少用performSelector系列方法

  • performSelector系列方法在內(nèi)存管理方面容易有疏失,他無法確定要執(zhí)行的選擇子到底是什么,因而ARC編譯器也就無法插入適當(dāng)?shù)膬?nèi)存管理方法.
  • performSelector系列方法所能處理的選擇子太過局限了,選擇子的返回值類型及發(fā)送給方法的參數(shù)個數(shù)都將受到限制
  • 如果想把一個任務(wù)放到另外一個線程上執(zhí)行,最好使用大中樞派發(fā)機制

43,掌握GCD以及操作隊列的使用時機

  • 解決多線程與任務(wù)管理問題時大中樞派發(fā)隊列并非唯一方案
  • 操作隊列NSOperation是一個更為重量的OC對象,他與大中樞派發(fā)相比具有以下特點:可以取消未執(zhí)行的操作,指定操作間的依賴關(guān)系,可以通過KVO觀察NSOperation的一些屬性,指定操作的優(yōu)先級

44,通過Dispatch Group機制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

  • 一系列任務(wù)可歸于一個dispatch group中,開發(fā)者可以在這組任務(wù)完成時獲得通知
  • 通過dispatch group,可以在并發(fā)式派發(fā)隊列中同時執(zhí)行多項任務(wù).此時GCD會根據(jù)系統(tǒng)資源狀況來調(diào)度執(zhí)行這些并發(fā)執(zhí)行的任務(wù)

45,使用dispatch_once來執(zhí)行只需運行一次的線程安全的代碼

  • 標(biāo)記應(yīng)該聲明在static或者global作用域中,這樣的話,在把只需執(zhí)行一次的塊傳給dispatch_once函數(shù)時,傳進去的標(biāo)記也是相同的

46,不使用dispatch_get_current_queue

  • 派發(fā)概念是按照層級來組織的,無法單用某個隊列對象來描述"當(dāng)前隊列"這一概念

系統(tǒng)框架

47,熟悉系統(tǒng)框架

  • 知道有這么回事就行了,想仔細研究還應(yīng)該具有C語言基礎(chǔ)

48,多用塊枚舉,少用for循環(huán)

  • "塊枚舉法"本身就能通過GCD并發(fā)執(zhí)行遍歷操作,無需另行編寫代碼.

49,對自定義其內(nèi)存管理語義的集合使用無縫橋接

  • __bridge:ARC仍具有這個OC對象的所有權(quán)
  • __bridge_retained:ARC交出對象的所有權(quán)
  • __bridge_transfer:將C數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為OC對象,同時令A(yù)RC獲取對象所有權(quán)
  • CoreFoundation層面創(chuàng)建集合時,可以指定許多回調(diào)函數(shù),這些函數(shù)表示此集合該如何處理其元素.然后可以運用無縫橋接技術(shù),將其轉(zhuǎn)換成具備特殊管理語義的OC集合

50,構(gòu)建緩存時,選用NSCache而非字典

  • NSCache可以提供優(yōu)雅的自動刪減功能,而且是線程安全的,與字典不同他不會拷貝鍵
  • 可以給NSCache設(shè)置上限,用以限制緩存中的對象的總個數(shù)及總成本,這些尺度定義了緩存刪減其中對象的時機,這個上限僅僅是起到了指導(dǎo)作用,不要把它當(dāng)作硬限制
  • NSPurgeableDataNSCache搭配使用,可以實現(xiàn)自動清除數(shù)據(jù)的功能,也就是說,當(dāng)NSPurgeableData對象所占內(nèi)存為系統(tǒng)所丟失時,該對象自身也會從緩存中移除

51,精簡initialize與load的實現(xiàn)代碼

  • 在加載階段,如果類實現(xiàn)了load方法,那么系統(tǒng)就會調(diào)用它.分類里也可以定義此方法,類的load方法要比分類中的先調(diào)用,load方法不參與覆寫機制.
  • 首次使用某個類之前,系統(tǒng)會向其發(fā)送initialize消息,此方法遵從覆寫機制,所以需要在里面判斷當(dāng)前初始化的是哪個類.
  • loadinitialize中應(yīng)該盡量少寫代碼
  • 無法在編譯期設(shè)定的全局常量,可以放在initialize方法里初始化

52,別忘了NSTimer會保留其目標(biāo)對象

  • NSTimer對象會保留其目標(biāo),直到計時器本身失效為止,調(diào)用invalidate方法可令計時器失效,另外,一次性的計數(shù)器在觸發(fā)完任務(wù)之后也會失效.
  • 反復(fù)執(zhí)行任務(wù)的計時器,很容易引入保留環(huán),如果這種計時器的目標(biāo)對象又保留了計時器本身,那肯定會導(dǎo)致保留環(huán).
  • 可以擴充NSTimer的功能,用塊來打破保留環(huán).不過,除非NSTimer將來在公共接口提供此功能,否則必須創(chuàng)建分類,將相關(guān)代碼實現(xiàn)加入其中.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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