iOS - 采用現(xiàn)代化的Objective-C

說明:本篇文章是作者(Mitchell)參考蘋果的 Adopting Modern Objective-C 這篇文章加上自己的總結(jié)所翻譯皮获,如需轉(zhuǎn)載請(qǐng)注明出處蝎困。


一择卦、instancetype###

  • 使用 instancetype 關(guān)鍵詞作為方法中返回類型事富,該方法返回一個(gè)它們所調(diào)用的類的實(shí)例(或者子類)砰逻,這些方法包括 alloc,init 和類的工廠方法昼牛。
  • 在適當(dāng)?shù)牡胤绞褂?instancetype 代替 id 會(huì)改善 Objective-C 中的代碼類型安全痰哨。`
    例如:
 @interface MyObject : NSObject
 + (instancetype)factoryMethodA;
 + (id)factoryMethodB;
 @end
 @implementation MyObject
 + (instancetype)factoryMethodA { return [[[self class] alloc]    init]; }
 + (id)factoryMethodB { return [[[self class] alloc] init]; }
 @end
void doSomething() {
NSUInteger x, y;
x = [[MyObject factoryMethodA] count]; // Return type of +factoryMethodA is taken to be "MyObject *"
y = [[MyObject factoryMethodB] count]; // Return type of +factoryMethodB is "id"
}

因?yàn)?factoryMethodA 的返回值類型是instancetype ,返回值的類型被表達(dá)為MyObject*匾嘱,然而MyObject并沒有-count的方法,所以編譯器在x的行給出了一則警告早抠。

main.m: ’MyObject’ may not respond to ‘count’

然而霎烙,factoryMethodB 返回的是一個(gè)id 類型的返回值,編譯器不能在 y 行給出警告蕊连。因?yàn)橐粋€(gè) id 類型的對(duì)象可能是任何的類悬垃,并且一個(gè)叫 -count 的方法可能存在于其中某些類中,對(duì)于編譯器來說這個(gè)返回值是有可能去響應(yīng)這個(gè)方法的甘苍。
為了確保instancetype的工廠方法有正確的子類化行為尝蠕,一定要確保在分配類的時(shí)候使用的是 [self class] ,而不是直接引用類的名稱载庭。遵循這個(gè)慣例會(huì)確保編譯器將正確推斷出子類的類型看彼。舉例:考慮嘗試讓 MyObject 的子類做一個(gè)這樣的事情:

@interface MyObjectSubclass : MyObject
@end
 void doSomethingElse() {
        NSString *aString = [MyObjectSubclass factoryMethodA];
}

這里如果這樣寫了,編譯器會(huì)報(bào)錯(cuò):

main.m: Incompatible pointer types initializing ’NSString *’ with an expression of type ’MyObjectSubclass *’

也就是說編譯器已經(jīng)能夠識(shí)別你的這個(gè)類型就是MyObjectSubclass*囚聚。但是如果將 instancetype 改成 id ,我們又會(huì)發(fā)現(xiàn)這個(gè)警告將會(huì)消失靖榕,也就是說編譯器不會(huì)知道你這個(gè)對(duì)象的準(zhǔn)確類型是什么。

  • 在這個(gè)例子中顽铸,+ factoryMethodA 方法傳遞了一個(gè)類型是MyObjectSubclass 的返回值類型茁计,它作為接受者的類型。編譯器將會(huì)適時(shí)的確定+ factoryMethodA方法的返回值就是子類MyObjectSubclass谓松,而不是父類的工廠方法中所返回的父類星压。
  • 如何運(yùn)用?
    在你的代碼中鬼譬,在適當(dāng)?shù)牡胤接?instancetype 替換現(xiàn)有的返回值為 id 的類型娜膘。通常是在 init 初始化方法和工廠類方法中。即使編譯器會(huì)自動(dòng)將 “alloc拧簸、init劲绪、new ”返回值是 id 的方法轉(zhuǎn)換為instancetype,它不會(huì)轉(zhuǎn)換其他方法。Objective-C 約定會(huì)為所有方法寫入instancetype贾富。
    注意:你只能在返回值的時(shí)候用 instancetype 替換 id 歉眷,而不是在你代碼的其他地方。instancetype 并不像 id颤枪,instancetype 關(guān)鍵詞只能被做聲明方法的返回值類型汗捡。
    例如:
 @interface MyObject
 - (id)myFactoryMethod;
 @end

應(yīng)該被改為:

```
@interface MyObject
- (instancetype) myFactoryMethod;
```
或者,您可以用現(xiàn)代Objective-C變化器在Xcode自動(dòng)進(jìn)行更改您的代碼畏纲,具體請(qǐng)參考   [Refactoring Your Code Using Xcode](https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW13) 扇住。
  • 對(duì)于 instancetype 和 id 異同的小結(jié):
    • 不同點(diǎn):
      1、instancetype可以返回和方法所在類相同類型的對(duì)象盗胀,而id只能返回未知類型的對(duì)象艘蹋,簡言之:instancetype會(huì)有一個(gè)類型檢測,在初始化的時(shí)候它會(huì)檢測是哪個(gè)類在調(diào)用初始化方法票灰,然后返回這個(gè)類女阀,而這個(gè)是id所做不到的。
      2屑迂、instancetype只能作為返回值浸策,不能像id那樣作為參數(shù)。
    • 相同點(diǎn):都能作為返回值的類型惹盼。

二庸汗、屬性###

一個(gè)Objective-C的屬性是由@property語法來定義是公有或者私有。

@property (readonly, getter=isBlue) BOOL blue;

屬性捕捉對(duì)象的狀態(tài)手报。他們反映了對(duì)象固有的屬性和其他對(duì)象們的關(guān)系蚯舱。屬性提供了一個(gè)安全,方便的方式關(guān)聯(lián)這些屬性昧诱,而無需編寫一組自定義訪問器方法(雖然屬性允許定制getter晓淀、setter方法,如果需要的話)盏档。

  • 自動(dòng)合成 getter和setter方法:當(dāng)你定義一個(gè)屬性的時(shí)候凶掰,默認(rèn)為你創(chuàng)建了getter和setter方法。
  • 更好的辦法是聲明一系列的方法蜈亩。因?yàn)樵L問器方法有命名約定懦窘,可以很清楚的知道getter和setter中做了什么。
  • 屬性的關(guān)鍵詞可以表達(dá)出關(guān)于自身行為的額外信息稚配。屬性提供了一個(gè)潛在的聲明屬性的方式畅涂,assign(vs copy),weak道川,atomic(vs nonatomic)午衰,等等立宜。
  • 屬性方法遵守了一個(gè)簡單的命名約定。getter方法的是以屬性的名稱命名臊岸,setter方法是以set前綴命名橙数,書寫的規(guī)范是駝峰式的大小寫。Boolean屬性的命名約定是在getter方法前加一個(gè)“is”:
@property(readonly,getter = isBlue)BOOL blue;

據(jù)此帅戒,所有的接下來的工作可以這樣:

if(color.blue){}
if(color.isBlue){}
if([color isBlue]){}

當(dāng)決定什么可以稱為一個(gè)屬性的時(shí)候灯帮,請(qǐng)記住以下的幾點(diǎn)不可以稱為屬性:
- init 方法
- copy 方法,mutableCopy方法
- 一個(gè)類的工廠方法
- 一個(gè)初始化方法并且返回一個(gè)BOOL值的方法
- 一個(gè)就像getter方法作用的一方面來明確內(nèi)部狀態(tài)變化的方法逻住。
除此之外钟哥,考慮考慮以下的規(guī)則當(dāng)在你的代碼中定義一個(gè)潛在的屬性:
- readwrite的屬性有兩個(gè)訪問器方法,setter帶一個(gè)參數(shù)無返回值瞎访。getter沒有參數(shù)但是有一個(gè)返回值腻贰。如果你想把這組方法轉(zhuǎn)換成屬性,標(biāo)記它一個(gè)readwrite關(guān)鍵詞扒秸。
- read-only的屬性只有一個(gè)訪問器方法银受,getter。如果你想把這個(gè)方法轉(zhuǎn)換成屬性鸦采,標(biāo)記它一個(gè)readonly關(guān)鍵詞。
- getter方法應(yīng)該被冪等(如果一個(gè)getter被調(diào)用兩次咕幻,第二次的返回的結(jié)果應(yīng)該是和第一次相同的)渔伯。然而,這也是可以接受的就是getter每次被調(diào)用時(shí)都計(jì)算結(jié)果肄程。

  • 如何采用
    定義一組方法锣吼,有資格被轉(zhuǎn)換為一個(gè)屬性,如下:
-  (NSColor*)backgroundColor;
 - (void)setBackgroundColor:(NSColor*)color;

然后用帶著適合的關(guān)鍵字的@property語法糖去聲明它們:

@property(copy)NSColor* backgroundColor;

關(guān)于屬性關(guān)鍵詞的信息和一些其他的注意事項(xiàng)蓝厌,請(qǐng)看Encapsulating Data


三玄叠、枚舉、宏###

NS_ENUM和NS_OPTION的宏提供了一個(gè)簡潔的基于C語言的方式去定義枚舉和選項(xiàng)拓提。這兩個(gè)宏在顯式地指定枚舉類型和大小的和選項(xiàng)方面提高了代碼的完成效率读恃。除此之外,這個(gè)語法生命枚舉的方式是由老的編程式來準(zhǔn)確評(píng)估的代态,由新的編程式來解釋潛在的類型信息寺惫。

使用 NS_ENUM 宏來定義枚舉,一組相互獨(dú)立的值:

typedef NS_ENUM (NSInteger,UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITavleViewCellStyleValue1,
    UITavleViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

NS_ENUM 宏幫助定義了枚舉的名字和類型兩個(gè)類型蹦疑,在這種情況下給 UITableViewCellStyle 命名為 NSInteger 的類型西雀。枚舉的類型應(yīng)該是 NSInteger。
使用 NS_OPTIONS 宏來定義選項(xiàng)歉摧,一組位掩碼值可以被組合在一起:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

像枚舉一樣艇肴,NS_OPTIONS宏定義了名稱和類型兩個(gè)值腔呜。然而,選項(xiàng)的類型應(yīng)該是NSUInteger再悼。

  • 如何采用
    替換你的魅族聲明核畴,就像下面這樣:
enum {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

使用 NS_ENUM 語法:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

當(dāng)你使用 enum 去定義一組位掩碼的時(shí)候,可以像下面這樣:

enum {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

使用 NS_OPTIONS 宏

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
  • 關(guān)于NS_ENUM 和NS_OPTIONS的小結(jié)
    • NS_ENUM:第一個(gè)參數(shù)用于存儲(chǔ)新類型的類型帮哈,在64位環(huán)境下膛檀,UITableViewCellStyle 和 NSInteger 一樣有8字節(jié)長。你要保證你給出的所有的值能被改類型容納娘侍,否則就會(huì)產(chǎn)生錯(cuò)誤咖刃。第二個(gè)參數(shù)是新類型的名字。大括號(hào)里面和以前i憾筏,是你要定義的各種值嚎杨。這種方法提取了之前各種不同實(shí)現(xiàn)的優(yōu)點(diǎn),甚至有提示編譯器在進(jìn)行switch判斷時(shí)檢查類型匹配的功能氧腰。

    • NS_OPTIONS:位掩碼使用枫浙。語法和 NS_ENUM 完全相同,但這個(gè)宏提示編譯值是如何通過位掩碼|組合在一起的古拴。同樣的箩帚,注意值的區(qū)間不要超過所使用類型的最大容納范圍。
      參考至NS_ENUM & NS_OPTIONS


四黄痪、對(duì)象的初始化###

  • 在 Objective-C 中紧帕,對(duì)象的初始化是基于一個(gè)指定的初始化器的概念,一個(gè)初始化方法負(fù)責(zé)調(diào)用的是父類的初始化方法然后會(huì)調(diào)用自身的對(duì)象實(shí)例方法桅打。不指定值的初始化被稱為便利的初始化是嗜。便利初始化通常委托給一個(gè)初始化器-最終的終端鏈在初始化器-而不是自己執(zhí)行的初始化。

  • 指定的初始化器會(huì)幫助并確保繼承的初始化器正確初始化所有的實(shí)例變量挺尾。子類所執(zhí)行重要的初始化需要覆蓋其父類所有指定的初始化鹅搪。但是它不必去覆蓋方便的初始化。更多關(guān)于初始化器的信息遭铺,看這里Object Initialization

  • 為了澄清指定和指定初始化之間的區(qū)別丽柿,你應(yīng)該在 init家庭中 增加 NS_DESIGNATED_INITIALIZER 這個(gè)宏,表示它是指定的初始化器魂挂。介紹這個(gè)宏的幾個(gè)使用機(jī)制:

    • 指定的初始化器必須鏈接到一個(gè)父類的 init 方法中([super init]),這是為父類指定的初始化器航厚。
    • 方便的初始化器(一個(gè)類的初始化方法并沒有被標(biāo)記為是指定的初始化方法,一個(gè)類必須至少有一個(gè)初始化方法作為指定的初始化器)必須委托給另一個(gè)初始化器锰蓬。
    • 如果一個(gè)類提供了一個(gè)或者更多的指定的初始化方法幔睬,它必須實(shí)現(xiàn)所有的指定的父類的初始化方法。
    • 如果違反了這些限制芹扭,你會(huì)收到來自編譯器的警告麻顶。
      如果你在你的類中使用了 NS_DESIGNATED_INITIALIZER 宏赦抖,你需要在你所有指定的初始化方法中都標(biāo)記這個(gè)宏,所有的其他的初始化會(huì)被認(rèn)為是便利的初始化辅肾。
  • 怎么使用
    在你的類中確定你指定的初始化方法队萤,然后用 NS_DESIGNATED_INITIALIZER 宏來標(biāo)記它們,舉個(gè)例子:

  - (instancetype)init NS_DESIGNATED_INITIALIZER;

五矫钓、自動(dòng)引用計(jì)數(shù)(ARC)###

  • 自動(dòng)引用計(jì)數(shù)是編譯器提供的一個(gè)自動(dòng)對(duì) Objective-C 對(duì)象進(jìn)行內(nèi)存管理的特色要尔。可之前你不得不用 retain新娜,releae 或者autorelease 不同赵辕,ARC 預(yù)估對(duì)象生命周期的需求然后在編譯的事后自動(dòng)插入適當(dāng)?shù)膬?nèi)存管理需求。編譯器也會(huì)生成適當(dāng)?shù)?dealloc 方法概龄。
  • 如何運(yùn)用
    Xcode提供了一個(gè)ARC轉(zhuǎn)換的自動(dòng)化工具(例如移除retain和release的調(diào)用)还惠,這會(huì)幫助你解決遷移的時(shí)候不能自動(dòng)處理的問題。使用ARC遷移工具私杜,選擇 Edit > Refactor > Convert to Objective-C ARC. 遷移器會(huì)將所有文件轉(zhuǎn)換成ARC蚕键。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市衰粹,隨后出現(xiàn)的幾起案子锣光,更是在濱河造成了極大的恐慌,老刑警劉巖铝耻,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癌别,死亡現(xiàn)場離奇詭異只祠,居然都是意外死亡蔬崩,警方通過查閱死者的電腦和手機(jī)凭迹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門鸳惯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汰规,“玉大人波附,你說我怎么就攤上這事杂曲≌┗穑” “怎么了兽赁?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冷守。 經(jīng)常有香客問我刀崖,道長,這世上最難降的妖魔是什么拍摇? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任亮钦,我火速辦了婚禮,結(jié)果婚禮上充活,老公的妹妹穿的比我還像新娘蜂莉。我一直安慰自己蜡娶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布映穗。 她就那樣靜靜地躺著窖张,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚁滋。 梳的紋絲不亂的頭發(fā)上宿接,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音辕录,去河邊找鬼睦霎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛踏拜,可吹牛的內(nèi)容都是我干的碎赢。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼速梗,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼肮塞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姻锁,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤枕赵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后位隶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拷窜,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年涧黄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了篮昧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笋妥,死狀恐怖懊昨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情春宣,我是刑警寧澤酵颁,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站月帝,受9級(jí)特大地震影響躏惋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嚷辅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一簿姨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧簸搞,春花似錦款熬、人聲如沸深寥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惋鹅。三九已至,卻和暖如春殉簸,著一層夾襖步出監(jiān)牢的瞬間闰集,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國打工般卑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留武鲁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓蝠检,卻偏偏與公主長得像沐鼠,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叹谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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