熟悉Objective-C

第1條:了解Objective-C語(yǔ)言的起源

  • Objective-C為C語(yǔ)言添加了面向?qū)ο筇匦灾⒒瑁瞧涑两荨@斫釩語(yǔ)言的內(nèi)存模型(memory model)有助于理解Objective-C的內(nèi)存模型及其引用計(jì)數(shù)(reference counting)機(jī)制的工作原理纠拔。
  • Objective-C使用動(dòng)態(tài)綁定的消息結(jié)構(gòu)歧杏,也就是說(shuō)砰碴,在運(yùn)行時(shí)才會(huì)檢查對(duì)象類型九榔。在接收一條消息之后兽埃,究竟應(yīng)執(zhí)行何種代碼侥钳,有運(yùn)行期環(huán)境決定,而非編譯器決定柄错。

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

  • 當(dāng)我們?cè)诰幾g一個(gè)類的時(shí)候舷夺,不需要知道該類的全部細(xì)節(jié),只需要知道有一個(gè)這樣的類就好的時(shí)候售貌,我們就不用通過(guò)#import的方式來(lái)引入該類頭文件给猾。

    這叫“向前聲明”(forward declaring)該類。

    @class "EOCEmployer.h"
    

    import和@class的區(qū)別

    import會(huì)包含這個(gè)類的所有信息颂跨,包括實(shí)體變量和方法敢伸,而@class只是告訴編譯器,其后面聲明的名稱是類的名稱毫捣。

    在頭文件中详拙, 一般只需要知道被引用的類的名稱就可以了。不需要知道其內(nèi)部的實(shí)體變量和方法蔓同,所以在頭文件中一般使用@class來(lái)聲明這個(gè)名稱是類的名稱饶辙。

    而在實(shí)現(xiàn)類里面,因?yàn)闀?huì)用到這個(gè)引用類的內(nèi)部的實(shí)體變量和方法斑粱,所以需要使用#import來(lái)包含這個(gè)被引用類的頭文件弃揽。

    如果存在循環(huán)依賴關(guān)系,如:A -> B, B -> A這樣的相互依賴關(guān)系,當(dāng)使用#import來(lái)相互包含矿微,那么就會(huì)出現(xiàn)編譯錯(cuò)誤痕慢,如果使用@class在兩個(gè)類的頭文件中相互聲明,則不會(huì)有編譯錯(cuò)誤出現(xiàn)涌矢。

    所以掖举,一般來(lái)說(shuō),@class是放在interface中的娜庇,只是為了在interface中引用這個(gè)類塔次,把這個(gè)類作為一個(gè)類型來(lái)用的。在實(shí)現(xiàn)這個(gè)接口的實(shí)現(xiàn)類中名秀,如果需要引用這個(gè)類的實(shí)體變量或者方法之類的励负,還是需要import。

  • 當(dāng)我們需要聲明某個(gè)類遵循一項(xiàng)協(xié)議時(shí)匕得,不能使用@class继榆。這種情況下,盡量把“該類遵循某協(xié)議”的這條聲明移至“class-continuation分類”中汁掠。如果不行的話略吨,就把協(xié)議單獨(dú)放在一個(gè)頭文件中,然后將其通過(guò)#import的方式引入调塌。

第3條:多用字面量語(yǔ)法晋南,少用與之等價(jià)的方法

  • 使用字面量語(yǔ)法來(lái)聲明可以縮減源代碼長(zhǎng)度,使其更為易讀羔砾。
字面數(shù)值
NSNumber *Number = [NSNumber numberWithInt:1];
NSNumber *newNumber = @1;
字面數(shù)組

如果數(shù)組元素對(duì)象中有nil负间,則會(huì)拋出異常,因?yàn)樽置媪空Z(yǔ)法實(shí)際上只是一種“語(yǔ)法糖”(syntactic sugar)姜凄,其效果等于是先創(chuàng)建了一個(gè)數(shù)組政溃,然后把方括號(hào)內(nèi)的所有對(duì)象都加入到這個(gè)數(shù)組中。
因此态秧,如下面這兩個(gè)數(shù)組的第二位都未nil時(shí)董虱,array里面會(huì)只有@“one”一個(gè)對(duì)象,而newArray將會(huì)拋出異常申鱼。這種微妙的差別表明愤诱,使用字面量語(yǔ)法更為安全。拋出異常令程序中止執(zhí)行捐友,這比創(chuàng)建好數(shù)組之后才發(fā)現(xiàn)元素個(gè)數(shù)少了要好淫半,因?yàn)橄驍?shù)組中插入nil通常就是說(shuō)明程序有錯(cuò),最終結(jié)果往往就是數(shù)組越界崩潰匣砖,而現(xiàn)在我們卻可以通過(guò)異晨瓶裕可以更快地發(fā)現(xiàn)這個(gè)錯(cuò)誤昏滴。

NSArray *array = [NSArray arrayWithObjects:@"one",@"two",@"three", nil];
NSArray *newArray = @[@"one",@"two",@"three"];
字面字典

與數(shù)組一樣,用字面量語(yǔ)法創(chuàng)建字典時(shí)也有一個(gè)問(wèn)題对人,那就是一旦有值為nil谣殊,便會(huì)拋出異常。

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
                        @"Mark",@"firstname",
                        @"Lin",@"lastname",nil];

NSDictionary *newDic = @{@"firstname" : @"Mark",
                         @"lastname" : @"Lin",
                         @"age" : @28};

大家可以很直觀地看出兩種創(chuàng)建實(shí)例變量的方法有什么區(qū)別牺弄,使用字面量的方式不僅可以令到代碼看起來(lái)更加地整潔姻几,而且字面量語(yǔ)法也更為精簡(jiǎn),因?yàn)槁暶髦兄话瑪?shù)值猖闪,而沒(méi)有多余的語(yǔ)法成分鲜棠。

局限性

字面量語(yǔ)法有一個(gè)小小的局限肌厨,就是除了字符串以外培慌,所創(chuàng)建出來(lái)的對(duì)象必須屬于Foundation框架才行。

而使用字面量語(yǔ)法創(chuàng)建的字符串柑爸、數(shù)組吵护、字典都是不可變的(immutable),若想要可變版本的對(duì)象可以參考以下寫(xiě)法:

NSMutableArray *mutable = [@[@"one", @"two", @"three"] mutableCopy];

第4條:多用類型常量表鳍,少用#define預(yù)處理指令

  • 編寫(xiě)代碼時(shí)經(jīng)常需要定義一些常量馅而,很多人都會(huì)用下面這種方式來(lái)實(shí)現(xiàn):

    #define ANIMATION_DURATION 0.3
    

    這種實(shí)現(xiàn)方式可能就是你想要的效果,但是這樣定義出來(lái)的常量沒(méi)有類型信息譬圣,此外瓮恭,預(yù)處理過(guò)程會(huì)把所有ANIMATION_DURATION一律改成0.3,這樣的話厘熟,假設(shè)此指令在某個(gè)頭文件中屯蹦,那么所有引入了這個(gè)頭文件的代碼,其ANIMATION_DURATION都會(huì)被替換绳姨。

    要想解決這個(gè)問(wèn)題登澜,可以使用下面這種方式來(lái)實(shí)現(xiàn):

    static const NSTimeInterval kAnimationDuration = 0.3;
    

    此方式定義的常量包含類型信息,清晰描述常量的含義飘庄。而且定義常量的位置很重要脑蠕,在頭文件中聲明預(yù)處理指令是一種很糟糕的做法,常量名稱有可能互相沖突跪削。因?yàn)镺bjective-C沒(méi)有“名稱空間”(namespace)這一概念谴仙,所以這樣做等于聲明了一個(gè)全局變量。

    所以若不打算公開(kāi)某常量碾盐,則應(yīng)將其定義在使用該常量的實(shí)現(xiàn)文件里面晃跺,如下:

    //  MarkAnimatedView.h
    
    #import <UIKit/UIKit.h>
    
    @interface MarkAnimatedView : UIView
    
    - (void)animate;
    
    @end
    
    //  MarkAnimatedView.m
    
    #import "MarkAnimatedView.h"
    
    static const NSTimeInterval kAnimationDuration = 0.3;
    
    @implementation MarkAnimatedView
    
    - (void)animate
    {
        [UIView animateWithDuration:kAnimationDuration animations:^{
            //Perform animations
        }];
    }
    
    @end
    

常量為什么一定要同時(shí)使用static與const來(lái)聲明呢?廓旬?哼审?
因?yàn)槿绻腥嗽噲D修改有const修飾符所聲明的變量時(shí)谐腰,那編譯就會(huì)報(bào)錯(cuò),而這就是我們所需要達(dá)到的目的I堋J!

而static修飾符則是意味著該常量?jī)H在定義此常量的編譯單元中可見(jiàn)春霍。

編譯器每收到一個(gè)編譯單元砸西,就會(huì)輸出一份“目標(biāo)文件”(object file),在Objective-C的語(yǔ)境下址儒,“編譯單元”一詞通常指每個(gè)類的實(shí)現(xiàn)文件芹枷,因此在上述代碼中聲明的kAnimationDuration,其作用域僅限于由MarkAnimatedView.m所生成的目標(biāo)文件中莲趣。

假如聲明此常量時(shí)不加static鸳慈,則編譯器會(huì)為它創(chuàng)建一個(gè)“外部符號(hào)”(external symbol),此時(shí)如若另一個(gè)編譯單元中聲明了同名常量喧伞,則會(huì)報(bào)錯(cuò)走芋。

而有時(shí)候確實(shí)是需要對(duì)外公開(kāi)某個(gè)常量的時(shí)候,我們有應(yīng)該如何處理呢潘鲫?(如在類代碼中調(diào)用NSNotificationCenter)

此類常量則應(yīng)放在“全局符號(hào)表”(global symbol table)中翁逞,以便可以在定義常量的編譯單元之外使用。具體實(shí)現(xiàn)如下:

```
//  MarkAnimatedView.h
#import <UIKit/UIKit.h>

extern NSString *const MarkLoginNotification;

@interface MarkAnimatedView : UIView

- (void)animate;

@end

//  MarkAnimatedView.m
#import "MarkAnimatedView.h"

static const NSTimeInterval kAnimationDuration = 0.3;

NSString *const MarkLoginNotification = @"MarkLoginNotification";

@implementation MarkAnimatedView

- (void)animate
{
   [UIView animateWithDuration:kAnimationDuration animations:^{
        //Perform animations
    }];
}

- (void)doNotificationAction
{
    [[NSNotificationCenter defaultCenter] postNotificationName:MarkLoginNotification object:nil];
}

@end
```

注意const修飾符在常量類型中的位置溉仑,常量定義應(yīng)從右至左解讀挖函,所以在本例子中,MarkLoginNotification和kAnimationDuration就是“一個(gè)常量浊竟,而這個(gè)常量是指針怨喘,指向NSString對(duì)象”。

extern這個(gè)修飾符會(huì)告訴編譯器逐沙,在全局符號(hào)表中將會(huì)有一個(gè)MarkLoginNotification的符號(hào)哲思,編譯器無(wú)須查看其定義,因?yàn)樗喇?dāng)鏈接成二進(jìn)制文件之后吩案,肯定能找到這個(gè)常量棚赔。

這樣定義常量要優(yōu)于#define預(yù)處理指令,因?yàn)榫幾g器會(huì)確保常量值不變徘郭,而且一旦定義好之后靠益,即可隨處使用。

第5條:用枚舉表示狀態(tài)残揉、選項(xiàng)胧后、狀態(tài)碼

  • 枚舉只是一種常量命名方式, 使用枚舉來(lái)表示各種狀態(tài)碼可以便于程序猿們更好地去理解代碼抱环。如使用枚舉來(lái)表示“套接字連接”(socket connection)的狀態(tài):

    enum MarkConnectionState {
        MarkConnectionDisconnected,
        MarkConnectionConnecting,
        MarkConnectionConnected,
    };
    
  • 要想每次不用敲入enum而只需寫(xiě)MarkConnectionState的話壳快,則需要使用typedef關(guān)鍵字重新定義枚舉類型:

    enum MarkConnectionState {
        MarkConnectionDisconnected,
        MarkConnectionConnecting,
        MarkConnectionConnected,
    };
    typedef enum MarkConnectionState MarkConnectionState;
    

    現(xiàn)在就可以使用簡(jiǎn)寫(xiě)的MarkConnectionState來(lái)代替完整的enum MarkConnectionState了:

    MarkConnectionState state = MarkConnectionConnected;
    
  • C++11標(biāo)準(zhǔn)修訂了枚舉的某些特性纸巷,其中就包括了很重要的一點(diǎn):可以指明使用何種“底層數(shù)據(jù)類型”(underlying type)來(lái)保存枚舉類型的變量。這樣做的好處就是可以向前聲明枚舉變量了:

    enum MarkConnectionState : NSInteger;
    

    當(dāng)然眶痰,還可以不使用編譯器所分配的序號(hào)瘤旨,而手工指定某個(gè)枚舉成員所對(duì)應(yīng)的值:

    enum MarkConnectionState {
        MarkConnectionDisconnected = 22,
        MarkConnectionConnecting,
        MarkConnectionConnected,
    };
    

    如前所述,由于第一個(gè)枚舉值為22竖伯,所以接下來(lái)的幾個(gè)枚舉值都會(huì)在上一個(gè)的基礎(chǔ)上遞增1存哲。

  • 還有一種情況是非常推薦使用枚舉類型的,那就是-----定義選項(xiàng)七婴!

    enum MarkAutoresizing {
        MarkAutoresizin         = 0,
        MarkAutoresizinWidth    = 1 << 0,
        MarkAutoresizinHeight   = 1 << 1,
        MarkAutoresizinTop      = 1 << 2,
        MarkAutoresizinBottom   = 1 << 3,
    };
    
  • 在iOS UI框架中的UIKit里也有一個(gè)非常常見(jiàn)的例子祟偷,那就是用枚舉值來(lái)告訴系統(tǒng)視圖所支持的設(shè)備顯示方向,這個(gè)枚舉類型叫做UIInterfaceOrientationMask打厘,開(kāi)發(fā)者可以通過(guò)supported InterfaceOrientations的方法修肠,將視圖所支持的顯示方向告訴系統(tǒng)。

  • 最后再講一種枚舉的用法婚惫,在swith中使用枚舉:

    typedef NS_ENUM(NSUInteger, MarkConnectionState){
        MarkConnectionDisconnected,
        MarkConnectionConnecting,
        MarkConnectionConnected,
    };
    
    switch (_currentState) {
            case MarkConnectionDisconnected:
                
                break;
            case MarkConnectionConnecting:
                
                break;
            case MarkConnectionConnected:
                
                break;
            default:
                break;
        }
    

使用NS_ENUM與NS_OPTIONS宏來(lái)定義枚舉類型氛赐,并指明其底層數(shù)據(jù)類型,這樣做可以確保枚舉是用開(kāi)發(fā)者所選的底層數(shù)據(jù)類型實(shí)現(xiàn)出來(lái)先舷,而不會(huì)采用編譯器所選的類型。
在處理枚舉類型的swith語(yǔ)句中不要實(shí)現(xiàn)default分支滓侍,這樣就可以在加入新枚舉之后蒋川,編譯器自動(dòng)提示開(kāi)發(fā)者:swith語(yǔ)句并未處理所有枚舉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撩笆,一起剝皮案震驚了整個(gè)濱河市捺球,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夕冲,老刑警劉巖氮兵,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異歹鱼,居然都是意外死亡泣栈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)弥姻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)南片,“玉大人,你說(shuō)我怎么就攤上這事庭敦√劢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵秧廉,是天一觀的道長(zhǎng)伞广。 經(jīng)常有香客問(wèn)我拣帽,道長(zhǎng),這世上最難降的妖魔是什么嚼锄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任诞外,我火速辦了婚禮,結(jié)果婚禮上灾票,老公的妹妹穿的比我還像新娘峡谊。我一直安慰自己,他們只是感情好刊苍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布既们。 她就那樣靜靜地躺著,像睡著了一般正什。 火紅的嫁衣襯著肌膚如雪啥纸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天婴氮,我揣著相機(jī)與錄音斯棒,去河邊找鬼。 笑死主经,一個(gè)胖子當(dāng)著我的面吹牛荣暮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罩驻,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼穗酥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了惠遏?” 一聲冷哼從身側(cè)響起砾跃,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎节吮,沒(méi)想到半個(gè)月后抽高,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡透绩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年翘骂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渺贤。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雏胃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出志鞍,到底是詐尸還是另有隱情瞭亮,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布固棚,位于F島的核電站统翩,受9級(jí)特大地震影響仙蚜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厂汗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一委粉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧娶桦,春花似錦贾节、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祈争,卻和暖如春斤程,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菩混。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工忿墅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沮峡。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓疚脐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親帖烘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亮曹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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