自O(shè)bjective-C 2.0以來的新增語法特性

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直接支持的對象字面量的類型有:NSNumberNSString敞嗡,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;
}

還有其他一些諸如ARC盒粮、Module鸵鸥、輕量級泛型等語法,各位可以在其他博客或官方文檔上查閱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妒穴,一起剝皮案震驚了整個(gè)濱河市宋税,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讼油,老刑警劉巖杰赛,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矮台,居然都是意外死亡乏屯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門瘦赫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辰晕,“玉大人,你說我怎么就攤上這事确虱『眩” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵校辩,是天一觀的道長窘问。 經(jīng)常有香客問我,道長召川,這世上最難降的妖魔是什么南缓? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮荧呐,結(jié)果婚禮上汉形,老公的妹妹穿的比我還像新娘。我一直安慰自己倍阐,他們只是感情好概疆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峰搪,像睡著了一般岔冀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上概耻,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天使套,我揣著相機(jī)與錄音,去河邊找鬼鞠柄。 笑死侦高,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的厌杜。 我是一名探鬼主播奉呛,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼计螺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞧壮?” 一聲冷哼從身側(cè)響起登馒,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咆槽,沒想到半個(gè)月后陈轿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡罗晕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年济欢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片小渊。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖茫叭,靈堂內(nèi)的尸體忽然破棺而出酬屉,到底是詐尸還是另有隱情,我是刑警寧澤揍愁,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布呐萨,位于F島的核電站,受9級特大地震影響莽囤,放射性物質(zhì)發(fā)生泄漏谬擦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一朽缎、第九天 我趴在偏房一處隱蔽的房頂上張望惨远。 院中可真熱鬧,春花似錦话肖、人聲如沸北秽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贺氓。三九已至,卻和暖如春床蜘,著一層夾襖步出監(jiān)牢的瞬間辙培,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工邢锯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扬蕊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓弹囚,卻偏偏與公主長得像厨相,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容