說明
:本篇文章是作者(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):都能作為返回值的類型惹盼。
- 不同點(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蚕键。