Objective—C語言的新魅力——Nullability、泛型集合與類型延拓

Objective—C語言的新魅力

1、引言

        在Xcode7中,iOS9的SDK已經全面兼容了Objective-C的一些新特性和新功能。這些功能都只做用於編譯期,對程序的運行並無影響,所以,它能夠很好的向下進行兼容,無縫的銜接低版本的iOS系統,那麼這些特性有什麼樣的用處呢,做爲開發者,我保證你必定會愛上他們,若是你能夠將這些新特性都應用於你的開發,你的開發效率和代碼質量,相比以前,會有一個很大的提高。swift

2、Nullability檢測的支持

        在swift語言中,經過!和?能夠將對象聲明成Optional,用於在開發中標記這個對象是否能夠爲空。在OC中,之前是沒有這樣的功能的,所以咱們在開發中會常常遇到由於某個函數應該返回實例而返回了空致使的崩潰。Nullability的主要用武之地,就是在這裏,它能夠起到提示開發者作是否爲空得判斷的提示。數組

        打開Xcode7,系統的框架中已經支持了Nullability,以下:app

@property (nullable, nonatomic, readonly) ObjectType firstObject;
@property (nullable, nonatomic, readonly) ObjectType lastObject;

這是NSArray中的兩個屬性,其中nullable關鍵字說明了這裏可能返回空的值。框架

若是僅僅是在返回值中給開發者一些提示,你可能以爲應用並不大,是的,對開發者最大的幫助是這一特性能夠用於函數的參數中,這樣咱們在調用函數時起到的提示做用,將是很是重要的,越是多人合做的項目,做用也越大。函數

例如:atom

-(void)setValue:(NSNumber * _Nonnull )number{
    
}

咱們在調用函數時,若是傳入了空值,編譯器會給咱們警告:spa

注意:指針

這一特性在Xcode6.3中就已經支持,但在Xcode7中又作了一些寫法上的小改動,例如,在Xcode6.3中這樣寫:code

-(void)setValue:( nonnull NSNumber *  )number{
    
}

而在Xcode7中提倡咱們使用第一種寫法。orm

與之相關的幾個關鍵字以下:

修飾參數

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的才須要手動寫。
 

3、泛型集合的支持

        這一特性和Nullability同樣,只做用於編譯期,是爲咱們開發者服務的另外一重要特性。還記得,在Xcode7以前,依然是爲了方便多人開發,我常常會在框架中寫這樣的一個空得宏:

在開發時以下使用,作到提示夥伴我這個數組中是什麼東西的做用:

@interface ViewController ()
{
    NSArray __TYPE__FIT_TO__CLASS(NSString) *  array;
}
@end

固然,全部這些都是我本身的自導自演,編譯器並不會鳥我,我在這個數組中加其餘的東西,它也不會介意,全部這些只是我和個人夥伴們約定的一種一廂情願。因此,當我看到Xcode7中的集合類型時,我着實興奮了一下。

一、有類型約定的集合

        在Xcode7中,咱們能夠給集合類型添加一個泛型的約定,以下:

 NSMutableArray<NSString *> *array = [[NSMutableArray alloc]init];

聲明瞭這樣一個數組後,就比如我告訴了編譯器,這個數組中的數據類型都是NSString*類型的,如今很是好,若是我這個數組中元素的方法,會出現以下的提示:

激動吧,使用點語法能夠訪問到數組中泛型的方法了,還有更加誘人的:

在咱們向這個數組中追加元素的時候,編譯器將元素的類型提示了出來,而且將FromArray方法中須要的元素類型也提示了出來。

一樣,若是咱們向這個數組中追加類型不匹配的元素,以下:

    NSMutableArray<NSString *> *array = [[NSMutableArray alloc]init];
    [array addObject:@1];

編譯器會給咱們一個這樣的警告:

二、關於一個類型通配符

        觀察Xcode7中iOS系統的類,咱們能夠發現這麼一個好玩的東西:ObjectType。它既不是一個類型,也不是關鍵字,然而卻大量存在,以下是系統的NSMutableArray的頭文件:

@interface NSMutableArray<ObjectType> : NSArray<ObjectType>
- (void)addObject:(ObjectType)anObject;
- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(ObjectType)anObject;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCapacity:(NSUInteger)numItems NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end

這個ObjectType其實只是一個類型標識符,它具體怎麼寫並不重要,只是系統中都約定使用了ObjectType,你也能夠在本身的類中按本身的喜愛來命名,這個東西有怎樣的用處,我用文字描述不清楚,咱們能夠經過本身來定義一個集合類來理解:

建立一個類,繼承於NSObject,我取名叫MyArray:

//這個類型通配符只能在interfave裏使用,做用域爲@interface到@end之間
//這裏我使用Type來作這個通配符
@interface MyArray<Type> : NSObject
@property(nonatomic,strong,nonnull)NSMutableArray<Type> *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<NSString *> * array;
    MyArray<NSMutableString *>*muArray;

array和muArray在編譯器看來已是不一樣的類型,若是咱們強行轉換,會報以下的警告:

所以,就有了逆變和協變這個概念:

__covariant :子類型指針能夠向父類型指針轉換

__contravariant:父類型指針能夠向子類型轉換

上面的狀況,咱們將自定義的類作以下修改,就不會出現警告:

@interface MyArray<__covariant Type> : NSObject
@property(nonatomic,strong,nonnull)NSMutableArray<Type> *array;
-(void)addObject:(nonnull Type)obj;
@end

4、類型延拓符的應用

        在開發中,開發者常常會遇到這樣的狀況,例如經過tag獲取某些UI控件時,viewWithTag方法一般會返回給咱們一個UIView類型的指針,這就須要開發者手動的強轉一下,十分麻煩。新增長的__kindof修飾符能夠幫助咱們解除這個煩惱。咱們還從自定義的那個數組類開刀,對其添加一個屬性:

@interface MyArray<__covariant Type> : NSObject
@property(nonatomic,strong,nonnull)NSMutableArray<Type> *array;
@property(nonnull,strong,nonatomic)NSMutableArray<UIView *> * viewArray;
-(void)addObject:(nonnull Type)obj;
@end

建立一個自定義的數組對象,並向其中添加一個UIButton,咱們會看到有以下一個警告:

這也是咱們開發中常遇到的問題,對吧,之前須要強轉。可是之後就不須要了,咱們在聲明這個數組時加上一個__kindof修飾符:

@property(nonnull,strong,nonatomic)NSMutableArray<__kindof UIView *> * viewArray;

警告就消失了,很cool吧。

這個修飾符就是告訴編譯器,這裏能夠返回UIView的子類指針。

5、結語

         雖然這些優勢在swift中早有體現,但就我我的而言,我對OC的感情會更深一些,也更加願意接受OC的改變和成長,你們都說swift的趨勢勢在必行,我只想說,swift很優秀,OC亦然。

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索