編寫高質(zhì)量代碼--第一章

第一條 Object-C語言的起源

  1. oc是面向?qū)ο笳Z言
  2. 雖然oc是面向?qū)ο蟮拇ビ祝菍Ρ萰ava c++這類面向?qū)ο蟮恼Z言還是略有差別:
    1>java c++的面向?qū)ο笳Z言使用的是“函數(shù)調(diào)用”
    2>而oc的面向?qū)ο笳Z言使用的是“消息結(jié)構(gòu)”
  3. oc的消息型語言是從Smalltalk演化而來的柜蜈,Smalltalk是消息型語言的鼻祖
  4. 消息結(jié)構(gòu)和函數(shù)調(diào)用在代碼上的區(qū)別:
    //這是oc的消息結(jié)構(gòu)
    NSObject *obj = [[NSObject alloc] init];
    [obj perform:parm1 parm:parm2];
    //這是c++的函數(shù)調(diào)用
    Object *obg = new Object;
    obj->perform(parm1, parm2);
    上述的就是寫的語法上的區(qū)別豆同,一個是通過:锦爵,:來的辐脖,另一個是直接最后括號里作為參數(shù)傳過去的
  5. 消息結(jié)構(gòu)的語言:其運行時執(zhí)行的代碼由運行環(huán)境來決定(正是這個特性才有我們平時說的runtime)
  6. 函數(shù)調(diào)用的語言:其運行時執(zhí)行的代碼由編譯器決定(在編譯時期就決定了執(zhí)行的代碼。)
  7. 總結(jié)5僵娃,6概作,oc在編譯時期并不決定時期執(zhí)行的代碼,比如歌函數(shù)只有聲明默怨,沒有具體實現(xiàn)在oc中就可以變易通過讯榕,只有在運行時如果運行環(huán)境發(fā)現(xiàn)沒有具體實現(xiàn)的時候才會報錯。而函數(shù)調(diào)用語言如果只聲明沒有具體實現(xiàn)匙睹,在編譯時期就會報錯愚屁。
  8. 多態(tài):疑問,如果4中的c++代碼痕檬,是個多態(tài)的話怎么辦集绰,不是說c++這類語言是在編譯的時候就要決定調(diào)用那個實現(xiàn)嗎,這個多態(tài)怎么辦谆棺,答案是c++這類代碼,如果是多態(tài)的話,比較特殊改淑,也是在運行時起決定的碍岔,在運行時期通過“虛方法表”來查出到底該執(zhí)行那個函數(shù)的實現(xiàn)。然而對于oc這類消息型語言朵夏,不管你是不是多態(tài)我都是在運行時期才去查找要執(zhí)行的方法蔼啦,這就是為什么咱們7中所說的如果只聲明不實現(xiàn)在編譯時期不會報錯的原因
  9. Object-C是為c語言添加了面向?qū)ο筇匦缘腸語言的超集的語言

第二條 在類的頭文件中盡量少引入其他頭文件

  1. 什么時候使用#import什么時候@class(向前聲明)


    image.png

    如上圖截圖所示,我在person類中的頭文件需要加入一個employer的類的屬性:這時候在編譯的時候仰猖,我們只需要知道有一個類名叫做YTEmployer就行了捏肢,不需要知道YTEmployer里面的具體實現(xiàn)細節(jié),所以上面的#import "YTEmployer.h"就顯得又點引入的太多了饥侵,所以優(yōu)雅的做法應該是只聲明一下該類就行了鸵赫,即使用@class "YTEmployer.h"就行了,這種使用@class的行為叫做“向前聲明”躏升。
    ps:所以為了提高編譯時的效率辩棒,盡量能不在.h中使用全部引入的#import就少使用,對于具體細節(jié)的實現(xiàn)放在.m中在導入
    ps:也就是將引入頭文件的時機盡量延后膨疏,只在確有需要的時候才引入

  2. 1中的介紹我們知道一睁,@class增加了編譯的效率,但是@class相比于#import還有一個好處佃却,請看下面如果使用#import的話:
    在YTPerson中需要知道雇員的屬性者吁,導入了YTEmployer.h


    image.png

    在YTEmployer.h中需要有一個增加雇員的方法,導入了YTPerson.h


    image.png

    如上述這樣怎么辦饲帅,這個時候你去編譯的時候复凳,如果編譯YTPerson需要引入YTEmployer.h,如果編譯YTEmployer.h需要引入YTPerson洒闸,所以假如我們使用了上述的#import分別進行導入染坯,這樣在解析其中一個頭文件的時候編譯器發(fā)現(xiàn)他引入了另一個頭文件,然后去編譯另一個發(fā)現(xiàn)另一個也引入這個頭文件丘逸,這樣雖不像使用#include能造成死循環(huán)单鹿,但是這樣的使用會使這兩個文件永遠都有一個無法正確編譯,通過上面的截圖你也能看出來 深纲,總有一個會報錯仲锄。
    so,此時我們就使用@class就會避免這種循環(huán)引用的問題,如下:
    image.png

    image.png

    這樣就沒問題湃鹊。你看到截圖中兩個我都用了@class儒喊。其實如果僅僅是為了編譯能通過,其中一個使用@class就行了币呵,就不會循環(huán)引用了怀愧,但是學習了1侨颈,我們要盡量延后引入,所以兩個都應該使用@class

  3. 學習了1芯义,2我們知道在.h中盡量使用@class哈垢,而不是@import,但是有沒有在.h中必須要要引入頭文件的呢扛拨?答案是:有耘分,例如:
    1》情況1:自定義的這個類繼承自父類,.h中必須引入绑警,這個毋庸置疑(YTSonPerson 繼承自YTPerson)


    image.png

    2》情況2:如果我們這個類要遵從某個協(xié)議求泰,也必須使用#import,使用@class只能知道說有這個名字的協(xié)議存在计盒,但是卻不知道這個協(xié)議里面有什么方法渴频,但是我們的編譯器在此時是需要知道這個協(xié)議有什么方法的:
    YTEmployer中定義了協(xié)議:


    image.png

    YTPerson中要遵從這個協(xié)議:
    image.png

    所以此時我們就也必須引入頭文件使用#import。

    但是對于情況2章郁,我們也就是只是必須知道YTEmployer協(xié)議中的方法枉氮,對于YTEmployer其他全局具體內(nèi)容也是不必知道的,我們就使用#import顯的也不是十分優(yōu)雅暖庄,所以“我們應該把協(xié)議單獨放到一個文件中來寫”:
    總結(jié):
    1》除非有必要聊替,否則不要引入頭文件。一般來說培廓,應在某個類的頭文件中使用向前聲明來提及別的類惹悄,并在實現(xiàn)文件中引入那些類的頭文件。這樣做可以盡量降低類之間的耦合
    2》有時無法使用向前聲明肩钠,比如要聲明某個類遵循一項協(xié)議泣港。這種情況下,盡量把“該類遵循某協(xié)議”的這條聲明移至“class-continuation分類”中价匠。如果不行的話当纱,就把協(xié)議單獨放在一個頭文件中,然后將其引入

第三條 多用字面量語法踩窖,少用與之等價的方法

1. NSString 字符串字面量語法
image.png
2. 字面量數(shù)值
image.png
3. 字面量數(shù)組

image.png

id obj1, obj2, obj3;
NSArray *arrayA = [NSArray arrayWithObjects:obj1,obj2,obj3, nil];
NSArray *arrayB = @[obj1, obj2, obj3];

問題:假如obj2是nil坡氯,來比較兩種方式的區(qū)別
1. 首先兩種方式都會報錯崩潰
2. arrayA這中方式,數(shù)組arrayA已經(jīng)被創(chuàng)建出來了洋腮,并且obj1已經(jīng)被添加進去了箫柳,到了obj2發(fā)現(xiàn)obj2是nil,就會報錯啥供。
3. arrayB這種方式悯恍,他的效果相當于先創(chuàng)建數(shù)組,然后再把中括號里面的所有對象都加到這個數(shù)組中伙狐,他這種方式在創(chuàng)建數(shù)組的時候就會檢查是否有nil涮毫,那么檢查到obj2為nil瞬欧,直接這個數(shù)組就不創(chuàng)建了
4. 綜上,還是字面量數(shù)組方式比較好一點窒百,因為數(shù)組中有nil本身就是一個bug黍判,一個錯誤,字面量可以在未創(chuàng)建出數(shù)組之前就判斷有nil篙梢。報錯,比arrayA的方式要好榴啸。比正常方式更加安全

4.字面量字典
image.png

同樣也會有值為nil的問題劝堪,這個道理和字面量數(shù)組是一模一樣的

字面量語法的局限性

1.上面我們說的這些類都可以使用字面量語法逼友,但是如果我們自定義這些類的“子類”,這些“子類”不能使用字面量語法,盡管使用了也會正常運行編譯妄呕,但是回報警告
“Incompatible pointer types initializing 'SonNSString *' with an expression of type 'NSString *'”
“Incompatible pointer types initializing 'SonArray *' with an expression of type 'NSArray *'”
所以不建議我們對這些類的子類使用字面量語法


image.png

2.使用字面量語法創(chuàng)建出來的都是不可變的--"Incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'"
如果想用字面量語法創(chuàng)建出可變的


image.png
總結(jié)
  1. 應該使用“字面量”語法來創(chuàng)建字符串,數(shù)值嗽测,數(shù)組绪励,字典。與創(chuàng)建此類對象的常規(guī)方法相比唠粥,這么做更加簡明扼要
  2. 應該通過取下標操作來訪問數(shù)組下標或字典中的鍵對應的元素
  3. 用字面量語法創(chuàng)建數(shù)組或字典時疏魏,若值中有nil,則會拋出異常晤愧。因此無比確保值里不含nil大莫。(即使是常規(guī)方法有nil也會拋出異常)

因此使用字面量語法1,簡明扼要 2官份,對于有nil值只厘,數(shù)組和字典來說字面量語法更安全。所以建議“多用字面量語法舅巷,少用與之等價的方法”

第四條 多用類型常量羔味,少用#define預處理指令

Object-C定義常量的三種方式

  1. 第一種
    #define Animation_Const 0.6
  2. 第二種
    static const NSInteger kAnimation_Const = 0.3;
  3. 第三種
    在.h 中
    extern NSString *const Animation_Notification;
    在.m中
    NSString *const Animation_Notification = @"value";

1這種方式,“不建議使用”钠右,這樣定義出來的常量不包含類型信息赋元,可讀性不強,編譯器針對這種方式做的就是在編譯之前把引用到的“ Animation_Const”這個東西替換為未0.6爬舰,其他的不會做人和操作们陆。這樣的話如果中途有人改了這個常量值,編譯器也不會警告情屹,將導致程序中不同的地方常量的值不一樣

2這種方式坪仇,比較好,但是僅僅使用與定義在.m中垃你,這種方式如果僅僅是在自己的編譯單元(一般都是.m是自己的一個獨立編譯單元)中使用的話用這個比較好
包含類型信息椅文,清楚的描述了常量的含義喂很。并且我們使用了static和const常量,因此中途如果有人試圖修改該常量編譯器就會報錯皆刺,因此相比較于1更加安全

3這種方式少辣,剛才我們說2的好處,但是2用在編譯單元中也就是.m中的時候比較好羡蛾,但是有些時候我就是需要外部能夠訪問到我的這個常量怎么辦漓帅,就是用3種方式,在.h中聲明痴怨,在.m中定義忙干,這樣一舉兩得。
---使用extern關(guān)鍵字來聲明的全局常量浪藻,會出現(xiàn)在全局符號表中
----編譯器再看到extern關(guān)鍵字的時候捐迫,編譯器就能明白如何在引入此頭文件的代碼中處理該常量了,這個extern關(guān)鍵字就是告訴編譯器爱葵,在全局符號表中將會有一個名叫Animation_Notification的符號施戴,也就是說編譯器無需查看其定于你,就允許代碼使用此常量萌丈,因為他知道赞哗,當連接成二進制文件之后,肯定能找到這個常量

第五條 用枚舉表示狀態(tài)浓瞪、選項懈玻、狀態(tài)碼

1.第一種定義枚舉:

image.png

這種方式下面定義枚舉變量的時候需要:
enum SocketConnectState state = SocketConnectStateDisConnected;
這樣每次都需要使用那個enum,不太簡潔乾颁,怎么才能不每次都適用enum呢涂乌,看第二種方式。
2.第二種定義枚舉
image.png

這種方式下面定義枚舉變量的時候就只需要:
ConnectState state = SocketConnectStateDisConnected;
| 上面這兩種方式都沒有定義枚舉的“類型”英岭,編譯器會為枚舉分配一個獨有的編號湾盒,從0開始,每個枚舉遞增1诅妹,但是這個枚舉具體類型取決于編譯器罚勾,不過其“二進制位”的個數(shù)必須能完全表示下枚舉編號才行,前例中因為只有三個枚舉吭狡,所以最大也就是2尖殃,即0x10就能夠表示,所以此枚舉類型位char就可以了划煮,如果枚舉種類更多了送丰,那么就可能不是char了,可能就是int或者什么的了
3-1. 第三種-1定義枚舉
image.png

前面1弛秋,2兩種器躏,都沒有聲明枚舉類型俐载,第三種直接聲明了枚舉的數(shù)據(jù)類型。并且還可以設(shè)置枚舉的起始值登失。
3-2. 第三種-2定義枚舉
image.png

如果定義的這些選項可以彼此“組合”遏佣,就更應該使用這種方式了。各選項之間可以通過“按位或操作符”來組合揽浙,請看:
image.png

因此EnumNumber num = EnumTwo | EnumThree;就相當于
0 0 0 0 1 1 因為進行了或操作嘛状婶,
因此在下面判斷的時候,就是只有這個枚舉值得位一樣就可以進入馅巷,所以:
image.png

這種3-2這種枚舉定義方式系統(tǒng)庫在非常頻繁的使用

4.第四種 定義枚舉


image.png

使用的這種“NS_ENUM”太抓,“NS_OPTIONS”系統(tǒng)宏,這些宏具備向后兼容能力令杈,如果目標平臺的編譯器支持新標準就是用新式語法,否則改用舊式語法
NS_ENUM宏如果是新式語法碴倾,上面的定義就相當于:


image.png

NS_OPTIONS宏針對看是否支持c++編譯
image.png

1》如果不按c++編譯逗噩,上面的枚舉展開其實和NS_ENUM的展開是一樣的,即:

image.png

為什么呢?--原因在于跌榔,用“按位或運算”來操作兩個枚舉值時异雁,c++編譯模式的處理辦法與非c++模式不一樣的。用“或”運算操作兩個枚舉值時僧须,c++認為運算結(jié)果的數(shù)據(jù)類型應該是枚舉的底層數(shù)據(jù)類型纲刀,也就是NSUInteger,并且c++還不允許將這個底層類型“隱式轉(zhuǎn)換”為枚舉類型本身,針對上面的展開担平,加入我們這樣來使用:
EnumNumber num = EnumFour | EnumSenven;
若編譯器是按c++模式編譯的(也可能是按object-c++模式編譯)示绊,那么上面的這句代碼就會報錯"不可能初始化這個變量類型"
所以我們?nèi)绻胱円咨厦孢@句代碼,就要將“按位或操作”的結(jié)果進行“顯示轉(zhuǎn)換”(因為c++編譯不能隱式轉(zhuǎn)換)為枚舉類型(EnumNumber類型)暂论。
總結(jié)面褐,因為我們不想每次都進行一行顯示類型轉(zhuǎn)換,所以我們應該用NS_OPTIONS宏取胎。使用NS_OPTIONS就是擔心我們?nèi)绻凑誧++模式編譯了展哭,如果還沒有顯示轉(zhuǎn)換就會報錯,如果顯示轉(zhuǎn)換每次還比較麻煩闻蛀,所以直接用了NS_OPTIONS宏
1》凡是需要按位操作的都應該使用NS_OPTIONS
2》若是枚舉不需要組合也就是不需要按位操作什么的使用NS_ENUM就可以了

5.在switch中使用枚舉
在switct如果食用了枚舉我們最好不要使用default分之匪傍,為什么呢,加入我們不適用default分之觉痛,我們switch中如果少了那個枚舉役衡,編譯就會警告,但是如果我們加了default編譯器就不會警告秧饮,所以使用枚舉在switch表示狀態(tài)機的時候最好不要加default


image.png
枚舉本節(jié)全文總結(jié)

1.應該用枚舉來表示狀態(tài)機的狀態(tài)映挂,傳遞給方法的選項以及狀態(tài)碼等值泽篮,給這些值起個易懂的名字
2.如果傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用柑船,那么就將各選項值定義為2的冪帽撑,以便通過按位或操作將其組合起來
3.用NS_ENUM 與NS_OPTIONS宏定義來定義枚舉類型,并指明其底層數(shù)據(jù)類型鞍时。這樣做可以確保枚舉是開發(fā)者所選的底層數(shù)據(jù)類型實現(xiàn)出來的亏拉,而不會采用編譯器所選的類型
4.在處理枚舉類型的switch語句中不要實現(xiàn)default分之。這樣的話逆巍,加入新枚舉之后編譯器就會提示開發(fā)者:switch語句并未處理所有枚舉及塘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锐极,隨后出現(xiàn)的幾起案子笙僚,更是在濱河造成了極大的恐慌,老刑警劉巖灵再,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肋层,死亡現(xiàn)場離奇詭異,居然都是意外死亡翎迁,警方通過查閱死者的電腦和手機栋猖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汪榔,“玉大人蒲拉,你說我怎么就攤上這事〕针纾” “怎么了雌团?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長衷掷。 經(jīng)常有香客問我辱姨,道長,這世上最難降的妖魔是什么戚嗅? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任雨涛,我火速辦了婚禮,結(jié)果婚禮上懦胞,老公的妹妹穿的比我還像新娘替久。我一直安慰自己,他們只是感情好躏尉,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布蚯根。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颅拦。 梳的紋絲不亂的頭發(fā)上蒂誉,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音距帅,去河邊找鬼右锨。 笑死,一個胖子當著我的面吹牛碌秸,可吹牛的內(nèi)容都是我干的绍移。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼讥电,長吁一口氣:“原來是場噩夢啊……” “哼蹂窖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恩敌,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瞬测,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后纠炮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涣楷,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年抗碰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绽乔。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡弧蝇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出折砸,到底是詐尸還是另有隱情看疗,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布睦授,位于F島的核電站两芳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏去枷。R本人自食惡果不足惜怖辆,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望删顶。 院中可真熱鬧竖螃,春花似錦、人聲如沸逗余。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽录粱。三九已至腻格,卻和暖如春画拾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菜职。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工青抛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人些楣。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓脂凶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愁茁。 傳聞我的和親對象是個殘疾皇子蚕钦,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

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