本文原文轉(zhuǎn)自http://my.oschina.net/u/2340880/blog/514804
原 Objective—C語(yǔ)言的新魅力——Nullability尺锚、泛型集合與類型延拓
摘要相對(duì)于Java吼蚁,OC語(yǔ)言是一門古老的語(yǔ)言了贮缕,而它又是一門不斷發(fā)展完善的語(yǔ)言辙谜。一些新的編譯特性,為OC語(yǔ)言帶來(lái)了許多新的活力感昼。Objective—C語(yǔ)言的新魅力
一筷弦、引言? ? ? ?
?在Xcode7中,iOS9的SDK已經(jīng)全面兼容了Objective-C的一些新特性和新功能抑诸。這些功能都只作用于編譯期烂琴,對(duì)程序的運(yùn)行并沒(méi)有影響,因此蜕乡,它可以很好的向下進(jìn)行兼容奸绷,無(wú)縫的銜接低版本的iOS系統(tǒng),那么這些特性有什么樣的用處呢层玲,作為開(kāi)發(fā)者号醉,我保證你一定會(huì)愛(ài)上他們,如果你可以將這些新特性都應(yīng)用于你的開(kāi)發(fā)辛块,你的開(kāi)發(fā)效率和代碼質(zhì)量畔派,相比之前,會(huì)有一個(gè)很大的提升润绵。
二线椰、Nullability檢測(cè)的支持? ? ? ?
?在swift語(yǔ)言中,通過(guò)!和?可以將對(duì)象聲明成Optional尘盼,用于在開(kāi)發(fā)中標(biāo)記這個(gè)對(duì)象是否可以為空憨愉。在OC中烦绳,以前是沒(méi)有這樣的功能的,因此我們?cè)陂_(kāi)發(fā)中會(huì)經(jīng)常遇到因?yàn)槟硞€(gè)函數(shù)應(yīng)該返回實(shí)例而返回了空導(dǎo)致的崩潰配紫。Nullability的主要用武之地径密,就是在這里,它可以起到提示開(kāi)發(fā)者做是否為空得判斷的提示躺孝。? ? ? ?
?打開(kāi)Xcode7享扔,系統(tǒng)的框架中已經(jīng)支持了Nullability,如下:
@property (nullable, nonatomic, readonly) ObjectType firstObject;
@property (nullable, nonatomic, readonly) ObjectType lastObject;
這是NSArray中的兩個(gè)屬性植袍,其中nullable關(guān)鍵字說(shuō)明了這里可能返回空的值惧眠。
如果僅僅是在返回值中給開(kāi)發(fā)者一些提示,你可能覺(jué)得應(yīng)用并不大奋单,是的,對(duì)開(kāi)發(fā)者最大的幫助是這一特性可以用于函數(shù)的參數(shù)中猫十,這樣我們?cè)谡{(diào)用函數(shù)時(shí)起到的提示作用览濒,將是非常重要的,越是多人合作的項(xiàng)目拖云,作用也越大贷笛。
例如:
-(void)setValue:(NSNumber * _Nonnull )number{? ?
?}
我們?cè)谡{(diào)用函數(shù)時(shí),如果傳入了空值宙项,編譯器會(huì)給我們警告:
注意:這一特性在Xcode6.3中就已經(jīng)支持乏苦,但在Xcode7中又做了一些寫(xiě)法上的小改動(dòng),例如尤筐,在Xcode6.3中這樣寫(xiě):
-(void)setValue:( nonnull NSNumber *? )number{? ?
?}
而在Xcode7中提倡我們使用第一種寫(xiě)法汇荐。
與之相關(guān)的幾個(gè)關(guān)鍵字如下:
修飾參數(shù)nonnull:不可為空
nullable: 可以為空
null_unspecified:不確定是否可以為空(極少情況)
在屬性的聲明中,還會(huì)有如下一個(gè)修飾符:
null_resettable:set方法可以為nil盆繁,get方法不可返回nil
一點(diǎn)提示:你可以發(fā)現(xiàn)掀淘,iOS9的SDK中已經(jīng)完全兼容使用了這些特性,并且nonnull的使用會(huì)比nullable廣泛的多油昂,因此革娄,系統(tǒng)提供了這樣一對(duì)宏:
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END? _Pragma("clang assume_nonnull end")
我們?cè)谶@對(duì)宏之間定義的變量都會(huì)加上nonnull的修飾符,只有我們特殊聲明nullable的才需要手動(dòng)寫(xiě)冕碟。
三拦惋、泛型集合的支持? ? ? ?
?這一特性和Nullability一樣,只作用于編譯期安寺,是為我們開(kāi)發(fā)者服務(wù)的另一重要特性厕妖。還記得,在Xcode7之前挑庶,依然是為了方便多人開(kāi)發(fā)叹放,我經(jīng)常會(huì)在框架中寫(xiě)這樣的一個(gè)空得宏:
在開(kāi)發(fā)時(shí)如下使用饰恕,做到提示伙伴我這個(gè)數(shù)組中是什么東西的作用:
@interface ViewController (){ ? ?
??? NSArray __TYPE__FIT_TO__CLASS(NSString) *? array;
}
@end
當(dāng)然,所有這些都是我自己的自導(dǎo)自演井仰,編譯器并不會(huì)鳥(niǎo)我埋嵌,我在這個(gè)數(shù)組中加其他的東西,它也不會(huì)介意俱恶,所有這些只是我和我的伙伴們約定的一種一廂情愿雹嗦。所以,當(dāng)我看到Xcode7中的集合類型時(shí)合是,我著實(shí)興奮了一下了罪。
1、有類型約定的集合? ? ? ?
?在Xcode7中聪全,我們可以給集合類型添加一個(gè)泛型的約定泊藕,如下: NSMutableArray*array = [[NSMutableArray alloc]init];聲明了這樣一個(gè)數(shù)組后,就好比我告訴了編譯器难礼,這個(gè)數(shù)組中的數(shù)據(jù)類型都是NSString*類型的娃圆,現(xiàn)在非常好,如果我這個(gè)數(shù)組中元素的方法蛾茉,會(huì)出現(xiàn)如下的提示:激動(dòng)吧讼呢,使用點(diǎn)語(yǔ)法可以訪問(wèn)到數(shù)組中泛型的方法了,還有更加誘人的:在我們向這個(gè)數(shù)組中追加元素的時(shí)候谦炬,編譯器將元素的類型提示了出來(lái)悦屏,并且將FromArray方法中需要的元素類型也提示了出來(lái)。同樣键思,如果我們向這個(gè)數(shù)組中追加類型不匹配的元素础爬,如下:? ??
NSMutableArray*array = [[NSMutableArray alloc]init];? ?
?[array addObject:@1];
編譯器會(huì)給我們一個(gè)這樣的警告:
2、關(guān)于一個(gè)類型通配符? ? ? ??
觀察Xcode7中iOS系統(tǒng)的類吼鳞,我們可以發(fā)現(xiàn)這么一個(gè)好玩的東西:ObjectType幕帆。它既不是一個(gè)類型,也不是關(guān)鍵字赖条,然而卻大量存在失乾,如下是系統(tǒng)的NSMutableArray的頭文件:
@interface NSMutableArray: NSArray
- (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
這個(gè)ObjectType其實(shí)只是一個(gè)類型標(biāo)識(shí)符,它具體怎么寫(xiě)并不重要纬乍,只是系統(tǒng)中都約定使用了ObjectType碱茁,你也可以在自己的類中按自己的喜好來(lái)命名,這個(gè)東西有怎樣的用處仿贬,我用文字描述不清楚纽竣,我們可以通過(guò)自己來(lái)定義一個(gè)集合類來(lái)理解:創(chuàng)建一個(gè)類,繼承于NSObject,我取名叫MyArray://這個(gè)類型通配符只能在interfave里使用蜓氨,作用域?yàn)锧interface到@end之間//這里我使用Type來(lái)做這個(gè)通配符
@interface MyArray: NSObject
@property(nonatomic,strong,nonnull)NSMutableArray*array;
-(void)addObject:(nonnull Type)obj;
@end
實(shí)現(xiàn)如下:
- (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;
}
我們?cè)谑褂眠@個(gè)自定義的集合類型時(shí)聋袋,就會(huì)有和系統(tǒng)一樣的效果了:
3、關(guān)于多參數(shù)的泛型集合? ? ? ?
?多參數(shù)的泛型集合穴吹,有一個(gè)非常好的例子幽勒,就是NSDictionary,在Xcode7中我們可以這樣寫(xiě)字典:可以看到港令,字典鍵值的類型編譯器為我們提示了出來(lái)啥容,結(jié)合上面類型通配符的使用,對(duì)于多參的集合顷霹,將參數(shù)類型用“,”隔開(kāi)即可咪惠。
4、協(xié)變性與逆變性? ? ? ??
因?yàn)橛辛朔盒图系母拍盍艿恚啾戎耙C粒覀兊念愋蛯?shí)際上更加復(fù)雜了,比如還拿我們自定義的集合類型來(lái)舉例:? ??
MyArray* array;? ? MyArray*muArray;array和muArray在編譯器看來(lái)已經(jīng)是不同的類型朵纷,如果我們強(qiáng)行轉(zhuǎn)換炭臭,會(huì)報(bào)如下的警告:
因此,就有了逆變和協(xié)變這個(gè)概念:
__covariant :子類型指針可以向父類型指針轉(zhuǎn):
__contravariant:父類型指針可以向子類型轉(zhuǎn)換上面的情況柴罐,我們將自定義的類做如下修改徽缚,就不會(huì)出現(xiàn)警告:
@interface MyArray<__covariant Type> : NSObject
@property(nonatomic,strong,nonnull)NSMutableArray*array;
-(void)addObject:(nonnull Type)obj;
@end
四憨奸、類型延拓符的應(yīng)用? ? ? ??
在開(kāi)發(fā)中革屠,開(kāi)發(fā)者經(jīng)常會(huì)遇到這樣的情況,例如通過(guò)tag獲取某些UI控件時(shí)排宰,viewWithTag方法通常會(huì)返回給我們一個(gè)UIView類型的指針似芝,這就需要開(kāi)發(fā)者手動(dòng)的強(qiáng)轉(zhuǎn)一下,十分麻煩板甘。新增加的__kindof修飾符可以幫助我們解除這個(gè)煩惱党瓮。我們還從自定義的那個(gè)數(shù)組類開(kāi)刀,對(duì)其添加一個(gè)屬性:
@interface MyArray<__covariant Type> : NSObject@property(nonatomic,strong,nonnull)NSMutableArray*array;
@property(nonnull,strong,nonatomic)NSMutableArray* viewArray;
-(void)addObject:(nonnull Type)obj;
@end
創(chuàng)建一個(gè)自定義的數(shù)組對(duì)象盐类,并向其中添加一個(gè)UIButton寞奸,我們會(huì)看到有如下一個(gè)警告:
這也是我們開(kāi)發(fā)中常遇到的問(wèn)題,對(duì)吧在跳,以前需要強(qiáng)轉(zhuǎn)枪萄。但是以后就不需要了,我們?cè)诼暶鬟@個(gè)數(shù)組時(shí)加上一個(gè)__kindof修飾符:
@property(nonnull,strong,nonatomic)NSMutableArray<__kindof UIView *> * viewArray;
警告就消失了猫妙,很cool吧瓷翻。
這個(gè)修飾符就是告訴編譯器,這里可以返回UIView的子類指針。
五齐帚、結(jié)語(yǔ)
雖然這些優(yōu)點(diǎn)在swift中早有體現(xiàn)妒牙,但就我個(gè)人而言,我對(duì)OC的感情會(huì)更深一些对妄,也更加愿意接受OC的改變和成長(zhǎng)湘今,大家都說(shuō)swift的趨勢(shì)勢(shì)在必行,我只想說(shuō)饥伊,swift很優(yōu)秀象浑,OC亦然。
? 著作權(quán)歸作者所有