Objective-C官方文檔 值和集合


版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

盡管Objective-c是一個面向對象的語言,是C語言的超集,這意味着你可以用任何標准的C標量(非對象)像int,float,和char在Objective-c里面。也有一些額外的標量類型在cocoa和cocoaTouch應用像NSIterger,NSUInterger和CGFloat,他們取決去不同的框架會有不同的定義。

常量類型一般都是用在不需要對象表達值的時候。在字符串的通常用NSString表示,數值通常存儲在本地變量或者屬性的標量中。

你可能定義一個C風格的數組在Objective-c中,但是在Objective-c有集合用來表示例如NSArray或者NSDictionay。這些類用來收集Objective-c的對象,這意味着你在添加到集合之前需要創建一些類的實例像NSValue,NSNumber,NSString來表達值。

在前面的章節我們用到了NSString的類並且也用到了他的初始化和工廠方法以及Objective-c的@“string”字面值,這提供了簡潔的語法來創建NSString的實例,這章闡述怎么創建NSValue和NSNumber對象,用方法調用或者字面值語法。

基本的C的原始類型在Objective-c還是有效的:

標准的C的基本數據類型在Objective-c還是有效的:

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類型的屬性像這樣:

@interface XYZCalculator : NSObject

@property double currentValue;

@end

你也可以用C的運算符在屬性里當用點語法訪問他的值的時候像這樣:

@implementation XYZCalculator

- (void)increment {

    self.currentValue++;

}

- (void)decrement {

    self.currentValue--;

}

- (void)multiplyBy:(double)factor {

    self.currentValue *= factor;

}

@end

點語法純粹是一個訪問器方法調用的包裝器。在這個例子中的操作第一次是用訪問器方法得到值,然后執行操作,然后用設置器來給結果設置值。

Objective-c定義額外的原始類型:

這個BOOL類型定義在Objective-C中用來持有一個布爾值,定義為YES和NO。正如你所料,YES在邏輯上等於true和1,NO在邏輯上是false和0.

一些方法參數也用一些特殊的基本類型像NSIterger或者CGFloat。

例如UITableViewDataSource協議有方法請求顯示的行數。

@protocol NSTableViewDataSource <NSObject>

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;

...

@end

有一些類型像NSIterger或者NSUIterger,根據目標架構會定義不同,當在32位環境下編譯時候(像IOS)那是32位有符號和無符號的整數。當在64位環境下編譯時候(例如OS X操作系統運行時)他們被定義為64位的有符號和無符號的整數。

這是最好的練習用這些平台的特定類型如果你可以跨邊界傳值(不管是內部還是導出的API)例如如果參數或者返回值的方法或者函數調用在你的應用程序代碼和一個框架。

對局部變量,例如在循環里的一個計數器,如果你知道他的值的標准限制你可以用C的基本類型。

C結構體可以保持私有值:

一些API用C結構體保持他們的值。例如詢問一個字符串對象的子串就像這個。

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

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

一個NSRange結構體含有位置和長度的信息。在這個例子中subStringRange將要含有一個{10,4}的范圍,這個”l“是long的開頭是字符串@“mainString”在以0開頭的索引的第10位置,並且@“long”是4個字符的長度。

相似的,如果你需要寫自定義的繪制代碼,你需要和Quartz交互,會需要CGFloat的結構體類型還有NSPoint和NSSize。再說一次CGFloat的定義不同取決於目標結構。

更多信息在 Quartz 2D繪制引擎,參考Quartz 2D Programming Guide.

對象可以表達原始值:

如果你需要表示一個標量值給一個對象。例如當你使用集合類在下一節描述。你可以使用cocoa或者cocoatouch的基本值類。

NSString的類的實例表示字符串

就像你看到的在前一章節,NSString是用來表達字符串的就像@”hello world“ 。有很多方法來創建字符串對象,包括標准的初始化和工廠方法或者字面值語法:

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

                                                    encoding:NSUTF8StringEncoding];

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

                                                encoding:NSUTF8StringEncoding];

    NSString *thirdString = @"Hello World!";

他們的每一個例子有效的完成同樣的事情-創建一個字符串對象來表示他們提供的字符串。

這個基本的NSString類是不可變的,意味着他的內容被創建時候設置不能稍后被改變。如果你需要表示不同的字符串。你需要創建一個新的字符串對象,像這樣:

NSString *name = @"John";

    name = [name stringByAppendingString:@"ny"];//返回一個新的字符串對象

這個NSMutableString 類是可變的繼承與NSString,允許你在運行時改變字符串的內容用appendString:appendFormat:的方法像這樣:

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

    [name appendString:@"ny"];  // 同樣的對象但是現在是“johnny”

格式字符串是從其他的對象或者值構建而來的

如果你需要構建一個字符串包含變量值,你需要用format string,這個允許你使用格式說明符指示值是如何插入的:

int magicNumber = ...

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

有效的格式化說明符被 “String Format Specifiers”描述,更多一般的信息關於字符串的看String Programming Guide.文檔

NSNumber類的實例表示數值:

這個NSNumber類是用來表示任何基本的C數據類型:包括char double float int long short 並且他們都有有無符號的變形以及Objective-c的布爾類型。

和NSString相比,你創建NSNumber實例有很多選擇,包括初始化或者工廠方法:

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'];

你也可以用字面值語法創建:

NSNumber *magicNumber = @42;

    NSNumber *unsignedNumber = @42u;

    NSNumber *longNumber = @42l;

    NSNumber *boolNumber = @YES;

    NSNumber *simpleFloat = @3.14f;

    NSNumber *betterDouble = @3.1415926535;

    NSNumber *someChar = @'T';

這些例子是用NSNumber類的工廠方法創建的。

一旦你創建了一個NSNumber的實例你可能請求一個值用訪問器方法:

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原始類型。如果你創建一個對象表示NSIterger或者NSUIterger類型你需要正確方法:

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實例都是不可變的,沒有可變的子類,你需要創建不同的NSNumber,簡單的用另一個NSNumber實例。

注意:NSNumber實際上是一個類簇。這意味着在運行時當你創建一個實例,你將要得到一個合適的具體子類所提供的價值,只是把你創建的對象作為NSNumber的一個實例。

用NSValue表示其他的值:

這個NSNumber是NSValue的一個子類,他提供了包裝一個單值或者數據項的一個對象。除了基本的C數據類型,NSValue可以被用來表示指針和結構體。

這個NSValue類提供多樣化的工廠方法來創建值用給定的標准結構,讓他更容易的創建一個實例來表示值,例如,一個NSRange,之前提到的:

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

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

    NSValue *rangeValue = [NSValue valueWithRange:substringRange];

他也可能創建NSValue對象表示自定義結構體。如果你需要一個特定的C結構(而不是一個Objective-c的對象)來存儲信息。像這樣:

typedef struct {

    int i;

    float f;

} MyIntegerFloatStruct;

你可以創建一個NSValue的實例提供一個指針指向結構體以及用Objective-c的編碼類型。這個@encode()編譯指令被用來創建正確的Objective-c的類型,像這樣:

struct MyIntegerFloatStruct aStruct;

    aStruct.i = 42;

    aStruct.f = 3.14;

    NSValue *structValue = [NSValue value:&aStruct

                            withObjCType:@encode(MyIntegerFloatStruct)];

&符號是用來提供aStruct地址的值參數。

大多數集合都是對象:

盡管可以使用標准的C數組來存儲標量的值,甚至對象指針,很多集合在Objective-c代碼都是cocoa或者cocoaTouch集合類的實例,像NSArray,NSSet和NSDictionay。

這些類用來管理對象組,這意味着你添加到集合的條目都是Objective-c類的對象。如果你需要添加基本數據類型那么你必須先創建NSNumber或者NSValue的實例來包裝他們。這個集合類用強引用來跟蹤他們的內容,而不是以某種方式為他們的集合元素維護一個單獨的副本。這說明在集合的元素你添加的將要保持存活只要集合存活着之前在管理對象圖的關系和責任介紹到了。

集合除了跟蹤他們的內容,每個集合類都很容易來執行某些方法,例如枚舉,訪問特定的元素,或者有沒有特定的元素在集合里面。

NSArray,NSSet 和NSDictionay類都是不可變的,這意味着內容在創建的時候被設置。但是他們都有可變的子類版本來提供添加刪除元素。更多信息關於不同集合類的參考Collections Programming Topics.

數組是有序的集合:

一個數組用來表示有序的集合對象。唯一的要求是數組元素里的每個元素必須是Objective-c的對象,對與每個對象是不是同一個類的實例沒做要求。

為了維持數組的順序,每個元素存儲在從零開始的索引如下圖:

Image

創建數組:

正如之前描述的值類型,你可以創建數組通過初始化方法,工廠方法,還有字面語法。

有很多創建數組的初始化方法和工廠方法,都需要一些對象參數:

+ (id)arrayWithObject:(id)anObject;

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

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

arrayWithObject:和initWithObjects:方法都以nil結尾,一些可變的參數個數,這意味着你必須以nil作為最后一個值像這樣:

NSArray *someArray =

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

這個例子創建一個數組,第一個對象,someObject,他在這個數組索引為0,someValue他這個數組的索引為3。

如果數組里的一個元素提供了nil那么這個列表就會被無意中截斷就像這樣:

id firstObject = @"someString";

    id secondObject = nil;

    id thirdObject = @"anotherString";

    NSArray *someArray =

  [NSArray arrayWithObjects:firstObject, secondObject, thirdObject, nil];

在這個例子當中,someArray 緊緊只有firstObject因為第二個元素是nil所以第二個元素打斷數組元素的列表作為最后一個元素。

字面語法:

我們也可以這樣創建數組:

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

當用字面語法創建數組的時候不應該以nil結尾,實際上nil是無效值,如果執行一下代碼你會得到一個異常:

id firstObject = @"someString";

    id secondObject = nil;

    NSArray *someArray = @[firstObject, secondObject];

    // exception: "attempt to insert nil object"

在集合類里面你不需要表達一個nil值,你應該用NSNull單例類具體描述參考“Represent nil with NSNull.”

查詢數組對象:

一旦你創建一個數組,你可能查詢里面的元素個數,或者查詢集合是不是包含這個元素:

NSUInteger numberOfItems = [someArray count];

    if ([someArray containsObject:someString]) {

        ...

    }

你也可以查詢數組元素根據索引,如果你在運行時請求一個無效的索引你會得到一個數組越界的異常,因此你應該一直檢查數組元素的數值:

if ([someArray count] > 0) {

        NSLog(@"First item is: %@", [someArray objectAtIndex:0]);

    }

這個例子檢查數組的元素個數是不是大於0,如果大於0,那么你就打印索引為0的第一個元素。

數組下標:

除了objectAtIndex我們還提供了下標語法,就像C語言數組的語法。之前的那段代碼也可以這樣寫:

if ([someArray count] > 0) {

        NSLog(@"First item is: %@", someArray[0]);

    }

排序數組對象

這個NSArray也提供了很多的方法來給集合對象排序。因為數組是不可變的,每個方法都返回一個排序后的新數組:例如你可以用compare:方法給字符串數組排序像這樣:

NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"];

    NSArray *sortedStrings =

                [unsortedStrings sortedArrayUsingSelector:@selector(compare:)];

集合可變性:

雖然NSArray本身是不可變的,這個集合元素沒有關系。如果你添加一個可變的字符串到不可變的數組,例如像這樣:

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

NSArray *immutableArray = @[mutableString];

沒有什么能阻止可變的字符串:

if ([immutableArray count] > 0) {

        id string = immutableArray[0];

        if ([string isKindOfClass:[NSMutableString class]]) {

            [string appendString:@" World!"];

        }

    }

如果你需要在數組創建后添加或者刪除數組元素,你需要用NSMutableArray,有很多方法可以添加刪除或者替換一個或者多個對象:

NSMutableArray *mutableArray = [NSMutableArray array];

    [mutableArray addObject:@"gamma"];

    [mutableArray addObject:@"alpha"];

    [mutableArray addObject:@"beta"];

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

這個例子創建一個數組最終結果是: @"epsilon", @"alpha", @"beta",你可以排序一個可變數組而不要再創建一個數組:

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

這個例子包括的數組元素將按照升序排序,不分大小寫@"alpha", @"beta", @"epsilon"

Sets是無序的集合:

一個NSSet就像數組,但是他包含的是無序的不同對象。如下:

Image(1)

因為Sets是無序的,他們提高了性能比數組當測試會員的時候。

這個NSSet是不可變的,創建的時候必須指定元素,用初始化方法或者工廠方法就像:

NSSet *simpleSet =

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

就像數組一樣,這個initWithObjects:和setWithObjects:方法都有以nil結尾,可變的參數個數,還有可變的子類NSMutableSet。

對於添加相同的對象到NSset:

NSNumber *number = @42;

    NSSet *numberSet =

      [NSSet setWithObjects:number, number, number, number, nil];

    // numberSet only contains one object

更多信息參考“Sets: Unordered Collections of Objects”.

字典集合鍵值對:

一個NSDictionay存儲對象給定鍵值,而不是無序或者有序的集合,以便於后來進行檢索。如下圖:

Image(2)

我們可能用其他對象作為鍵,但是重要的說明是你的字典的鍵會被復制,因此你必須支持NSCopying協議。

如果你希望用鍵值編碼然后在 Key-Value Coding Programming Guide描述,你的字典鍵必須是字符串

創建字典:

你可以創建字典用初始化方法或者工廠方法像這樣:

NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:

                  someObject, @"anObject",

            @"Hello, World!", @"helloString",

                          @42, @"magicNumber",

                    someValue, @"aValue",

                            nil];

注意:dictionaryWithObjectsAndKeys:和initWithObjectsAndKeys:方法每個對象都有鍵相對最后以nil結尾。

字面語法:

Objective-c也提供字面語法創建字典:

NSDictionary *dictionary = @{

                  @"anObject" : someObject,

              @"helloString" : @"Hello, World!",

              @"magicNumber" : @42,

                    @"aValue" : someValue

    };

注意的是字典的字面常量也不能以nil結束。

查詢字典:

一旦你創建字典,你可以通過鍵值訪問他的對象值像這樣:

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

如果這個對象沒有找到這個objectForKey:就會返回nil。

我們同樣可以用下標語法訪問字典代替objectForKey:像這樣:

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

字典的可變版本:

當你創建字典的時候你移除或者添加字典的元素你需要NSMutableDictionary像這樣:

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

    [dictionary removeObjectForKey:@"anObject"];

我們用

NSNull代替空

你不可能添加nil到集合類里面,因為nil在集合里面意味着沒有對象,如果你需要表示沒有對象在集合里面,你可以用NSNUll。

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

NSNull是一個單例類,意味着null方法將要返回同樣的實例,這意味着你需要在數組里面檢查元素是不是等譯NSNull的實例。

for (id object in array) {

        if (object == [NSNull null]) {

            NSLog(@"Found a null object");

        }

    }

用集合來保持你的對象圖:

這個NSArray和NSDictionay類使把你的內容寫進硬盤變的方面就像這樣:

NSURL *fileURL = ...

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

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

    if (!success) {

        // an error occured...

    }

集合里的每個元素都是屬性列表中的類型(NSArray, NSDictionary, NSString, NSData, NSDate and NSNumber並且還可以從硬盤再讀取回來:

NSURL *fileURL = ...

    NSArray *array = [NSArray arrayWithContentsOfURL:fileURL];

    if (!array) {

        // an error occurred...

    }

更多信息參考屬性列表:Property List Programming Guide.

如果你堅持用其他類型對象而不僅僅是標准的屬性列表里的類型,你可以用archiver 對象,例如NSKeyedArchiver為集合對象創建歸檔。

創建歸檔唯一的要求的是每個對象支持 NSCoding 協議。這意味着每個對象都必須知道如果編碼本身到存檔(通過實現encodeWithCoder:方法)和解碼本身當從現有的存檔閱讀的時候(initWithCoder:方法)。

這個NSArray和NSSet和NSDictionay類都有可變的子類,也都支持NSCoding協議,意味着你可以繼續用負責的對象結構用歸檔。如果你用IB來設計你的界面。例如這個nib文件就是一個存儲對象層次結構的歸檔。在運行時候,nib文件用相關的類解檔出一個復雜的對象結構。

更多歸檔信息參考Archives and Serializations Programming Guide.

集合的快速枚舉:

Objective-c里面提供了很多方法列舉集合的元素,例如for循環像這樣:

int count = [array count];

    for (int index = 0; index < count; index++) {

        id eachObject = [array objectAtIndex:index];

        ...

    }

快速枚舉使列舉集合元素更方便:

一些集合類符合NSFastEnumeration協議包括NSArray和NSDictionay。這意味着你可以使用快速枚舉,一個Objective-C語言特性。

快速枚舉的語法怎么快速枚舉數組里的元素像這樣:

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

        ...

    }

這個例子你可以使用快速枚舉快速打印出數據的元素像這樣:

for (id eachObject in array) {

        NSLog(@"Object: %@", eachObject);

    }

這個eachObject變量是被自動設置當前循環經過的對象因此會打印出每個對象。

如果你在字典里用快速枚舉,你可以迭代字典的keys像這樣:

for (NSString *eachKey in dictionary) {

        id object = dictionary[eachKey];

        NSLog(@"Object: %@ for key: %@", object, eachKey);

    }

快速枚舉像C語言的for循環,因此你可以用break打斷這個迭代或者繼續前進到下一個元素。

如果你快速枚舉一個有序的集合,快速枚舉也是有序的。對於一個NSArray,這意味着第一個通過的對象索引是0,第二個對象的索引是1等。如果你跟蹤他們當前的索引,簡單計算迭代的發生:

int index = 0;

    for (id eachObject in array) {

        NSLog(@"Object at index %i is: %@", index, eachObject);

        index++;

    }

你不能改變一個集合在快速枚舉時候,盡管集合是可變的,如果你在循環中試圖添加刪除幾個對象,你將要生成一個異常在運行時候。

大多數集合支持枚舉對象:

我們可以用NSEnumerator 對象來枚舉出很多集合。例如objectEnumerator 或者reverseObjectEnumerator得到一個NSArray。我們可以用這些對象進行快速枚舉像這樣:

for (id eachObject in [array reverseObjectEnumerator]) {

        ...

    }

在這個例子中,這個循環迭代出的集合對象變成相反的順序,因此這個最后一個對象講是第一個,等等。

我們可以用枚舉器的nextObject方法來遍歷集合元素像這樣:

id eachObject;

    while ( (eachObject = [enumerator nextObject]) ) {

        NSLog(@"Current object is: %@", eachObject);

    }

在這個例子中一個while循環通過循環用於設置eachObject的下一個對象變量當沒有更多的對象在的時候,nextObject方法返回nil對象,用來評估邏輯值為false因此循環結束。

注意:因為這是一個常見的編碼錯誤使用C語言賦值運算符(=)當你想用(==)編譯器會提醒你如果設置一個變量在一個條件分支或者循環像這樣:

if (someVariable = YES) {

    ...

}

如果你真想重新分配變量(左邊是統一分配的邏輯值)你可以表明這將分配在括號里。

if ( (someVariable = YES) ) {

    ...

}

作為快速枚舉,你不能在枚舉的時候改變集合。並且如你收集的名字。快速枚舉比手動使用計數器枚舉要快。

許多集合支持塊的枚舉:
NSArray,Dictionary,NSSet用塊語法做枚舉。對於塊的詳情在下章介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM