用OBJC編程 6 - Value and Collections
OBJC里可以用基本的C原生類型,也定義了一些擴(kuò)展的原生類型。BOOL類型,它的值是YES和NO,YES等于true等于1。NO等于false等于0。Cocoa定義了特殊的原生類型,如NSInteger和CGFloat。像NSInteger和NSUInteger,依賴于平臺(tái),在32位系統(tǒng)下是32位的,在64位下是64位的。通過API傳遞時(shí),最佳實(shí)踐是使用這些特定平臺(tái)的類型。而局部變量如循環(huán)計(jì)數(shù),使用基本C變量更好。C結(jié)構(gòu)體可持有原生類型一些Cocoa API用C結(jié)構(gòu)體持有數(shù)值NSString *mainString = @"This is a long string";
NSRange substringRange = [mainString rangeOfString:@"long"];
NSRange結(jié)構(gòu)體持有l(wèi)ocation和length,本例中,substringRange為{10,4}。類似的在Quartz中,基于CGFloat,NSPoint和NSSize(OSX)或者CGPoint和CGSize(IOS)都是C結(jié)構(gòu)體。用對(duì)象來代替原生類型例如NSString,NSString是不可變的,這意味著你需要一個(gè)不同的字符串時(shí),你需要?jiǎng)?chuàng)建一個(gè)新的對(duì)象。NSMutableString是可變的,是NSString的子類。NSString *firstString = [[NSString alloc] initWithCString:"Hello World" encoding:NSUTF8StringEncoding];
NSString *secondString = [NSString stringWithCString:"Hello World" encoding:NSUTF8StringEncoding];
NSString *thirdString = @"Hello World";
// ------------
NSString *name = @"John";
name = [name stringByAppendingString:@"ny"]; // returns a new string object
//----------------
NSMutableString *name = [NSMutableString stringWithString:@"John"];
[name appendString:@"ny"]; // same object, but now represents "Johnny"
格式化字符串NSString *magicString = [NSString stringWithFormat:@"The magic number is %i", magicNumber];
NSNumber可以表示任何基本C標(biāo)量類型,包括,char, double, float, int, long, short, 以及unsigned類型和BOOL。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'];
可以使用字面常量來創(chuàng)建NSNumber實(shí)例,這些例子等價(jià)于使用NSNumber的工廠方法NSNumber *magicNumber = @42;
NSNumber *unsignedNumber = @42u;
NSNumber *longNumber = @42l;
NSNumber *boolNumber = @YES;
NSNumber *simpleFloat = @3.14f;
NSNumber *betterDouble = @3.1415926535;
NSNumber *someChar = @'T';
可以使用訪問器獲得標(biāo)量值int scalarMagic = [magicNumber intValue];
unsigned int scalarUnsigned = [unsignedNumber unsignedIntValue];
BOOL scalarBool = [boolNumber boolValue];
NSNumber也可以用在NSInteger和NSUInteger上面NSInteger anInteger = 64;
NSUInteger anUnsignedInteger = 100;
NSNumber *firstInteger = [[NSNumber alloc] initWithInteger:anInteger];
NSNumber *sencondInteger = [NSNumber numberWithUnsignedInteger:anUnsignedIneger];
NSInteger integerCheck = [firstInteger integerValue];
NSUInteger unsignedCheck = [sencondInteger unsignedIntegerValue];
NSNumber實(shí)例是不可變的,而且沒有可變版本的子類,如果你需要一個(gè)不同的數(shù)字,就使用另一個(gè)NSNumber實(shí)例。NSNumber實(shí)際是一個(gè)類簇class cluster。使用NSValue,NSValue是NSNumber的父類,可以用來持有更多的類型,包括指針和結(jié)構(gòu)等。它有非常多的工廠方法。NSString *mainString = @"This is a long string";
NSRange substringRange = [mainString rangeOfString:@"long"];
NSValue *rangeValue = [NSValue valueWithRange:substringRange];
// ---------
typedef struct{
int i;
float f;
} MyIntegerFloatStruct;
//======================
stuct MyIntegerFloatStruct aStruct;
aStruct.i = 42;
aStruct.f = 3.14;
NSValue *structValue = [NSValue value:&aStruct withObjCType:@encode(MyIntegerFloatStruct)];
Collections 容器像NSArray,NSSet和NSDictionary這些容器可以管理一組OBJC 對(duì)象實(shí)例,如果你要放一個(gè)標(biāo)量進(jìn)去,需要包裝為NSNumber或者NSValue。這些容器使用強(qiáng)引用持有它們的內(nèi)容,也就意味著這些對(duì)象實(shí)例和容器生命周期一樣。基本的NSArray,NSSet和NSDictionary都是不可變的,它們都有一個(gè)可變版本的子類。NSArray是有序的容器,它的內(nèi)容不必是同一類對(duì)象。它有很多不同的初始化方法和工廠方法,依賴于對(duì)象的數(shù)目:+ (id)arrayWithObject:(id)anObject;
+ (id)arrayWithObjects:(id)firstObject, ,,,;
+ (id)initWithObjects: (id)firstObject, ,,,;
可變參數(shù)版本的如arrayWithObjects依賴nil終止NSArray *someArray = [NSArray arrayWithObjects:someObject, someString, someNumber, nil];
如果某個(gè)列表中的對(duì)象是nil,可能會(huì)發(fā)生意外的截?cái)?/span>
id firstObject = @"SomeString";
id secondObject = nil;
id thirdObject = @"anotherString";
NSArray *someArray = [NSArray arrayWithObjects:firstObject, secondObject, thirdObject, nil];
使用字面常量
NSArray *someArray = @[firstObject, secondObject, thirdObject];
// 列表里不能有nil,nil是一個(gè)非法數(shù)值,會(huì)導(dǎo)致運(yùn)行時(shí)異常
id firstObject = @"someString";
id secondObject = nil;
NSArray *someArray = @[firstObject, secondObject];
// exception: "attempt to insert nil object"
如果你需要在容器中使用nil,你應(yīng)該使用NSNull單件。
查詢Array
NSUInteger numberOfItems = [someArray count];
if([someArray containsObject:someString]){
//,,,
}
///// -------------
if([someArray count] > 0){ // 訪問無效index將導(dǎo)致運(yùn)行時(shí)異常
NSLog(@"First item is: %@", [someArray objectAtIndex:0]);
}
//// ------ 可以使用下標(biāo)語(yǔ)法
if([someArray count] > 0){
NSLog(@"First item is: %@", someArray[0]);
}
Array排序
因?yàn)镹SArray是不可變的,像排序這樣的方法會(huì)產(chǎn)生一個(gè)新的array
NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"];
NSArray *sortedStrings = [unsortedStrings sortedArrayUsingSelector:@Selector(compare:)];
可變版本
NSMutableArray *mutableArray = [NSMutableArray array];
[mutableArray addObject:@"gamma"];
[mutableArray addObject:@"alpha"];
[mutableArray addObject:@"beta"];
[mutableArray replaceObjectAtIndex:0 withObject:@"epsilon"];
[mutableArray sortUsingSelector:@selector(caseInseneitiveCompare:)];
NSSet是一個(gè)無序的容器,同樣是不可變的,也有可變版本。初始化同樣是nil結(jié)尾。Set只為一個(gè)對(duì)象存儲(chǔ)一個(gè)引用,哪怕你多次加入一個(gè)對(duì)象
NSNumber *number = @42;
NSSet *numberSet = [NSSet setWithObjects:number, number, number, nil];
// numberSet里僅僅有一個(gè)對(duì)象
字典是一個(gè)key-value容器,注意需要一個(gè)對(duì)象作為key,這個(gè)key對(duì)象需要依從NSCopying協(xié)議
創(chuàng)建字典,
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
someObject, @"anObject",
@"Hello, World1", @"helloString",
@42, @"magicNumber",
someValue, @"aValue",
nil];
使用字面常量
// 注意和上面是反的
NSDictionary *dictionary = @{
@"anObject", someObject,
@"helloString", @"Hello, World1",
@"magicNumber", @42,
@"aValue", someValue,,
}; // 沒有nil
查詢字典
NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];
// or
NSNumber *storedNumber = dictionary[@"magicNumber"];
用NSNull代替nil,NSNull是一個(gè)單件,可以用等號(hào)來判斷
NSArray *array = @[@"string", @42, [NSNull null] ];
for(id object in array){
if(object == [NSNull null]){
NSLog(@"Found a null object");
}
}
使用容器保存你的對(duì)象。NSArray和NSdictionary可以很容易把內(nèi)容寫入磁盤。如果它包含的內(nèi)容是一個(gè)property list對(duì)象(NSArray,NSDictionary, NSString,NSData,NSDate和NSNumber ), 則可以從磁盤中重新創(chuàng)建。
NSURL *fileURL = //,,,
NSArray *array = @[@"first", @"second", @"third"];
BOOL success = [array writeToURL:fileURL atomically:YES];
if(!success){
// an error occured ,,,
}
//,,,,,,,,,,,,,,
NSURL *fileURL = //,,,
NSArray *array = [NSArray arrayWithContentsOfURL:fileURL];
if(!array){
// an error occured ,,,
}
如果是其它對(duì)象,你需要一個(gè)歸檔對(duì)象,諸如NSKeyedArchiver,去創(chuàng)建一個(gè)容器的歸檔。創(chuàng)建歸檔僅僅需要每一個(gè)對(duì)象實(shí)現(xiàn)NSCoding協(xié)議,這意味著每一個(gè)對(duì)象能夠知道如何在一個(gè)歸檔文件里編碼自己(通過實(shí)現(xiàn)encodeWithCoder方法)以及如何解碼(實(shí)現(xiàn)initWithCoder方法)。
NSArray, NSSet, and NSDictionary 以及它們的可變版本都支持NSCoding,這意味著你可以歸檔一個(gè)復(fù)雜的對(duì)象關(guān)系。Interface Builder就是這么做的。
快速枚舉,包括NSArray,NSSet,NSDictionary這樣支持NSFastEnumeration協(xié)議的容器都支持OBJC語(yǔ)言級(jí)別的快速枚舉
for(id eachObject in array){
NSLog(@"Object: %@", eachObject);
}
for(NSString *eachKey in dictionary){
id object = dictionary[eachKey];
NSLog(@"Object: %@ for key: %@", object, eachKey);
}
對(duì)于有序容器,你需要自己保存索引。不能在快速枚舉里改變?nèi)萜?,即使容器是可變的,否則將得到一個(gè)運(yùn)行時(shí)異常。
int index = 0;
for(id eachObject in array){
NSLog(@"Object at index %i is: %@", index, eachObject);
index++;
}
枚舉對(duì)象,使用NSEnumerator對(duì)象
for(id eachObject in [array reverseObjectEnumerator]){ // 反序枚舉
//,,,
}
id eachObject;
while( (eachObject = [enumerator nextObject]) ) { // 中止時(shí),nextObject返回nil
NSLog(@"Current object is: %@", eachObject);
}
// 在條件語(yǔ)句里使用=號(hào),會(huì)產(chǎn)生一個(gè)編譯時(shí)警告,因此使用了雙括號(hào)來消除這個(gè)警告