版權聲明:原創做品,謝絕轉載!不然將追究法律責任。html
儘管Objective-c是一個面向對象的語言,是C語言的超集,這意味着你能夠用任何標準的C標量(非對象)像int,float,和char在Objective-c裏面。也有一些額外的標量類型在cocoa和cocoaTouch應用像NSIterger,NSUInterger和CGFloat,他們取決去不一樣的框架會有不一樣的定義。ios
常量類型通常都是用在不須要對象表達值的時候。在字符串的一般用NSString表示,數值一般存儲在本地變量或者屬性的標量中。web
你可能定義一個C風格的數組在Objective-c中,可是在Objective-c有集合用來表示例如NSArray或者NSDictionay。這些類用來收集Objective-c的對象,這意味着你在添加到集合以前須要建立一些類的實例像NSValue,NSNumber,NSString來表達值。數組
在前面的章節咱們用到了NSString的類而且也用到了他的初始化和工廠方法以及Objective-c的@「string」字面值,這提供了簡潔的語法來建立NSString的實例,這章闡述怎麼建立NSValue和NSNumber對象,用方法調用或者字面值語法。架構
基本的C的原始類型在Objective-c仍是有效的:app
標準的C的基本數據類型在Objective-c仍是有效的:框架
int someInteger = 42;ide
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的對象,對與每一個對象是否是同一個類的實例沒作要求。
爲了維持數組的順序,每一個元素存儲在從零開始的索引以下圖:
建立數組:
正如以前描述的值類型,你能夠建立數組經過初始化方法,工廠方法,還有字面語法。
有不少建立數組的初始化方法和工廠方法,都須要一些對象參數:
+ (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就像數組,可是他包含的是無序的不一樣對象。以下:
由於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存儲對象給定鍵值,而不是無序或者有序的集合,以便於後來進行檢索。以下圖:
咱們可能用其餘對象做爲鍵,可是重要的說明是你的字典的鍵會被複制,所以你必須支持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用塊語法作枚舉。對於塊的詳情在下章介紹。