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

前言

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

熟悉OC

1,了解OC

所有的OC對(duì)象都是在堆中,我們?cè)跅V杏涗泴?duì)象分配的地址.

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

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

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

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

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

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

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

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

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

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

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

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

我們?cè)陬惖膬?nèi)部使用類型常量一般是這么去使用的:

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

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

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

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

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

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

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

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

對(duì)象,消息,運(yùn)行期

6,理解屬性

iOS開發(fā)時(shí)使用nonatomic,因?yàn)榫退闶怯胊tomic也不能完全保證線程安全,還需要采用更為深層的鎖定機(jī)制才可以,而且使用同步鎖對(duì)性能的消耗更大.例如,一個(gè)線程連續(xù)多次讀取某屬性值的過程中有別的線程在同事改寫該值,即便使用了同步鎖,還是會(huì)讀到不同的值.如果線程 A 調(diào)了 getter蔚出,與此同時(shí)線程 B 笆凌、線程 C 都調(diào)了 setter——那最后線程 A get 到的值,3種都有可能:可能是 B澎埠、C set 之前原始的值,也可能是 B set 的值雕擂,也可能是 C set 的值踊东。同時(shí),最終這個(gè)屬性的值斤斧,可能是 B set 的值早抠,也有可能是 C set 的值。Mac開發(fā)一般使用atomic是不會(huì)有性能個(gè)方面的考慮的.

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

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

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

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

  • 在初始化和dealloc方法中,全部使用實(shí)例變量.因?yàn)樽宇惡芸赡芨矊懺O(shè)置方法,所以如果使用屬性可能會(huì)有問題.

8,理解"對(duì)象等同性"

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

  • 相同的對(duì)象它的哈希值必定是相同的,但哈希值相同的對(duì)象未必是相同的.因?yàn)楣K惴ㄎ覀儾⒉磺宄?這里作者推薦了一種算法,在編寫哈希算法的時(shí)候,需要多次試驗(yàn),以便減少碰撞與降低運(yùn)算復(fù)雜程度之間取舍.

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

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

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

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

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

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

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

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

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

調(diào)用文件

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

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

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

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

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

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

11,理解objc_msgSend的作用

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

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


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

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

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

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

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

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

實(shí)現(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];
        //刪除冒號(hào)
        [key deleteCharactersInRange:NSMakeRange(key.length-1, 1)];
        //刪除set
        [key deleteCharactersInRange:NSMakeRange(0, 3)];
        //將第一個(gè)字母小寫
        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)試"黑盒方法"

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

14,理解類對(duì)象

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

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

接口與API設(shè)計(jì)

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

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

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

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

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

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

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

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

18,盡量使用不可變對(duì)象

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

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

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

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

  • 給方法命名
  • 要點(diǎn)

20,為私有方法名加前綴

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

21,理解OC的錯(cuò)誤模型

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

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

22,理解NSCopying協(xié)議

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

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

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

協(xié)議與分類

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

  • 如果有必要,可實(shí)現(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方法中對(duì)是否實(shí)現(xiàn)了方法的判斷進(jìn)行一次緩存:

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

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

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

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

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

26,勿在分類中聲明屬性

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

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

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

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

內(nèi)存管理

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

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

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

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

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

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

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

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

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

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

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

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

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

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

36,不要使用"retainCount"

塊與大中樞派發(fā)

37,理解"塊"這一概念

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

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

  • 用typedef重新定義塊類型

39,用handler塊降低代碼

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

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

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

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

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

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

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

43,掌握GCD以及操作隊(duì)列的使用時(shí)機(jī)

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

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

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

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

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

46,不使用dispatch_get_current_queue

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

系統(tǒng)框架

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

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

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

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

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

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

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

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

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

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

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

  • 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).
  • 可以擴(kuò)充NSTimer的功能,用塊來打破保留環(huán).不過,除非NSTimer將來在公共接口提供此功能,否則必須創(chuàng)建分類,將相關(guān)代碼實(shí)現(xiàn)加入其中.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蕊连,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子游昼,更是在濱河造成了極大的恐慌甘苍,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烘豌,死亡現(xiàn)場離奇詭異载庭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廊佩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門囚聚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人标锄,你說我怎么就攤上這事顽铸。” “怎么了料皇?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵谓松,是天一觀的道長。 經(jīng)常有香客問我践剂,道長鬼譬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任逊脯,我火速辦了婚禮优质,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘军洼。我一直安慰自己盆赤,他們只是感情好贾富,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牺六,像睡著了一般颤枪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淑际,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天畏纲,我揣著相機(jī)與錄音,去河邊找鬼春缕。 笑死盗胀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锄贼。 我是一名探鬼主播票灰,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼宅荤!你這毒婦竟也來了屑迂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤冯键,失蹤者是張志新(化名)和其女友劉穎惹盼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惫确,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡手报,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了改化。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掩蛤。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陈肛,靈堂內(nèi)的尸體忽然破棺而出盏档,到底是詐尸還是另有隱情,我是刑警寧澤燥爷,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站懦窘,受9級(jí)特大地震影響前翎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜畅涂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一港华、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧午衰,春花似錦立宜、人聲如沸冒萄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尊流。三九已至,卻和暖如春灯帮,著一層夾襖步出監(jiān)牢的瞬間崖技,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工钟哥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迎献,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓腻贰,卻偏偏與公主長得像吁恍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子播演,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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