編寫(xiě)Objective-C程序時(shí),總會(huì)用到幾個(gè)類(lèi),它們屬于Foundation框架。雖然從技術(shù)上來(lái)說(shuō),不用Foundation框架也能寫(xiě)出Objective-C代碼,但實(shí)際上卻經(jīng)常要用到此框架。這幾個(gè)類(lèi)是NSString、NSNumber、NSArray、NSDictionary。從類(lèi)名上即可看出各自所表示的數(shù)據(jù)結(jié)構(gòu)。
Objective-C以語(yǔ)法繁雜而著稱(chēng)。事實(shí)上的確是這樣。不過(guò),從Objective-C1.0起,有一種非常簡(jiǎn)單的方式能創(chuàng)建NSString對(duì)象。這就是"字符串字面量"(string literal),其語(yǔ)法如下:
NSString *someString = @"Effective Objective-C 2.0";
如果不用這種語(yǔ)法的話(huà),就要以常見(jiàn)的alloc及init方法來(lái)分配并初始化NSString對(duì)象了。在版本較新的編譯器中,也能用這種字面量語(yǔ)法來(lái)聲明NSNumber、NSArray、NSDictionary類(lèi)的實(shí)例。使用字面量語(yǔ)法(literal syntax)可以縮減源代碼長(zhǎng)度,使其更為已讀。
字面數(shù)值
有時(shí)需要把整數(shù)、浮點(diǎn)數(shù)、布爾值封入Objective-C對(duì)象中。這種情況下可以用NSNumber類(lèi),該類(lèi)可處理多種類(lèi)型的數(shù)值。若是不用字面量,那么就需要按下述方式創(chuàng)建實(shí)例:
NSNumber *someNumber = [NSNumber numberWithInt:1];
上面這行代碼創(chuàng)建了一個(gè)數(shù)字,將其設(shè)為整數(shù)1。然而使用字面量能令其代碼更為整潔:
NSNumber *someNumber = @1;
可以看到,字面量語(yǔ)法更為精簡(jiǎn)。不過(guò)它還有很多好處。能夠以NSNumber實(shí)例表示的所有數(shù)據(jù)類(lèi)型都可使用該語(yǔ)法。例如:
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
字面量語(yǔ)法也適用于下述表達(dá)式:
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
以字面量來(lái)表示數(shù)值十分有用。這樣做可以令NSNumber對(duì)象變得整潔,因?yàn)槁暶髦兄话瑪?shù)值,而沒(méi)有多余的語(yǔ)法成分。
字面量數(shù)組
數(shù)組是常用的數(shù)據(jù)結(jié)構(gòu)。如果不使用字面量語(yǔ)法,那么就要這樣來(lái)創(chuàng)建數(shù)組:
NSArray *animals = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
而使用字面量語(yǔ)法來(lái)創(chuàng)建則是:
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
上面這種做法不僅簡(jiǎn)單,而且還利于操作數(shù)組。數(shù)組的常見(jiàn)操作就是取某個(gè)下標(biāo)所對(duì)應(yīng)的對(duì)象,這用字面量來(lái)做更為容易。如果不用字面量,那么通常會(huì)用"objectAtIndex:"方法:
NSString *dog = [animals objectAtIndex:1];
若使用字面量,則是:
NSString *dog = animals[1];
這也叫做"取下標(biāo)"操作(subscripting),與使用字面量語(yǔ)法的其他情況一樣,這種方式也更為簡(jiǎn)潔、更易理解,而且與其他語(yǔ)言中依下標(biāo)來(lái)訪(fǎng)問(wèn)數(shù)組元素時(shí)所使用的語(yǔ)法類(lèi)似。
不過(guò),用字面量語(yǔ)法實(shí)際上只是一種"語(yǔ)法糖"(syntactic sugar),其效果等于是先創(chuàng)建了一個(gè)數(shù)組,然后把方括號(hào)內(nèi)的所有對(duì)象都加到這個(gè)數(shù)組中。拋出的異常會(huì)是這樣:
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:'***
- [__NSPlaceholderArray initWithObjects:count:] attempt to insert nil object from objects[0]'
在改用字面量語(yǔ)法來(lái)創(chuàng)建數(shù)組時(shí)就會(huì)遇到這個(gè)問(wèn)題。下面這段代碼分別以?xún)煞N語(yǔ)法創(chuàng)建數(shù)組:
id object1 = /* ... */;
id object2 = /* ... */;
id object3 = /* ... */;
NSArray *arrayA = [NSArray arrayWithObjects:object1, object2, object3,nil];
NSArray *arrayB = @[object1, object2, object3];
如果object1與object3都指向了有效的Objective-C對(duì)象,而object2是nil,那么會(huì)出現(xiàn)什么情況呢?按字面量語(yǔ)法創(chuàng)建數(shù)組arrayB時(shí)會(huì)拋出異常。arrayA雖然能創(chuàng)建出來(lái),但是其中卻含有object1一個(gè)對(duì)象。原因在于,"arrayWithObjects:"方法會(huì)依次處理各個(gè)參數(shù),直到發(fā)現(xiàn)nil為止,由于object2是nil,所以該方法會(huì)提前結(jié)束。
這個(gè)微妙的差別表明,使用字面量語(yǔ)法更為安全。拋出異常令應(yīng)用程序終止執(zhí)行,這比創(chuàng)建好數(shù)組之后才發(fā)現(xiàn)元素個(gè)數(shù)少了要好。向數(shù)組中插入nil通常說(shuō)明程序有錯(cuò),而通過(guò)異常可以更快地發(fā)現(xiàn)這個(gè)錯(cuò)誤。
字面量字典
"字典"(Dictionary)是一種映射型數(shù)據(jù)結(jié)構(gòu),可向其中添加鍵值對(duì)。與數(shù)組一樣,Objective-C代碼也經(jīng)常用到字典。其創(chuàng)建方式如下:
NSDictionary *personData = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt", @"firstName", @"Galloway", @"lastName", [NSNumber numberWithInt:28, @"age", nil];
這樣寫(xiě)令人困惑,因?yàn)槠漤樞蚴?lt;對(duì)象>, <鍵>,<對(duì)象>,<鍵>。這與通常理解的順序相反,我們一般認(rèn)為是把"鍵"映射到"對(duì)象"。因此,這樣寫(xiě)法不容易讀懂。如果改用字面量語(yǔ)法,就清晰多了:
NSDictionary *personData = @{@"firstName" : @"Matt", @"lastName" : @"Galloway", @"age" : @28};
上面這種寫(xiě)法更簡(jiǎn)明,而且鍵出現(xiàn)在對(duì)象之前,理解起來(lái)較順暢。此范例代碼還說(shuō)明了使用字面量數(shù)值的好處。字典中的對(duì)象和鍵必須都是Objective-C對(duì)象,所以不能把整數(shù)28直接放進(jìn)去,而要將其封裝在NSNumber實(shí)例中才行。使用字面量語(yǔ)法很容易就能做到這一點(diǎn),只需給數(shù)字前加一個(gè)@字符即可。
與數(shù)組一樣,用字面量語(yǔ)法創(chuàng)建字典時(shí)也有個(gè)問(wèn)題,那就是一旦有值為nil,便會(huì)拋出異常。不過(guò)基于同樣的問(wèn)題,這也是個(gè)好事。假如在創(chuàng)建字典時(shí)不小心用了空值對(duì)象,那么"dictionaryWithObjectsAndKeys:"方法就會(huì)在首個(gè)nil之前停下,并拋出異常,這有助于查錯(cuò)。
字典也可以像數(shù)組那樣用字面量語(yǔ)法訪(fǎng)問(wèn)。按照特定鍵訪(fǎng)問(wèn)其值的傳統(tǒng)做法是:
NSString *lastName = [personData objectForKey:@"lastName"];
與之等效的字面量語(yǔ)法則是:
NSString *lastName = personData[@"lastName"];
這樣寫(xiě)也省去了冗贅的語(yǔ)法,令此行代碼簡(jiǎn)單易讀。
可變數(shù)組與字典
通過(guò)取下標(biāo)操作,可以訪(fǎng)問(wèn)數(shù)組中某個(gè)下標(biāo)或字典中某個(gè)鍵所對(duì)應(yīng)的元素。如果數(shù)組與字典對(duì)象是可變的(mutable),那么也能通過(guò)下標(biāo)修改其中的元素值。修改可變數(shù)組與字典內(nèi)容的標(biāo)準(zhǔn)做法是:
[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableDictionary setObject:@"Galloway" forKey:@"lastName"];
若換用取下標(biāo)操作來(lái)寫(xiě),則是:
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
局限性
字面量語(yǔ)法有個(gè)小小的限制,就是除了字符串以外,所創(chuàng)建出來(lái)的對(duì)象必須屬于Foundation框架才行。如果自定義了這些類(lèi)的子類(lèi),則無(wú)法用字面量語(yǔ)法創(chuàng)建其對(duì)象。要想創(chuàng)建自定義子類(lèi)的實(shí)例,必須采用"非字面量語(yǔ)法"(nonliteral syntax)。然而,由于NSArray、NSDictionary、NSNumber都是業(yè)已定型的"子族"(class cluster),因此很少有人會(huì)從其中自定義子類(lèi),真要那樣做也比較麻煩。而且一般來(lái)說(shuō),標(biāo)準(zhǔn)的實(shí)現(xiàn)已經(jīng)很好了,無(wú)須再改動(dòng)。創(chuàng)建字符串時(shí)可以使用自定義的子類(lèi),然而必須要修改編譯器的選項(xiàng)才行。除非你明白這樣做的后果,否則不鼓勵(lì)使用此選項(xiàng),用NSString就足夠了。
使用字面量語(yǔ)法創(chuàng)建出來(lái)的字符串、數(shù)組、字典對(duì)象都是不可變得(immutable)。若想要可變版本的對(duì)象,則需復(fù)制一份:
NSMutableArray *mutable = [@[@1, @2, @3, @4, @5] mutableCopy]
這么做會(huì)多調(diào)用一個(gè)方法,而且還要再創(chuàng)建一個(gè)對(duì)象,不過(guò)使用字面量語(yǔ)法所帶來(lái)的好處還是多于上述缺點(diǎn)的。