Objective—C常見特性(instancetype和id比較、@property、枚舉宏(Enumeration Macros))

Objective—C中常見特性

這麼多年過去了,Objective—C不斷的成長和進化。雖然核心思想和實踐保持不變,可是語言仍是產生了標誌性的改變和提升。這些現代化的提升體如今類型安全、內存管理、性能和Objective—C的其餘方面,這些讓咱們更加容易的編寫正確的代碼。express

instancetype和id比較

使用instancetype做爲關鍵字的函數返回一個類的實例,這個函數方法裏面包含alloc、init和這個類的工廠方法。xcode

使用instancetype代替id會提升代碼的類型安全。例如安全

@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"
}

由於用instancetype做爲返回類型的函數+factoryMethodA,這個消息的類型表示的是Myobject *。由於Myobject類沒有-count方法。編譯器會再 x 行報出警告:編輯器

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

然而,使用id類型的話,表示的是任何一個類型,因此裏面可能會包含有-count,編譯器就不會報出錯誤提示。函數

在子類中,爲了確保instancetype的工廠方法有正確的子類行爲,當分配一個類的時候,要用[self class]代替直接使用類名。工具

例如在上面的類的子類以下:性能

@interface MyObjectSubclass : MyObject
@end

void doSomethingElse() {
        NSString *aString = [MyObjectSubclass factoryMethodA];
}

編譯器會給出如下錯誤提示:atom

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

How to Adopt如何讓代碼採用instancetype方式

在你的代碼中,用instancetype替代已經存在的id。有表明性的是init方法和類工廠方法。編輯器只會替咱們自動把以alloc、init、new開頭的方法和返回值爲id的方法轉換成返回instancetype的方法,可是編輯器不會覆蓋其餘的方法。Objective-C 中約定明確的爲全部方法寫上instancetype。code

注意:你只須要在返回值的地方用instancetype替換id,er'bu'而不是把全部的id都用instancetype替換。instancetype只能做爲函數聲明的返回值。這是和id的不一樣點。對象

例如:

@interface MyObject
- (id)myFactoryMethod;
@end

應該變成

@interface MyObject
- (instancetype)myFactoryMethod;
@end

還有一種方法,你能夠在xcode中使用Objective-C的轉換器自動改變你的代碼,方法是下面的用xcode重構代碼

用xcode重構代碼

xcode提供現代Objective-C轉換工具,在開發工程中能夠輔助咱們,它能夠識別並應用現代化工具轉換代碼,但它並不能解釋代碼的意思。例如:它不會知道你的-toggle方法會影響你的對象的狀態,而且它可能會錯誤的認爲這個行爲是一個屬性。因此凡是自動轉換的代碼都要手動進行審查和確認。

它會幫咱們作這些事情:

  • 在正確的地方把id轉換爲instancetype
  • 改變enum成爲NS_ENUM或NS_OPTIONS
  • 更新@property語法

除了這些,它還會更改你的代碼的地方以下:

  • 轉換成文字,因此這樣的聲明[NSNumber numberWithInt:3]成爲@3。
  • 使用加下標,因此這樣的聲明[dictionary setObject:@3 forKey:key]成爲dictionary[key]= @3。

使用這種模式,選擇Edit > Refactor > Convert to Modern Objective-C Syntax.

屬性

使用@property聲明屬性比使用實例變量帶來的好處是:

  • 自動生成getters和setters方法
  • 更好的聲明一套方法的意圖,這樣使得編輯器更好的知道getter和setter方法是幹什麼的
  • 會提供一些關於行爲的信息,由於這樣聲明的屬性會帶有assign (vs copy), weak, atomic (vs nonatomic),等等

屬性的名字有一套命名規則:屬性的getter方法名就是屬性的名字自己(例如:屬性date的getter方法就是date),屬性的setter方法就是在屬性名前面加上set前綴(例如:屬性date的setter方法就是setDate)。Boolean屬性的getter方法以is開頭

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

這樣作就能像下面這樣使用了:

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

枚舉宏(Enumeration Macros)

使用NS_ENUM 和 NS_OPTIONS宏定義枚舉,能夠明確的指定你的宏的類型和大小,,除此以外,這個語法在老的編輯器中也能被識別。

使用NS_ENUM宏定義一組互斥的枚舉值:

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

NS_ENUM幫助定義名字和類型的列舉,這裏名字是UITableViewCellStyle,類型是NSInteger。這中類型的枚舉應該是NSInteger。

使用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
};

這種類型的枚舉一般使用NSUInteger。

相關文章
相關標籤/搜索