賦值與集合 - Values and Collections

2018-08-12 21:19 更新

賦值與集合 - Values and Collections

盡管 Objective-C 是一種面向?qū)ο蟮木幊陶Z(yǔ)言,它也是C語(yǔ)言的加強(qiáng)版,這意味著你可以在 Objective-C 中使用標(biāo)準(zhǔn) C 中任意的純量類型(非對(duì)象的),例如 int,float,char。在 Cocoa 和 Cocoa Touch 應(yīng)用中,你還可以使用一些額外的類型,例如 NSInteger,NSUInteger 和 CGFloat,在不同的系統(tǒng)結(jié)構(gòu)中,他們的定義方式也不同。

純量類型用于當(dāng)你不需要用一個(gè)對(duì)象來(lái)表示值的時(shí)候。字符型經(jīng)常作為 NSString 的實(shí)例而使用,數(shù)值被存儲(chǔ)在純量類型的局部變量或?qū)傩灾小?/p>

你可以在 Objective-C 中定義與 C 語(yǔ)言類似的數(shù)組,但是在 Cocoa 和 Cocoa Touch 應(yīng)用中,集合被用于實(shí)例化像 NSArray 或 NSDictionary 這樣的類。這些類只能存放對(duì)象,這意味著在添加對(duì)象到集合之前,你就要用 NSValue, NSNumber 或 NSString 這樣的類為對(duì)象賦好值。

在前面的章節(jié)中,我們多次使用了 NSString 類、它的初始化、方法函數(shù)庫(kù),@"string" 字符為創(chuàng)建 NSString 的實(shí)例提供了簡(jiǎn)單的語(yǔ)法。在本章中,我們會(huì)通過(guò)方法調(diào)用和賦值語(yǔ)句來(lái)示范如何使用 NSValue 和 NSNumber 類。

你可以使用C中的基本數(shù)據(jù)類型

在 Objective-C 中,C 的每一個(gè) scalar 變量類型都是可以使用的:


    int someInteger = 42;
    float someFloatingPointNumber = 3.1415;
    double someDoublePrecisionFloatingPointNumber = 6.02214199e23;

還有 C 的操作符也都可用:


    int someInteger = 42;
    someInteger++;            // someInteger == 43

    int anotherInteger = 64;
    anotherInteger--;         // anotherInteger == 63

    anotherInteger *= 2;      // anotherInteger == 126

如果你要為 Objective-C 屬性使用純量類型 ,請(qǐng)這樣做:


    @interface XYZCalculator : NSObject
    @property double currentValue;
    @end

你也可以對(duì)屬性使用 C 操作符,用點(diǎn)語(yǔ)法進(jìn)行賦值操作,就像這樣:


    @implementation XYZCalculator
    - (void)increment {
        self.currentValue++;
    }
    - (void)decrement {
    self.currentValue--;
    }
    - (void)multiplyBy:(double)factor {
    self.currentValue *= factor;
    }
    @end

點(diǎn)語(yǔ)法是一種純粹用于存取器(accessor)調(diào)用方法的語(yǔ)法,所以這個(gè)例子中的每一條操作都是先使用 get accessor 方法獲取實(shí)例變量值,運(yùn)行程序后,再使用 set accessor 方法存儲(chǔ)實(shí)例變量值作為結(jié)果。

Objective-C中其他的基本類型

BOOL 類型在 Objective-C 中用來(lái)表示布爾值:yes 和 no。正如你想的那樣,yes 在邏輯上等于 true 和 1,no 等于 false 和 0;

在 Cocoa 和 Cocoa Touch 中,許多方法的參數(shù)也可以使用特殊的數(shù)據(jù)類型,例如 NSInteger 和 CGFloat。

例如, NSTableViewDataSource 和 UITableViewDataSource 協(xié)議(在之前章節(jié)提到的)都有令數(shù)據(jù)按行顯示的方法:


    @protocol NSTableViewDataSource <NSObject>
    - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
    ...
    @end

像 NSInteger 和 NSUInteger 這樣的類型,在不同的系統(tǒng)結(jié)構(gòu)中有不同的定義方式。當(dāng)為 32 位運(yùn)行環(huán)境(例如 IOS)編程時(shí),他們分別是 32 位的有符號(hào)整數(shù)和無(wú)符號(hào)整數(shù);當(dāng)為 64 位運(yùn)行環(huán)境(例如 modern OS X runtime)編程時(shí),他們分別是 64 位的有符號(hào)整數(shù)和無(wú)符號(hào)整數(shù)。

如果你希望在 API 邊界間(包括內(nèi)外部 API)傳值,最好使用這種特定平臺(tái)的類型,例如在你的應(yīng)用代碼和平臺(tái)架構(gòu)間,利用方法、函數(shù)調(diào)用傳遞參數(shù)或返回?cái)?shù)值。

對(duì)于局部變量,例如循環(huán)中的計(jì)數(shù)值,如果你知道該值作用域的限制,使用 C 的基本類型定義它也是可以的。

C的結(jié)構(gòu)可以存儲(chǔ)基本數(shù)據(jù)

一些 Cocoa and Cocoa Touch API 使用 C 的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)數(shù)值。例如,一個(gè)字符串對(duì)象可以作為一個(gè)子字符串的取值域,就像這樣:


    NSString *mainString = @"This is a long string";

    NSRange substringRange = [mainString rangeOfString:@"long"];

一個(gè) NSRange 結(jié)構(gòu)保存了一個(gè)地址和一個(gè)長(zhǎng)度值。在這個(gè)例子中,substringRange 中會(huì)保存 {10,4} 這兩個(gè)值:以 0 開(kāi)始計(jì)算的位置,主字符串中的第 10 個(gè)字母為子字符串 @"long" 的第 1 個(gè)字母,@"long" 的長(zhǎng)度為 4。

相似的是,如果你要編寫(xiě)自定義圖形代碼,你需要使用 Quartz,它要使用以 CGFloat 數(shù)據(jù)類型為基礎(chǔ)的結(jié)構(gòu),就像 OS X 上的 NSPoint 和 NSSize ,IOS 上的 CGPoint 和 CGSize。CGFloat 也同樣在不同的系統(tǒng)結(jié)構(gòu)上有不同的定義方式。

想看更多關(guān)于 Quartz 2D 繪制機(jī)制?點(diǎn)擊Quartz 2D Programming Guide

對(duì)象可以表示基本數(shù)值

如果你需要用 scalar 表示一個(gè)對(duì)象的值,你可以使用 Cocoa 和 Cocoa Touch 提供的基礎(chǔ)賦值類,例如我們?cè)谙乱粋€(gè)部分里面提到的集合類。

通過(guò)NSString類的實(shí)例表示Strings

就像之前章節(jié)里面提到的,NSString 是用來(lái)表示字符串的,例如 Hello World。創(chuàng)建 NSString 對(duì)象有很多種方式,包括分配空間并初始化,使用工廠方法或者純語(yǔ)法:


    NSString *firstString = [[NSString alloc] initWithCString:"Hello World!"

    encoding:NSUTF8StringEncoding];

    NSString *secondString = [NSString stringWithCString:"Hello World!"

    encoding:NSUTF8StringEncoding];

    NSString *thirdString = @"Hello World!";

每一個(gè)例子都高效的完成了同一件事:創(chuàng)建了一個(gè)字符串對(duì)象,其值為給定內(nèi)容。

基本 NSString 類是不可變的,它的內(nèi)容在創(chuàng)建時(shí)設(shè)好就不能再改變了。如果你想要一個(gè)不同的字符串,你必須創(chuàng)建一個(gè)新的對(duì)象,就像這樣:


    NSString *name = @"John";

    name = [name stringByAppendingString:@"ny"]; // returns a new string object

NSMutableString 類是 NSString 的可變子類,允許在方法中修改,與 appendString 和 appendFormat 相似:


    NSMutableString *name = [NSMutableString stringWithString:@"John"];

    [name appendString:@"ny"];   // same object, but now represents "Johnny"

使用Format Strings創(chuàng)建來(lái)自其他對(duì)象和值的Strings

如果你需要?jiǎng)?chuàng)建一個(gè)帶有變量的字符串,你要使用 format string。它允許你使用格式符來(lái)表示值是如何插入的:


    int magicNumber = ...
    NSString *magicString = [NSString stringWithFormat:@"The magic number is %i", magicNumber];

可用的格式符在這里String Format Specifiers。想了解更多關(guān)于strings,請(qǐng)點(diǎn)擊String Programming Guide。

通過(guò)NSNumber類的實(shí)例表示數(shù)字

NSNumber 類用來(lái)表示所有 C 基礎(chǔ)的 scalar 類型,包括 char,double,float,int,long,short 以及它們的無(wú)符號(hào)變量,還有 Objective-C 中的布爾類型,BOOL。

使用 NSString,你有很多種方式可以創(chuàng)建 NSNumber 實(shí)例,包括空間分配并初始化,以及使用工廠方法:


    NSNumber *magicNumber = [[NSNumber alloc] initWithInt:42];

    NSNumber *unsignedNumber = [[NSNumber alloc] initWithUnsignedInt:42u];

    NSNumber *longNumber = [[NSNumber alloc] initWithLong:42l];

    NSNumber *boolNumber = [[NSNumber alloc] initWithBOOL:YES];

    NSNumber *simpleFloat = [NSNumber numberWithFloat:3.14f];

    NSNumber *betterDouble = [NSNumber numberWithDouble:3.1415926535];

    NSNumber *someChar = [NSNumber numberWithChar:'T'];

你也可以用 Objective-C 中的語(yǔ)法來(lái)創(chuàng)建 NSNumber 實(shí)例:


    NSNumber *magicNumber = @42;

    NSNumber *unsignedNumber = @42u;

    NSNumber *longNumber = @42l;

    NSNumber *boolNumber = @YES;

    NSNumber *simpleFloat = @3.14f;

    NSNumber *betterDouble = @3.1415926535;

    NSNumber *someChar = @'T';

這些例子都等同于使用 NSNumber 類的工廠方法。

一旦你已經(jīng)創(chuàng)建了一個(gè) NSNumber 實(shí)例,你就可以使用 accessor 方法申請(qǐng)一個(gè)純量值:


    int scalarMagic = [magicNumber intValue];

    unsigned int scalarUnsigned = [unsignedNumber unsignedIntValue];

    long scalarLong = [longNumber longValue];

    BOOL scalarBool = [boolNumber boolValue];

    float scalarSimpleFloat = [simpleFloat floatValue];

    double scalarBetterDouble = [betterDouble doubleValue];

    char scalarChar = [someChar charValue];

NSNumber 類也為 Objective-C 額外的基本類型提供了方法。如果你想創(chuàng)建 NSInteger 和 NSUInteger 的對(duì)象,請(qǐng)使用如下方法:


    NSInteger anInteger = 64;

    NSUInteger anUnsignedInteger = 100;

    NSNumber *firstInteger = [[NSNumber alloc] initWithInteger:anInteger];

    NSNumber *secondInteger = [NSNumber numberWithUnsignedInteger:anUnsignedInteger];

    NSInteger integerCheck = [firstInteger integerValue];

    NSUInteger unsignedCheck = [secondInteger unsignedIntegerValue];

所有的 NSNumber 實(shí)例都是不可變的,并且它也沒(méi)有可變的子類;如果你需要一個(gè)不同的數(shù)字,請(qǐng)使用另一個(gè) NSNumber 實(shí)例。

提示

NSNumber 實(shí)際上是一個(gè)類集。這意味著當(dāng)你在運(yùn)行時(shí)創(chuàng)建了一個(gè)實(shí)例時(shí),你會(huì)得到一個(gè)合適的具體化的子類,值為給定值。只把創(chuàng)建的對(duì)象當(dāng)做一個(gè) NSNumber 的實(shí)例就可以了。

使用NSValue類的實(shí)例表示其他值

NSNumber 類是基礎(chǔ) NSValue 類的子類,它給單獨(dú)的數(shù)據(jù)或數(shù)據(jù)項(xiàng)提供了包裝對(duì)象。除了 C 的基本純量類型,NSValue 也可以表示指針和結(jié)構(gòu)體。

NSValue 類提供了很多的工廠方法來(lái)創(chuàng)建一個(gè)給定結(jié)構(gòu)的值,這使得創(chuàng)建一個(gè)實(shí)例變得十分簡(jiǎn)單,例如之前例子中的 NSRange:


    NSString *mainString = @"This is a long string";

    NSRange substringRange = [mainString rangeOfString:@"long"];

    NSValue *rangeValue = [NSValue valueWithRange:substringRange];

你也可以使用 NSValue 來(lái)創(chuàng)建自定義的結(jié)構(gòu)體對(duì)象。如果你一定要用 C 中結(jié)構(gòu)體存儲(chǔ)信息,請(qǐng)這樣做:


    typedef struct {
        int i;
        float f;
    } MyIntegerFloatStruct;

你可以通過(guò)指向結(jié)構(gòu)體的指針或編好的 Objective-C 類型來(lái)創(chuàng)建一個(gè) NSValue 實(shí)例。@encode() 這條編譯器指令是用來(lái)創(chuàng)建正確的 Objective-C 類型的,如:


    struct MyIntegerFloatStruct aStruct;

    aStruct.i = 42;

    aStruct.f = 3.14;

    NSValue *structValue = [NSValue value:&aStruct
                             withObjCType:@encode(MyIntegerFloatStruct)];

標(biāo)準(zhǔn) C 中的 & 符號(hào)在這里表示 aStruct 中 value 參數(shù)的地址。

大多數(shù)集合都是對(duì)象

盡管你可以使用 C 中的數(shù)組存放 scalar 數(shù)據(jù)的集合,甚至還可以存放對(duì)象指針,大多數(shù) Objective-C 代碼中的集合都是 Cocoa 和 Cocoa Touch 集合類中的一個(gè),例如 NSArray, NSSet 和 NSDictionary。

這些類用來(lái)管理一組對(duì)象,這意味著你加入集合的任何一項(xiàng)都必須是 Objective-C 類的實(shí)例。如果你需要添加一個(gè) scalar 值,你必須要先創(chuàng)建一個(gè)合適的 NSNumber 或 NSValue 實(shí)例。

集合類使用強(qiáng)大的引用持續(xù)追蹤他們的內(nèi)容,而不是不管怎樣都為集合中的每一個(gè)對(duì)象做一份拷貝。這意味著你加入集合中的每一個(gè)對(duì)象的生命周期都將至少和集合的生命周期一樣長(zhǎng), 正如這里描述的Manage the Object Graph through Ownership and Responsibility

除了追蹤他們的內(nèi)容,Cocoa 和 Cocoa Touch 中的每一個(gè)集合類都可以簡(jiǎn)單地完成一些任務(wù),例如列舉,存取特定的項(xiàng),查找某一個(gè)對(duì)象是否在這個(gè)集合中。

基本的 NSArray, NSSet 和 NSDictionary 類都是不可變的,它們的值在創(chuàng)建時(shí)就固定了。但它們都擁有可變的子類,你可以通過(guò)子類來(lái)添加和刪除對(duì)象。

想查看更多 Cocoa 和 Cocoa Touch 中的集合類,請(qǐng)點(diǎn)擊Collections Programming Topics

數(shù)組是有序的集合

NSArray 是用來(lái)表示對(duì)象的有序集合。對(duì)集合中內(nèi)容唯一的要求是每一項(xiàng)都要是 Obejctive-C 的對(duì)象,但它們并不需要是同一個(gè)類的實(shí)例。

為了滿足有序,每一個(gè)元素的存儲(chǔ)都是從 0 開(kāi)始記序,就像圖 6-1 這樣。

圖6-1 Objective-C 對(duì)象數(shù)組

創(chuàng)建數(shù)組

就像之前章節(jié)里寫(xiě)的賦值類那樣,你可以通過(guò)分配空間并初始化,使用工廠類方法或純語(yǔ)法方式來(lái)創(chuàng)建數(shù)組。

初始化方法和工廠方法有很多種,你可以根據(jù)對(duì)象的個(gè)數(shù)來(lái)選擇不同的方法:


    + (id)arrayWithObject:(id)anObject;

    + (id)arrayWithObjects:(id)firstObject, ...;

    - (id)initWithObjects:(id)firstObject, ...;

arrayWithObjects 和 initWithObjects: 這兩種方法都采用零終止(nil-terminated),參數(shù)個(gè)數(shù)是可變的,這意味著你數(shù)組的最后一個(gè)值必須是 nil:


    NSArray *someArray =
    [NSArray arrayWithObjects:someObject, someString, someNumber, someValue, nil];

這個(gè)例子創(chuàng)建了一個(gè)類似圖6-1中的數(shù)組。第一個(gè)對(duì)象:someObject,下標(biāo)是 0;最后一個(gè)對(duì)象:someValue,下標(biāo)是 3。

如果數(shù)組中有一個(gè)值為 nil,那么這個(gè)數(shù)組會(huì)在這里被截?cái)啵?/p>


    id firstObject = @"someString";

    id secondObject = nil;

    id thirdObject = @"anotherString";

    NSArray *someArray =
    [NSArray arrayWithObjects:firstObject, secondObject, thirdObject, nil];

在這里,someArray 數(shù)組只包含 firstObject,因?yàn)榈诙€(gè)元素為 nil,系統(tǒng)認(rèn)為它是數(shù)組的結(jié)束。

語(yǔ)法

你可以用 Objective-C 的語(yǔ)法創(chuàng)建數(shù)組:


    NSArray *someArray = @[firstObject,secondObject, thirdObject];

在這里你不能用 nil 來(lái)結(jié)束數(shù)組,事實(shí)上 nil 是非法的值。如果你運(yùn)行下面的代碼,系統(tǒng)會(huì)拋出異常:


    id firstObject = @"someString";

    id secondObject = nil;

    NSArray *someArray = @[firstObject, secondObject];
    // exception: "attempt to insert nil object"

如果你一定要使用 nil 值,你可以用 NSNull 類,詳情請(qǐng)查看Represent nil with NSNull

查找數(shù)組對(duì)象

一旦你創(chuàng)建了數(shù)組,你可以查詢有關(guān)的信息,例如對(duì)象個(gè)數(shù)或查找特定的元素:


    NSUInteger numberOfItems = [someArray count];

    if ([someArray containsObject:someString]) {
        ...
    }

你也可以查詢指定下標(biāo)的元素。如果你查詢的下標(biāo)數(shù)是錯(cuò)誤的,系統(tǒng)會(huì)拋出越界異常,所以你應(yīng)該首先確認(rèn)數(shù)組長(zhǎng)度:


    if ([someArray count] > 0) {
        NSLog(@"First item is: %@", [someArray objectAtIndex:0]);
    }

這個(gè)例子檢查了數(shù)組長(zhǎng)度是否大于 0。如果大于 0,它會(huì)記錄下第一個(gè)元素,也就是下標(biāo)為 0 的元素信息。

下標(biāo)

還有另一種下標(biāo)寫(xiě)法,使用 objectAtIndex:這與 C 中數(shù)組寫(xiě)法十分像。使用這種方法重寫(xiě)前面的例子:


    if ([someArray count] > 0) {
        NSLog(@"First item is: %@", someArray[0]);
    }

給數(shù)組對(duì)象排序

NSArray 類提供了很多種排序的方法。因?yàn)?NSArray 是不可變的,所以所有的方法都會(huì)返回一個(gè)有序數(shù)組。

例如,你可以通過(guò)調(diào)用 compare 給字符串排序:


    NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"];
    NSArray *sortedStrings =
                 [unsortedStrings sortedArrayUsingSelector:@selector(compare:)];

可變性

盡管 NSArray 類是不可變的,這并不會(huì)影響集合中的對(duì)象。如果你想添加一個(gè)可變的字符串,可以這樣做:


    NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"];
    NSArray *immutableArray = @[mutableString];

沒(méi)有什么可以阻止你改變它:


    if ([immutableArray count] > 0) {
        id string = immutableArray[0];
        if ([string isKindOfClass:[NSMutableString class]]) {
            [string appendString:@" World!"];
        }
    }

使用 NSMutableArray 增刪初始化后的數(shù)組對(duì)象,它提供了很多個(gè)方法添加、刪除和替換一個(gè)或多個(gè)對(duì)象:


    NSMutableArray *mutableArray = [NSMutableArray array];

    [mutableArray addObject:@"gamma"];

    [mutableArray addObject:@"alpha"];

    [mutableArray addObject:@"beta"];

    [mutableArray replaceObjectAtIndex:0 withObject:@"epsilon"];

這個(gè)例子創(chuàng)建了以 @"epsilon", @"alpha",@"beta" 結(jié)尾的數(shù)組。

不用創(chuàng)建一個(gè)新的數(shù)組也可以給可變數(shù)組排序:


    [mutableArray sortUsingSelector:@selector(caseInsensitiveCompare:)];

這使得該數(shù)組以字母升序排序:@"alpha", @"beta", @"epsilon"。

無(wú)序集合:Sets

NSSet和數(shù)組很相似,但是它可以存儲(chǔ)不同對(duì)象的無(wú)序組,如圖6-2

圖6-2 一組對(duì)象

因?yàn)?sets 是無(wú)序的,所以當(dāng)涉及到測(cè)試成員時(shí),sets 比數(shù)組更有優(yōu)勢(shì)。

基礎(chǔ)的 NSSet 類也是不可變的,所以它的值必須在創(chuàng)建時(shí)確定,使用空間分配并初始化或工廠方法:


    NSSet *simpleSet =
      [NSSet setWithObjects:@"Hello, World!", @42, aValue, anObject, nil];

和 NSArray, initWithObjects:,setWithObjects: 方法一樣,NSSet 以 nil 作為結(jié)束,參數(shù)個(gè)數(shù)可變??勺兊?NSSet 的子類是 NSMutableset。

Sets 中每一個(gè)對(duì)象只能存儲(chǔ)一個(gè)引用,所以你不能多次添加同一個(gè)對(duì)象:


    NSNumber *number = @42;

    NSSet *numberSet =
      [NSSet setWithObjects:number, number, number, number, nil];
    // numberSet only contains one object

查看更多關(guān)于sets: Sets: Unordered Collections of Objects

字典是一種存儲(chǔ)鍵值對(duì)的集合

NSDictionary 存儲(chǔ)了對(duì)象和它們的關(guān)鍵字,而不是單單只保存了一個(gè)有序或無(wú)序的集合,這個(gè)功能可以用來(lái)恢復(fù)信息。

最好使用字符串作為字典的關(guān)鍵字,如圖6-3:

圖6-3 對(duì)象詞典

提示

使用其他對(duì)象作為關(guān)鍵字也可以,但值得注意的是關(guān)鍵字需要被字典復(fù)制,所以你的關(guān)鍵字一定要支持 NSCopying。

如果你的代碼包含鍵值,你必須用字符串關(guān)鍵字作為字典對(duì)象,正如這里提到的: Key-Value Coding Programming Guide

字典的創(chuàng)建

你可以用空間分配并初始化或工廠方法創(chuàng)建字典:


    NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                   someObject, @"anObject",
             @"Hello, World!", @"helloString",
                          @42, @"magicNumber",
                    someValue, @"aValue",
                             nil];

注意使用 dictionaryWithObjectsAndKeys: 和 initWithObjectsAndKeys:方法時(shí),每個(gè)對(duì)象在給定關(guān)鍵字前需要確定好,對(duì)象和關(guān)鍵字都必須以 nil 結(jié)束。

語(yǔ)法

Objective-C 也可以使用純語(yǔ)法創(chuàng)建字典:


    NSDictionary *dictionary = @{
                  @"anObject" : someObject,
               @"helloString" : @"Hello, World!",
               @"magicNumber" : @42,
                    @"aValue" : someValue
    };

注意在字典語(yǔ)法中,關(guān)鍵字需要先確定,然后再確定對(duì)象,而且關(guān)鍵字不能以 nil 結(jié)束。

字典的查詢

一旦你創(chuàng)建了字典,你可以從中給對(duì)象指定一個(gè)關(guān)鍵字:


    NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];

如果沒(méi)有找到該對(duì)象,objectForKey:方法會(huì)返回 nil。

objectForKey:方法還有另一種使用方法也可以完成這個(gè)功能:


    NSNumber *storedNumber = dictionary[@"magicNumber"];

可變性

如果要在創(chuàng)建 dictionary 后增刪對(duì)象,你需要使用 NSMutableDictionary 子類:


    [dictionary setObject:@"another string" forKey:@"secondString"];

    [dictionary removeObjectForKey:@"anObject"];

 使用NSNull代替nil

在這一部分中,你不可以在集合類中添加 nil,因?yàn)樵凇bjectiv-C 中,nil 表示“沒(méi)有對(duì)象”。如果你需要在集合中表示“沒(méi)有對(duì)象”,你要使用 NSNull 類:


    NSArray *array = @[ @"string", @42, [NSNull null] ];

NSNull 是一個(gè)單例類,這意味著 null 方法總是返回同樣的實(shí)例。這表示你可以用共享的 NSNull 實(shí)例來(lái)查找數(shù)組中的對(duì)象:


    for (id object in array) {
        if (object == [NSNull null]) {
            NSLog(@"Found a null object");
        }
    }

使用集合來(lái)保存對(duì)象的圖形。

直接寫(xiě) NSArray 和 NSDictionary 類是十分簡(jiǎn)單的:


    NSURL *fileURL = ...

    NSArray *array = @[@"first", @"second", @"third"]; 

    BOOL success = [array writeToURL:fileURL atomically:YES];

    if (!success) 
    {
        // an error occured...
    }

如果集合中的某一個(gè)對(duì)象是 property list 類型( NSArray,NSDictionary,NSString,NSData,NSDate 和 NSNumber ),你可以從磁盤(pán)中重新創(chuàng)建完整的層次:


    NSURL *fileURL = ...
    NSArray *array = [NSArra arrayWithContentsOfURL:fileURL];
    if (!array) {
        // an error occurred...
    }

查看更多關(guān)于property lists,請(qǐng)看Property List Programming Guide

如果你需要保存除了上面提到的標(biāo)準(zhǔn)property list類之外的對(duì)象類,你可以使用存檔對(duì)象,例如 NSKeyedArchiver,來(lái)創(chuàng)建一個(gè)集合式的對(duì)象存檔。

創(chuàng)建存檔的唯一要求是每一個(gè)對(duì)象必須支持 NSCoding 協(xié)議。這意味著每一個(gè)對(duì)象必須知道如何在存檔中編碼(通過(guò)實(shí)現(xiàn) encodeWithCoder: 方法),以及在讀取存檔時(shí)解碼(通過(guò) initWithCoder: 方法)。

NSArray,NSSet,NSDictionary 以及他們的可變子類,都支持 NSCoding,這意味著你可以通過(guò)存檔來(lái)保存復(fù)雜的對(duì)象層次。如果你使用 Interface Builder 來(lái)布局窗口和視圖,那么你的 nib 文件就是視覺(jué)化的層次對(duì)象存檔。在程序執(zhí)行時(shí),對(duì)于使用有關(guān)類的對(duì)象層次,nib 文件是不能存檔的。

查看更多關(guān)于 Archives,請(qǐng)看Archives and Serializations Programming Guide

使用最有效的集合列舉技術(shù)

Objective-C、Cocoa、Cocoa Touch 提供了多種列舉集合內(nèi)容的方法。當(dāng)然,使用 C 語(yǔ)言中傳統(tǒng)的 for 循環(huán)來(lái)列舉內(nèi)容也是可以的:


    int count = [array count];
    for (int index = 0; index < count; index++) {
        id eachObject = [array objectAtIndex:index];
        ...
    }

你最好可以練習(xí)使用這部分中描述的其他技術(shù)來(lái)實(shí)現(xiàn)這個(gè)功能。

快速列舉使列舉一個(gè)集合變得容易

許多集合類都符合 NSFastEnumeration 協(xié)議,包括 NSArray,NSSet 和 NSDictionary。這意味著你可以使用快速列舉,一種 Objective-C 中特有的語(yǔ)言。

快速列舉數(shù)組或集合中內(nèi)容的語(yǔ)法:


    for (<Type> <variable> in <collection>) {
        ...
    }

使用快速列舉描述每一個(gè)數(shù)組中對(duì)象:


    for (id eachObject in array) {
        NSLog(@"Object: %@", eachObject);
    }

其中的 eachObject 變量在每一層循環(huán)里自動(dòng)地被設(shè)為當(dāng)前對(duì)象,所以每一個(gè)對(duì)象都會(huì)列舉出來(lái)。

如果你在字典中進(jìn)行快速列舉,你要像這樣遍歷字典的關(guān)鍵字:


    for (NSString *eachKey in dictionary) {
        id object = dictionary[eachKey];
        NSLog(@"Object: %@ for key: %@", object, eachKey);
    }

快速列舉十分像 C 中的 for 循環(huán),所以你可以用 break 來(lái)終止循環(huán),用 continue 來(lái)進(jìn)入下一層。

如果你要列舉一個(gè)有序的集合,列舉操作會(huì)按循序進(jìn)行。對(duì)于 NSArray 類,列舉操作的第一個(gè)對(duì)象是下標(biāo)為 0 的那個(gè)元素,第二個(gè)對(duì)象是下標(biāo)為 1 的那個(gè),以此類推。如果你需要記錄當(dāng)前下標(biāo)的值,只要簡(jiǎn)單的計(jì)算迭代次數(shù):


    int index = 0;
    for (id eachObject in array) {
        NSLog(@"Object at index %i is: %@", index, eachObject);
        index++;
    }

大多數(shù)集合也支持列舉對(duì)象

你可以使用 NSEnumerator 對(duì)象來(lái)列舉許多 Cocoa and Cocoa Touch 集合。

你也可以使用 NSArray 中的 objectEnumerator 或者 reverseObjectEnumerator 來(lái)實(shí)現(xiàn)快速列舉:


    for (id eachObject in [array reverseObjectEnumerator]) {
        ...
    }

在這個(gè)例子中,循環(huán)會(huì)倒序輸出集合中的對(duì)象,所以最后一個(gè)對(duì)象將會(huì)第一個(gè)被列舉。

你也可以通過(guò)重復(fù)地調(diào)用 nextObject 方法來(lái)迭代內(nèi)容:


    id eachObject;
    while ( (eachObject = [enumerator nextObject]) ) {
        NSLog(@"Current object is: %@", eachObject);
    }

在這個(gè)例子中,while 用來(lái)設(shè)置下一個(gè)循環(huán)中對(duì)象的 eachObject 變量。當(dāng)集合中沒(méi)有剩下的對(duì)象時(shí),nextObject 方法會(huì)返回 nil ,它被當(dāng)成 false 所以循環(huán)終止。

提醒

由于在 C 程序中,等號(hào)(==)非常容易寫(xiě)錯(cuò)成賦值符號(hào)(=),當(dāng)你在分支程序或循環(huán)中設(shè)定變量時(shí),編譯器會(huì)彈出警告:


if (someVariable = YES) 
{
...
}

如果你真的想讓邏輯值等于等號(hào)左邊的變量,你可以將表達(dá)式用括號(hào)括起來(lái):


if ((someVariable = YES)) 
{
...
}

當(dāng)你使用快速列舉時(shí),你不可以改變集合。而且,使用快速列舉法比人工列舉對(duì)象要更快,因?yàn)槟悴挥檬占瘜?duì)象的名字。

許多集合都支持基于塊的列舉

你也可以使用塊來(lái)列舉 NSArray,NSSet 和 NSDictionary。關(guān)于塊的詳細(xì)信息會(huì)在下一章中介紹。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)