在Xcode7中,iOS9的SDK已經全面兼容了Objective-C的一些新特性和新功能。這些功能都只作用於編譯期,對程序的運行並沒有影響,因此,它可以很好的向下進行兼容,無縫的銜接低版本的iOS系統,那麼這些特性有什麼樣的用處呢,作為開發者,我保證你一定會愛上他們,如果你可以將這些新特性都應用於你的開發,你的開發效率和代碼質量,相比之前,會有一個很大的提升。
在swift語言中,通過!和?可以將對象聲明成Optional,用於在開發中標記這個對象是否可以為空。在OC中,以前是沒有這樣的功能的,因此我們在開發中會經常遇到因為某個函數應該返回實例而返回了空導致的崩潰。Nullability的主要用武之地,就是在這裡,它可以起到提示開發者做是否為空得判斷的提示。 打開Xcode7,系統的框架中已經支持了Nullability,如下:
@property (nullable, nonatomic, readonly) ObjectType firstObject; @property (nullable, nonatomic, readonly) ObjectType lastObject;
這是NSArray中的兩個屬性,其中nullable關鍵字說明了這裡可能返回空的值。
如果僅僅是在返回值中給開發者一些提示,你可能覺得應用並不大,是的,對開發者最大的幫助是這一特性可以用於函數的參數中,這樣我們在調用函數時起到的提示作用,將是非常重要的,越是多人合作的項目,作用也越大。
例如:
-(void)setValue:(NSNumber * _Nonnull )number{ }
我們在調用函數時,如果傳入了空值,編譯器會給我們警告:
注意:
這一特性在Xcode6.3中就已經支持,但在Xcode7中又做了一些寫法上的小改動,例如,在Xcode6.3中這樣寫:
-(void)setValue:( nonnull NSNumber * )number{ }
而在Xcode7中提倡我們使用第一種寫法。
與之相關的幾個關鍵字如下:
修飾參數
nonnull:不可為空
nullable: 可以為空
null_unspecified:不確定是否可以為空(極少情況)
在屬性的聲明中,還會有如下一個修飾符:
null_resettable:set方法可以為nil,get方法不可返回nil
一點提示:
你可以發現,iOS9的SDK中已經完全兼容使用了這些特性,並且nonnull的使用會比nullable廣泛的多,因此,系統提供了這樣一對宏:
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") #define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
我們在這對宏之間定義的變量都會加上nonnull的修飾符,只有我們特殊聲明nullable的才需要手動寫。
這一特性和Nullability一樣,只作用於編譯期,是為我們開發者服務的另一重要特性。還記得,在Xcode7之前,依然是為了方便多人開發,我經常會在框架中寫這樣的一個空得宏:
在開發時如下使用,做到提示伙伴我這個數組中是什麼東西的作用:
@interface ViewController () { NSArray __TYPE__FIT_TO__CLASS(NSString) * array; } @end
當然,所有這些都是我自己的自導自演,編譯器並不會鳥我,我在這個數組中加其他的東西,它也不會介意,所有這些只是我和我的伙伴們約定的一種一廂情願。所以,當我看到Xcode7中的集合類型時,我著實興奮了一下。
在Xcode7中,我們可以給集合類型添加一個泛型的約定,如下:
NSMutableArray*array = [[NSMutableArray alloc]init];
聲明了這樣一個數組後,就好比我告訴了編譯器,這個數組中的數據類型都是NSString*類型的,現在非常好,如果我這個數組中元素的方法,會出現如下的提示:
激動吧,使用點語法可以訪問到數組中泛型的方法了,還有更加誘人的:
在我們向這個數組中追加元素的時候,編譯器將元素的類型提示了出來,並且將FromArray方法中需要的元素類型也提示了出來。
同樣,如果我們向這個數組中追加類型不匹配的元素,如下:
NSMutableArray*array = [[NSMutableArray alloc]init]; [array addObject:@1];
編譯器會給我們一個這樣的警告:
觀察Xcode7中iOS系統的類,我們可以發現這麼一個好玩的東西:ObjectType。它既不是一個類型,也不是關鍵字,然而卻大量存在,如下是系統的NSMutableArray的頭文件:
@interface NSMutableArray
這個ObjectType其實只是一個類型標識符,它具體怎麼寫並不重要,只是系統中都約定使用了ObjectType,你也可以在自己的類中按自己的喜好來命名,這個東西有怎樣的用處,我用文字描述不清楚,我們可以通過自己來定義一個集合類來理解:
創建一個類,繼承於NSObject,我取名叫MyArray:
//這個類型通配符只能在interfave裡使用,作用域為@interface到@end之間 //這裡我使用Type來做這個通配符 @interface MyArray: NSObject @property(nonatomic,strong,nonnull)NSMutableArray *array; -(void)addObject:(nonnull Type)obj; @end
實現如下:
- (instancetype)init { self = [super init]; if (self) { _array = [[NSMutableArray alloc]init]; } return self; } -(void)addObject:(id)obj{ [_array addObject:obj]; } -(NSString *)description{ NSMutableString * str = [[NSMutableString alloc]init]; for (int i=0; i<_array.count; i++) { [str appendString:[NSString stringWithFormat:@"%@\n",_array[i]]]; } return str; }
我們在使用這個自定義的集合類型時,就會有和系統一樣的效果了:
多參數的泛型集合,有一個非常好的例子,就是NSDictionary,在Xcode7中我們可以這樣寫字典:
可以看到,字典鍵值的類型編譯器為我們提示了出來,結合上面類型通配符的使用,對於多參的集合,將參數類型用“,”隔開即可。
因為有了泛型集合的概念,相比之前,我們的類型實際上更加復雜了,比如還拿我們自定義的集合類型來舉例:
MyArray* array; MyArray *muArray;
array和muArray在編譯器看來已經是不同的類型,如果我們強行轉換,會報如下的警告:
因此,就有了逆變和協變這個概念:
__covariant :子類型指針可以向父類型指針轉換
__contravariant:父類型指針可以向子類型轉換
上面的情況,我們將自定義的類做如下修改,就不會出現警告:
@interface MyArray<__covariant Type> : NSObject @property(nonatomic,strong,nonnull)NSMutableArray*array; -(void)addObject:(nonnull Type)obj; @end
在開發中,開發者經常會遇到這樣的情況,例如通過tag獲取某些UI控件時,viewWithTag方法通常會返回給我們一個UIView類型的指針,這就需要開發者手動的強轉一下,十分麻煩。新增加的__kindof修飾符可以幫助我們解除這個煩惱。我們還從自定義的那個數組類開刀,對其添加一個屬性:
@interface MyArray<__covariant Type> : NSObject @property(nonatomic,strong,nonnull)NSMutableArray*array; @property(nonnull,strong,nonatomic)NSMutableArray * viewArray; -(void)addObject:(nonnull Type)obj; @end
創建一個自定義的數組對象,並向其中添加一個UIButton,我們會看到有如下一個警告:
這也是我們開發中常遇到的問題,對吧,以前需要強轉。但是以後就不需要了,我們在聲明這個數組時加上一個__kindof修飾符:
@property(nonnull,strong,nonatomic)NSMutableArray<__kindof UIView > viewArray;
警告就消失了,很cool吧。
這個修飾符就是告訴編譯器,這裡可以返回UIView的子類指針。
雖然這些優點在swift中早有體現,但就我個人而言,我對OC的感情會更深一些,也更加願意接受OC的改變和成長,大家都說swift的趨勢勢在必行,我只想說,swift很優秀,OC亦然。