Objective-C 2.0一開始用在GCC編譯器上牍戚,后來因?yàn)镚CC嚴(yán)格的GPL許可證使得Apple不得不尋找新的良好的編譯器開源項(xiàng)目烦粒,而LLVM很快就被她盯上了模燥。
Objective-C在Clang上發(fā)展速度非吵道螅快峦树!先后加入了許多出色的語法特性辣辫,包括Blocks(在純C語言上也能使用),ARC魁巩,Module等等急灭。這里筆者將列出我們常用的一些新增語法特性,這些語法特性不僅可以在Apple LLVM 9.0編譯器上使用谷遂,而且也能在Clang 5.0中使用葬馋。
1. 關(guān)聯(lián)結(jié)果類型
在很早之前,我們寫一個(gè)類的初始化器往往用 id
關(guān)鍵字埋凯,但這個(gè)關(guān)鍵字僅僅表明我們所返回的對象類型為一個(gè)Objective-C類類型点楼,而不能表征當(dāng)前類類型本身。這所引發(fā)的問題就是我們在用鏈?zhǔn)椒椒ㄕ{(diào)用時(shí)白对,由于當(dāng)前方法所返回的對象類型并非為當(dāng)前類類型掠廓,因而不能直接訪問其方法或?qū)傩浴N覀兣e一個(gè)簡單的??甩恼。
/// 定義MyObject蟀瞧,并且聲明其init方法的返回類型為id。
/// 這也是傳統(tǒng)的使用方法条摸。
@interface MyObject: NSObject
@property (nonatomic, assign) int myValue;
@end
@implementation MyObject
@synthesize myValue;
- (id)init
{
self = super.init;
self.myValue = 10;
return self;
}
@end
// 使用MyObject
int a = MyObject.new.autorelease.myValue;
NSLog(@"a = %d", a);
我們?nèi)绻幾g上述代碼的話悦污,在較新的Apple LLVM上可能仍然沒有問題,但如果在一般的Clang編譯器上編譯的話估計(jì)就會有報(bào)錯(cuò)或者至少會有warning钉蒲。原因就是 id
類型不能表明當(dāng)前對象屬于自己所定義的MyObject類型切端。
因此Apple后來新引入了一個(gè)關(guān)鍵字—— instancetype
。這個(gè)關(guān)鍵字只能作為一個(gè)Objective-C類的類型方法或?qū)嵗椒ǖ姆祷仡愋瓦M(jìn)行使用顷啼,不能用在其他地方踏枣,它指明了當(dāng)前方法所返回的類型即為其自身類型昌屉,以此幫助編譯器做類型自動推導(dǎo),因此 instancetype
也被稱作“關(guān)聯(lián)結(jié)果類型”茵瀑。
在上述代碼中间驮,我們直接把 - (id)init
修改為 -(instancetype)init
即可正常通過編譯。
2. 枚舉可帶有一個(gè)基本類型
這個(gè)語法擴(kuò)展是為了能兼容C++11標(biāo)準(zhǔn)新增的對應(yīng)語法特性马昨。不過各位需要注意的是竞帽,此語法擴(kuò)展只針對Objective-C,而不針對C鸿捧。不過后續(xù)C2X標(biāo)準(zhǔn)也有可能會支持此語法特性屹篓。
這個(gè)語法特性可以比較靈活地配置當(dāng)前枚舉類型的基本類型,從而可以指定該枚舉類型的大小笛谦。下面我們舉一個(gè)簡單的??抱虐。
/// 這里定義了一個(gè)uint8_t基本類型的枚舉,
/// 其每個(gè)枚舉值的類型均為uint8_t類型
enum MyEnum: uint8_t
{
MyEnum_HELLO,
MyEnum_HI
};
// 使用MyEnum
enum MyEnum em = MyEnum_HI;
// 這里可以看到饥脑,這里MyEnum枚舉類型的大小為1字節(jié)
NSLog(@"The size is: %zu", sizeof(em));
這個(gè)語法特性還是挺給力的恳邀。我們之前可能要糾結(jié)是讓枚舉類型作為int類型還是無符號8位整數(shù)類型。但現(xiàn)在無需糾結(jié)了灶轰,我們一般可以讓枚舉仍然保持為默認(rèn)的int類型谣沸,然后在需要的地方將某些枚舉類型定制為8位整數(shù)類型,以節(jié)省存儲空間笋颤。
3. 對象字面量
對象字面量是一個(gè)比較靈活方便的語法糖乳附,它直接將一些數(shù)值類型以及字符串類型的Objective-C對象以字面量的形式給出,而不需要用這些類進(jìn)行構(gòu)建伴澄。除了字符串字面量比較特殊之外赋除,其他類型的對象字面量均為autorelease的對象。而字符串對象則為全局唯一的對象非凌,我們不用擔(dān)心它被自動釋放举农。
當(dāng)前Objective-C直接支持的對象字面量的類型有:NSNumber
,NSString
敞嗡,NSArray
颁糟,NSDictionary
。此外還可以有自定義的對象字面量喉悴,這在Objective-C中也稱為可裝箱表達(dá)式(boxable expression)棱貌。下面我們來一一介紹。
(1) NSNumber
類型:此類型的對象字面量非常簡單箕肃,直接在數(shù)值之前添加 @ 符號即可婚脱。下面以代碼的方式舉例:
// 整數(shù)對象字面量
NSNumber *si = @-100;
// 相當(dāng)于:
si = [NSNumber numberWithInt:-100];
// 無符號整數(shù)對象字面量
NSNumber *ui = @100;
// 相當(dāng)于:
ui = [NSNumber numberWithUnsignedInt:100];
// 長整型對象字面量
NSNumber *sl = @-10000L;
// 相當(dāng)于:
sl = [NSNumber numberWithLong:-10000L];
// 無符號長整型對象字面量
NSNumber *ul = @10000UL;
// 相當(dāng)于:
ul = [NSNumber numberWithUnsignedLong:10000UL];
// long long類型對象字面量
NSNumber *sll = @-1000000LL;
// 相當(dāng)于:
sll = [NSNumber numberWithLongLong:-1000000LL];
// 無符號long long類型對象字面量
NSNumber *ull = @1000000ULL;
// 相當(dāng)于:
ull = [NSNumber numberWithUnsignedLongLong:1000000ULL];
// 字符類型對象字面量
NSNumber *ch = @'a';
// 相當(dāng)于:
ch = [NSNumber numberWithChar:'a'];
// 布爾類型對象字面量
NSNumber *b = @YES;
// 相當(dāng)于:
b = [NSNumber numberWithBool:YES];
// 單精度浮點(diǎn)對象字面量
NSNumber *f = @3.14f;
// 相當(dāng)于
f = [NSNumber numberWithFloat:3.14f];
// 雙精度浮點(diǎn)對象字面量
NSNumber *d = @3.14159;
// 相當(dāng)于
d = [NSNumber numberWithDouble:4.14159];
(2) NSString
類型:這個(gè)類型的對象字面量是我們非常常用的,我們剛接觸Objective-C的時(shí)候就會接觸此對象字面量。
NSString *str = @"Hello, world. 你好世界起惕!";
(3) NSArray
類型:此類型對象字面量也是非常實(shí)用涡贱,其形式如下:
/// 定義了一個(gè)NSArray數(shù)組對象,并用一個(gè)數(shù)組對象字面量對它初始化
NSArray *array = @[@100, @"Hello", @2.5, @NO];
NSLog(@"array[0] = %@", array[0]);
NSLog(@"array[3] = %@", array[3]);
(4) NSDictionary
類型:此類型的對象字面量非常直觀惹想。我們用 @{ } 來封裝一組鍵值對的列表,每個(gè)元素的形式為<key> : <value>督函,相鄰兩個(gè)元素之間用逗號分隔嘀粱。下面我們看個(gè)例子。
NSDictionary *dict = @{
@"key1" : @"value1",
@"key2" : @100,
@3 : @"value3"
};
NSLog(@"key1 value = %@", dict[@"key1"]);
NSLog(@"key2 value = %@", dict[@"key2"]);
NSLog(@"3 value = %@", dict[@3]);
(5) Objective-C可裝箱的表達(dá)式:從Clang3.7開始辰狡,Objective-C引入了可裝箱的表達(dá)式锋叨,可以將任一指定的結(jié)構(gòu)體和聯(lián)合體對象封裝為一個(gè) NSValue
對象。我們可以通過對一個(gè)結(jié)構(gòu)體或聯(lián)合體指定 __attribute__((objc_boxable))
這一屬性來指明該類型是可裝箱的宛篇。對于一個(gè)可裝箱的對象娃磺,我們直接使用 @() 將它包圍即可將它轉(zhuǎn)換為對應(yīng)的Objective-C類型對象锅很。像之前的int湾宙、long、long long姆钉、const char* 等基本類型均屬于可裝箱的對象類型吆倦。下面我們舉些??來說明~
/// 這里定義了MyPoint結(jié)構(gòu)體听诸,并且將它聲明為可裝箱的
struct __attribute__((objc_boxable)) MyPoint
{
float x, y;
};
// 這里定義了MyPoint結(jié)構(gòu)體對象
struct MyPoint mp = { 10.0, -20.0 };
// 這里用一個(gè)NSValue對象對裝箱的mp進(jìn)行引用
NSValue *value = @(mp);
// 我們這里再定義一個(gè)np的MyPoint結(jié)構(gòu)體對象
struct MyPoint np = { };
// 我們把value存放的值輸出給np結(jié)構(gòu)體對象
[value getValue:&np];
NSLog(@"x = %.1f, y = %.1f", np.x, np.y);
int i = 100;
// 對int對象直接裝箱為NSNumber對象
NSNumber *ni = @(i);
NSLog(@"ni = %@", ni);
const char *s = u8"Hello, world!";
// 對s對象直接裝箱為NSString類型
NSString *ns = @(s);
NSLog(@"ns = %@", ns);
4. 基于數(shù)組下標(biāo)的屬性訪問模式
在iOS 6.0以及macOS 10.8之后,Apple引入了一套非正式協(xié)議(informal protocol)與Objective-C語法直接綁定蚕泽。當(dāng)你實(shí)現(xiàn)了這其中的方法之后即可使用數(shù)組下標(biāo)來訪問屬性元素晌梨。
在Foundation庫中,NSArray類實(shí)現(xiàn)了 - (id)objectAtIndexedSubscript:(NSUInteger)idx
方法须妻。因此仔蝌,我們可以這么來訪問數(shù)組元素:
NSArray *arr = @[@100, @200, @300];
NSNumber *num = arr[0];
上述代碼中的arr[0]就相當(dāng)于[arr objectAtIndex:0]。
而NSMutableArray在基于NSArray的基礎(chǔ)上又實(shí)現(xiàn)了 - (void)setObject:(id)anObject atIndexedSubscript:(NSUInteger)index
方法荒吏。這樣我們可以通過數(shù)組下標(biāo)來修改相應(yīng)元素敛惊,比如:
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[@100, @200, @300]];
arr[2] = arr[0];
而NSDictionary類實(shí)現(xiàn)了 - (id)objectForKeyedSubscript:(id)key
方法。這樣我們能以數(shù)組下標(biāo)的形式來訪問相應(yīng)鍵的值司倚。比如:
NSDictionary *dict = @{@"key" : @"value"};
NSString *value = dict[@"key"];
而NSMutableDictionary在NSDictionary類的基礎(chǔ)上又實(shí)現(xiàn)了 - (void)setObject:(id)object forKeyedSubscript:(id < NSCopying >)aKey
方法豆混。這樣,我們能以數(shù)組下標(biāo)的方式來修改相應(yīng)鍵的值动知。比如:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{@"key":"@Hello"}];
dict[dict[@"key"]] = @"world";
下面我們通過實(shí)現(xiàn)這四個(gè)方法皿伺,自己實(shí)現(xiàn)一個(gè)能同時(shí)使用這四種下標(biāo)方式訪問模式的類。
//
// main.m
// objCTest
//
// Created by Zenny Chen on 12-2-7.
// Copyright (c) 2014年 GreenGames Studio. All rights reserved.
//
@import Foundation;
@interface MyContainer : NSObject
{
@private
NSMutableDictionary *mDict;
NSMutableArray *mArray;
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)aKey;
- (id)objectForKeyedSubscript:(id)key;
- (void)setObject:(id)anObject atIndexedSubscript:(NSUInteger)index;
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
@end
@implementation MyContainer
- (instancetype)init
{
self = super.init;
mDict = [NSMutableDictionary.alloc initWithDictionary:@{@"key1":@"value1", @"key2":@"value2"}];
mArray = [NSMutableArray.alloc initWithArray:@[@100, @200, @300, @400]];
return self;
}
- (void)dealloc
{
if(mDict != nil)
{
[mDict removeAllObjects];
[mDict release];
mDict = nil;
}
if(mArray != nil)
{
[mArray removeAllObjects];
[mArray release];
mArray = nil;
}
[super dealloc];
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)aKey
{
[mDict setObject:object forKey:aKey];
}
- (id)objectForKeyedSubscript:(id)key
{
return [mDict objectForKey:key];
}
- (void)setObject:(id)anObject atIndexedSubscript:(NSUInteger)index
{
const NSUInteger length = mArray.count;
if(index > length)
return;
if(index == length)
[mArray addObject:anObject];
else
[mArray replaceObjectAtIndex:index withObject:anObject];
}
- (id)objectAtIndexedSubscript:(NSUInteger)idx
{
if(idx >= mArray.count)
return nil;
return [mArray objectAtIndex:idx];
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool
{
// insert code here...
MyContainer *cont = MyContainer.new;
cont[@"mykey"] = @"myvalye";
NSLog(@"key1 is: %@", cont[@"key1"]);
NSLog(@"key2 is: %@", cont[@"key2"]);
NSLog(@"mykey is: %@", cont[@"mykey"]);
cont[4] = @500;
cont[2] = @-300;
NSLog(@"The value[4] = %@", cont[4]);
NSLog(@"The value[3] = %@", cont[3]);
NSLog(@"The value[2] = %@", cont[2]);
}
return 0;
}