Values and Collections
尽管OC是面向对象的编程语言,但她还是一个C语言的超集,这意味着你可以在OC代码中使用任何标准C标量类型,比如int,float,char。同样也有额外的标量类型的变量在Cocoa和Cocoa Touch应用中,比如NSInteger,NSUInteger,CGFloat,这些类型在不同的目标结构中有不同的定义。
标量类型是在你不需要使用一个对象表示一个值的好处(或者相关的开销)的时候使用的。然而字符串通常表示为NSString类的实例,数字值经常存储在标量局部变量或者属性中。
你可能会在OC中声明一个C形式的数组,但是你会发现在Cocoa和Cocoa Touch应用中集合通常被用来表示使用类的实例,像NSArray或者NSDictionary。这些类可以被用来收集OC对象,这意味着在你添加对象到一个集合之前,你需要创建一些NSValue,NSNumber或者NSString的类的实例对象,以用来表示他们的值。
在这本指南的前面章节,频繁的使用了NSString类和她的初始化和工厂方法,也用到了OC中@“string”关键字来简洁 的创建一个NSString实例。本章将会告诉你如何创建NSValue和NSNumber对象,通过方法调用或者通过OC字面值的语法。
Basic C Primitive Types Are Available in Objective-C
Each of the standard C scalar variable types is available in Objective-C:
每个标准C中的标量变量类型在OC中都是有效的:
int someInteger = 42; |
float someFloatingPointNumber = 3.1415; |
double someDoublePrecisionFloatingPointNumber = 6.02214199e23; |
as well as the standard C operators:
标准C中的运算符也一样有效:
int someInteger = 42; |
someInteger++; // someInteger == 43 |
|
int anotherInteger = 64; |
anotherInteger--; // anotherInteger == 63 |
|
anotherInteger *= 2; // anotherInteger == 126 |
If you use a scalar type for an Objective-C property, like this:
如果你为一个OC的属性使用标准C的标量类型,像这样:
@interface XYZCalculator : NSObject |
@property double currentValue; |
@end |
it’s also possible to use C operators on the property when accessing the value via dot syntax, like this:
也可能当你通过点语法访问值的时候使用标准C的运算符,像这样:
@implementation XYZCalculator |
- (void)increment { |
self.currentValue++; |
} |
- (void)decrement { |
self.currentValue--; |
} |
- (void)multiplyBy:(double)factor { |
self.currentValue *= factor; |
} |
@end |
Dot syntax is purely a syntactic wrapper around accessor method calls, so each of the operations in this example is equivalent to first using the get accessor method to get the value, then performing the operation, then using the set accessor method to set the value to the result.
点语法就是访问器方法包装之后的样子,因此例子中的每个操作都等价于先使用get方法得到值,执行操作后使用set方法设置值。
Objective-C Defines Additional Primitive Types
在OC中的BOOL标量类型被定义为存储一个Boolean值,这个值要么是YES,要么是NO。如你所料,YES逻辑上等价于true和1,然而NO等价于false和0;
在Cocoa和Cocoa Touch上的许多方法的参数都使用了特定的标量数字类型,比如NSInteger或者CGFloat。
例如,NSTableDataSource和UITableViewDataSource协议(前面章节描述的)都包含响应显示行数的方法:
@protocol NSTableViewDataSource <NSObject> |
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView; |
... |
@end |
像NSInteger和NSUInteger类型被定义成不同的类型取决于目标的体系结构。当构建一个32位环境(比如iOS)时,她们分别是32位的有符号和无符号类型;当构建64环境(如现代的OS X运行时)时,她们分别是64位的有符号和无符号类型。
如果你可能传递的值跨越了API边界(内部和导出APIs),那么使用这些跨平台的特定类型是个很好的惯例,比如参数或者返回值或者函数调用是在你的程序代码和框架之间。
对于局部变量来说,例如循环中的计数器,如果你知道这个值是在标准的范围之内的,那么你当然可以使用标准的C类型来定义她。
C Structures Can Hold Primitive Values
Some Cocoa and Cocoa Touch API use C structures to hold their values. As an example, it’s possible to ask a string object for the range of a substring, like this:
一些Cocoa和Cocoa Touch API使用C中结构体来保存她们的值,例如,你可能访问一个字符串当中的一段子字符串对象,像这样:
NSString *mainString = @"This is a long string"; |
NSRange substringRange = [mainString rangeOfString:@"long"]; |
一个NSRange结构体保存了一个位置和一个长度。本例中,substringrange会保存一个范围{10,4}--long字符串的开始字母l是在mainstring字符串中下标为10,从0开始,长度是4.
类似的,如果你需要编写你自己的绘图代码,你可能需要与Quartz交互,那么你需要的结构就是基于CGFloat数据类型,就像NSPoint和NSSize在OS X上,CGPoint和CGSize在iOS上。CGFloat的定义取决于具体的目标结构。
For more information on the Quartz 2D drawing engine, see Quartz 2D Programming Guide.
Objects Can Represent Primitive Values
If you need to represent a scalar value as an object, such as when working with the collection classes described in the next section, you can use one of the basic value classes provided by Cocoa and Cocoa Touch.
如果你需要表示一个标量值作为一个对象,比如当你需要使用到下一章节中描述的集合类的时候,你可以使用Cocoa和Cocoa Touch提供的基本数值类。
Strings Are Represented by Instances of the NSString Class
As you’ve seen in the previous chapters, NSString
is used to represent a string of characters, like Hello World
. There are various ways to create NSString
objects, including standard allocation and initialization, class factory methods or literal syntax:
正如前面章节提到过的那样,NSString被用来表示字符串,比如Hello World。有许多的方法可以创建NSString对象,包括标准的分配和初始化器,类工厂方法或字面语法:
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"]; // 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 Are Used to Build Strings from Other Objects or Values
如果你需要创建一个包含变量的字符串,你需要使用格式化字符串。这样就可以使用格式说明符来指定插入的值:
int magicNumber = ... |
NSString *magicString = [NSString stringWithFormat:@"The magic number is %i", magicNumber]; |
The available format specifiers are described in String Format Specifiers. For more information about strings in general, see the String Programming Guide.
Numbers Are Represented by Instances of the NSNumber Class
The NSNumber
class is used to represent any of the basic C scalar types, including char
, double
, float
, int
, long
, short
, and the unsigned
variants of each, as well as the Objective-C Boolean type, BOOL
.
NSNumber类被用来表示标准C的标量类型,包括char,double,float,int,long,short,还有unsigned类型的值,也可以表示OC的Boolean类型,BOOL。
就像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']; |
It’s also possible to create NSNumber
instances using Objective-C literal syntax:
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类同样提供了处理额外的OC原始数据的方法。例如,如果你需要创建一个对象是用NSInteger和NSUInteger类型表示的,确保你使用了正确的方法:
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]; |
All NSNumber
instances are immutable, and there is no mutable subclass; if you need a different number, simply use another NSNumber
instance.
Represent Other Values Using Instances of the NSValue Class
NSNumber类就是NSValue类的子类,这个类提供了一个包装单值或者数据项的对象。除了标准C的标量类型,NSValue也可以用来表示指针和结构体。
NSValue类提供了许多工厂方法用来创建一个给定了标准结构的值,例如,创建一个NSRange的实例,就会变的很方便,就像之前的例子:
NSString *mainString = @"This is a long string"; |
NSRange substringRange = [mainString rangeOfString:@"long"]; |
NSValue *rangeValue = [NSValue valueWithRange:substringRange]; |
It’s also possible to create NSValue
objects to represent custom structures. If you have a particular need to use a C structure (rather than an Objective-C object) to store information, like this:
你也可以创建NSValue对象表示自定义的结构。如果你有一个特殊的需要使用C结构体(而不是OC对象)保存的信息,像这样:
typedef struct { |
int i; |
float f; |
} MyIntegerFloatStruct; |
you can create an NSValue
instance by providing a pointer to the structure as well as an encoded Objective-C type. The @encode()
compiler directive is used to create the correct Objective-C type, like this:
您可以创建一个NSValue实例通过提供一个指向结构的指针和一个objective - c编码类型。@encode()编译器指令用于创建正确的objective - c类型,如下:
struct MyIntegerFloatStruct aStruct; |
aStruct.i = 42; |
aStruct.f = 3.14; |
|
NSValue *structValue = [NSValue value:&aStruct |
withObjCType:@encode(MyIntegerFloatStruct)]; |
The standard C reference operator (&
) is used to provide the address of aStruct
for the value
parameter.
Most Collections Are Objects
尽管你可以使用一个C数组来保存标量值的集合,或者对象的指针,但是大多数的OC代码中的集合都是一个Cocoa和Cocoa Touch类的实例对象,比如NSArray,NSSet和NSDictionary。
这些类被用来管理对象组,这就意味着任何你想要添加到集合中的条目都必须是一个OC类的实例。如果你需要添加一个标量值,首先你必须创建一个合适的NSNumber或者NSValue实例用来表示她。
与其维护每个单独收集对象的副本,不如使用强引用来追踪她们的内容。这意味着任何集合中的对象生命周期都和这个集合实例本身一样。详见Manage the Object Graph through Ownership and Responsibility。
除了追踪他们的内容,每个Cocoa和Cocoa Touch集合类使得执行特定的任务变得很容易,比如枚举器,访问特定条目,或者找出集合中特定的对象。
基础的NSArray,NSSet和NSDictionary类是不可变的,这就意味着他们的内容在创建之初就被设置好了。每个类同样都有一个可变的子类可以按照你的意愿更改对象。
For more information on the different collection classes available in Cocoa and Cocoa Touch, see Collections Programming Topics.
Arrays Are Ordered Collections
一个NSArray被用来表示一个对象的有序集合。唯一的要求就是需要每个条目都是一个OC对象--并不要求每个对象都是同一个类的实例。
To maintain order in the array, each element is stored at a zero-based index, as shown in Figure 6-1.
Figure 6-1 An Array of Objective-C Objects
Creating Arrays
As with the value classes described earlier in this chapter, you can create an array through allocation and initialization, class factory methods, or literal syntax.
There are a variety of different initialization and factory methods available, depending on the number of objects:
和前面章节 描述的数值类一样,你可以使用分配和初始化器,类工厂方法或者字面语法来创建一个数组。有许多的初始化器和工厂方法可以使用,这取决于对象的数量:
+ (id)arrayWithObject:(id)anObject; |
+ (id)arrayWithObjects:(id)firstObject, ...; |
- (id)initWithObjects:(id)firstObject, ...; |
The arrayWithObjects:
and initWithObjects:
methods both take a nil-terminated, variable number of arguments, which means that you must include nil
as the last value, like this:
arrayWithObjects:和initWithObjects:方法都是以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]; |
In this case, someArray
will contain only firstObject
, because the nil
secondObject
would be interpreted as the end of the list of items.
本例中,someArray只会包含firstObjec,因为nil类型的secondObject被解释执行为数组列表的末尾。
Literal Syntax
It’s also possible to create an array using an Objective-C literal, like this:
也可以用字面语法创建数组:
NSArray *someArray = @[firstObject, secondObject, thirdObject]; |
You should not terminate the list of objects with nil
when using this literal syntax, and in fact nil
is an invalid value. You’ll get an exception at runtime if you try to execute the following code, for example:
当使用字面语法创建数组的时候,不要使用nil终止对象列表,实际上nil是一个无效的值。如果你尝试执行下面的代码,你会在运行时得到一个异常:
id firstObject = @"someString"; |
id secondObject = nil; |
NSArray *someArray = @[firstObject, secondObject]; |
// exception: "attempt to insert nil object" |
If you do need to represent a nil
value in one of the collection classes, you should use the NSNull
singleton class, as described in Represent nil with NSNull.
如果你需要表示nil类型的值,你可以使用NSNull,详见上
Querying Array Objects
Once you’ve created an array, you can query it for information like the number of objects, or whether it contains a given item:
一旦你创建了一个数组,那么你就可以查询如对象的个数或者是否包含给定的内容等信息:
NSUInteger numberOfItems = [someArray count]; |
|
if ([someArray containsObject:someString]) { |
... |
} |
You can also query the array for an item at a given index. You’ll get an out-of-bounds exception at runtime if you attempt to request an invalid index, so you should always check the number of items first:
你也可以通过下标查询数组中的某个条目。如果你试图请求一个无效的下标在运行时你会得到一个超出边界的异常,因此,每次你应该先检查一下数组中条目的个数:
if ([someArray count] > 0) { |
NSLog(@"First item is: %@", [someArray objectAtIndex:0]); |
} |
This example checks whether the number of items is greater than zero. If so, it logs a description of the first item, which has an index of zero.
Subscripting
There’s also a subscript syntax alternative to using objectAtIndex:
, which is just like accessing a value in a standard C array. The previous example could be re-written like this:
下标法可以代替前面使用的objectAtIndex:方法:
if ([someArray count] > 0) { |
NSLog(@"First item is: %@", someArray[0]); |
} |
Sorting Array Objects
The NSArray
class also offers a variety of methods to sort its collected objects. Because NSArray
is immutable, each of these methods returns a new array containing the items in the sorted order.
NSArray类也提供了各种各样的方法对对象进行排序。因为NSArray是不可变的,这些方法返回一个新的数组,其中包含项目的顺序。
As an example, you can sort an array of strings by the result of calling compare:
on each string, like this:
NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"]; |
NSArray *sortedStrings = |
[unsortedStrings sortedArrayUsingSelector:@selector(compare:)]; |
Mutability
Although the NSArray
class itself is immutable, this has no bearing on any collected objects. If you add a mutable string to an immutable array, for example, like this:
尽管NSArray类是不可变的,但是这不影响类中的各个对象。如果你添加一个可变字符串在一个不可变的数组,像这样:
NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"]; |
NSArray *immutableArray = @[mutableString]; |
there’s nothing to stop you from mutating the string:
随意改变你的字符串:
if ([immutableArray count] > 0) { |
id string = immutableArray[0]; |
if ([string isKindOfClass:[NSMutableString class]]) { |
[string appendString:@" World!"]; |
} |
} |
If you need to be able to add or remove objects from an array after initial creation, you’ll need to use NSMutableArray
, which adds a variety of methods to add , remove or replace one or more objects:
如果你需要在创建之后添加或者删除对象,你需要使用NSMutableString,这个类有许多的方法,可以添加,删除或者替换对象。
NSMutableArray *mutableArray = [NSMutableArray array]; |
[mutableArray addObject:@"gamma"]; |
[mutableArray addObject:@"alpha"]; |
[mutableArray addObject:@"beta"]; |
|
[mutableArray replaceObjectAtIndex:0 withObject:@"epsilon"]; |
This example creates an array that ends up with the objects @"epsilon"
, @"alpha"
, @"beta"
.
It’s also possible to sort a mutable array in place, without creating a secondary array:
也可以直接給数组排序,不用创建第一个数组:
[mutableArray sortUsingSelector:@selector(caseInsensitiveCompare:)]; |
In this case the contained items will be sorted into the ascending, case insensitive order of @"alpha"
, @"beta"
, @"epsilon"
.
本例中的数组条目会按升序排列。
Sets Are Unordered Collections
An NSSet
is similar to an array, but maintains an unordered group of distinct objects, as shown in Figure 6-2.
一个NSSet类似于一个数组,但是她包含的是无序的并且是不同类型的对象,如图:
Figure 6-2 A Set of Objects
Because sets don’t maintain order, they offer a performance improvement over arrays when it comes to testing for membership.
由于集合是无序的,所以他们提供了一个对于数组来说是更好的性能当检测成员资格的时候。
基础的NSSet类也是不可变的,所以她的内容在创建的时候需要被指定:
The basic NSSet
class is again immutable, so its contents must be specified at creation, using either allocation and initialization or a class factory method, like this:
NSSet *simpleSet = |
[NSSet setWithObjects:@"Hello, World!", @42, aValue, anObject, nil]; |
As with NSArray
, the initWithObjects:
and setWithObjects:
methods both take a nil-terminated, variable number of arguments. The mutable NSSet
subclass is NSMutableSet
.
Sets only store one reference to an individual object, even if you try and add an object more than once:
集合只是存储个别对象的一个引用,即使你尝试着不止一次的添加同一个对象:
NSNumber *number = @42; |
NSSet *numberSet = |
[NSSet setWithObjects:number, number, number, number, nil]; |
// numberSet only contains one object |
For more information on sets, see Sets: Unordered Collections of Objects.
Dictionaries Collect Key-Value Pairs
Rather than simply maintaining an ordered or unordered collection of objects, an NSDictionary
stores objects against given keys, which can then be used for retrieval.
It’s best practice to use string objects as dictionary keys, as shown in Figure 6-3.
和简单的包含一个有序的或者是无序的集合相比,NSDictionary储存的对象对应的会给一个键,这个值可以被用于检索。使用字符串对象作为字典的键是个很好的惯例,如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/b1aafbd2b8d0808af55c4a6266887fb1.png)
Note: 可能你需要使用别的对象作为键,但是你需要注意的是每个键都是使用字典的副本,因此必须支持NSCopying。如果你希望使用键值代码,你必须为字典对象使用字符串键,详见Key-Value Coding Programming Guide。
Creating Dictionaries
You can create dictionaries using either allocation and initialization, or class factory methods, like this:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: |
someObject, @"anObject", |
@"Hello, World!", @"helloString", |
@42, @"magicNumber", |
someValue, @"aValue", |
nil]; |
Note that for the dictionaryWithObjectsAndKeys:
and initWithObjectsAndKeys:
methods, each object is specified before its key, and again, the list of objects and keys must be nil-terminated.每个对象被指定在她的键之前。
Literal Syntax
Objective-C also offers a literal syntax for dictionary creation, like this:
OC同样提供字面语法来创建字典:
NSDictionary *dictionary = @{ |
@"anObject" : someObject, |
@"helloString" : @"Hello, World!", |
@"magicNumber" : @42, |
@"aValue" : someValue |
}; |
Note that for dictionary literals, the key is specified before its object and is not nil-terminated.
注意,这样的创建,键是在对象之前指定的,没有nil结尾。
Querying Dictionaries
一旦你创建了一个字典,你可以访问她,通过给定的对应的键:
NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"]; |
如果对象没找到,方法返回nil。
同样有下标语法替换objectForKey:方法:
NSNumber *storedNumber = dictionary[@"magicNumber"]; |
Mutability
If you need to add or remove objects from a dictionary after creation, you need to use the NSMutableDictionary
subclass, like this:
如果你需要从一个创建好的字典中添加或者删除对象,你需要使用NSMutableDictionary子类,像这样:
[dictionary setObject:@"another string" forKey:@"secondString"]; |
[dictionary removeObjectForKey:@"anObject"]; |
Represent nil with NSNull
It’s not possible to add nil
to the collection classes described in this section because nil
in Objective-C means “no object.” If you need to represent “no object” in a collection, you can use the NSNull
class:
本节讨论的内容是不可能添加一个nil给集合类的,因为OC中的nil意味着没有对象。如果你需要表示没有对象的意思,就得使用NSNull类:
NSArray *array = @[ @"string", @42, [NSNull null] ]; |
NSNull
is a singleton class, which means that the null
method will always return the same instance. This means that you can check whether an object in an array is equal to the sharedNSNull
instance:
NSNull是一个单例类,这就意味着null方法总是返回同样的实例。你可以检查一个数组中的对象是否等价于NSNull实例:
for (id object in array) { |
if (object == [NSNull null]) { |
NSLog(@"Found a null object"); |
} |
} |
Use Collections to Persist Your Object Graph
NSArray和NSDictionary类使得写入她们的内容到磁盘中变得很容易,像这样:
NSURL *fileURL = ... |
NSArray *array = @[@"first", @"second", @"third"]; |
|
BOOL success = [array writeToURL:fileURL atomically:YES]; |
if (!success) { |
// an error occured... |
} |
If every contained object is one of the property list types (NSArray
, NSDictionary
, NSString
, NSData
, NSDate
and NSNumber
), it’s possible to recreate the entire hierarchy from disk, like this:
如果每个被包含的对象都是属性列表类型(NSArray,NSDictionary,NSString,NSData,NSDate和NSNumber),你可能从磁盘中重新创建整个层级结构,像这样:
NSURL *fileURL = ... |
NSArray *array = [NSArray arrayWithContentsOfURL:fileURL]; |
if (!array) { |
// an error occurred... |
} |
For more information on property lists, see Property List Programming Guide.
如果你需要坚持使用其他类型的对象不仅仅是上面所示的标准属性列表类,您可以使用一个文档对象,如NSKeyedArchiver,为收集到的对象创建一个归档文件。
创建文档的唯一要求是每个对象必须支持NSCoding协议。这意味着每个对象必须把自己编码成一个文档(通过实现encodeWithCoder:方法),同时需要知道如何解码(initWithCoder:方法)。
NSArray,NSSet和NSDictionary类,还有她们的子类都是支持NSCoding的,这意味着你可以用文档坚持使用复杂层级的对象。例如,如果你使用界面构建器布置窗口和视图,随之而来的nib文件只是一个存档您已经创建了视觉的对象层次结构。在运行时,nib文件不会使用相关的类被归档为一个对象的层次结构。
The NSArray
, NSSet
and NSDictionary
classes, and their mutable subclasses, all support NSCoding
, which means you can persist complex hierarchies of objects using an archiver. If you use Interface Builder to lay out windows and views, for example, the resulting nib file is just an archive of the object hierarchy that you’ve created visually. At runtime, the nib file is unarchived to a hierarchy of objects using the relevant classes.
For more information on Archives, see Archives and Serializations Programming Guide.
Use the Most Efficient Collection Enumeration Techniques
OC和Cocoa或者Cocoa Touch提供了许多方法用来列举集合中的内容。尽管你可以使用一个传统的C循环遍历内容,像这样:
int count = [array count]; |
for (int index = 0; index < count; index++) { |
id eachObject = [array objectAtIndex:index]; |
... |
} |
惯例是使用本节描述的其他技术实现。
Fast Enumeration Makes It Easy to Enumerate a Collection
许多集合类都遵守NSFastEnumeration协议,包括NSArray,NSSet和NSDictionary。这意味着你可以使用快速枚举,她是OC语言级别的特性。
The fast enumeration syntax to enumerate the contents of an array or set looks like this:
for (<Type> <variable> in <collection>) { |
... |
} |
例如,你可以使用快速枚举器来输出数组中的每个对象:
for (id eachObject in array) { |
NSLog(@"Object: %@", eachObject); |
} |
eachObject变量为每个通过循环的当前对象自动输出结果,因此会有一个日志表在每个对象的前面。
如果你在字典里使用快速枚举器,你需要遍历字典的键,像这样:
for (NSString *eachKey in dictionary) { |
id object = dictionary[eachKey]; |
NSLog(@"Object: %@ for key: %@", object, eachKey); |
} |
Fast enumeration behaves much like a standard C for
loop, so you can use the break
keyword to interrupt the iteration, or continue
to advance to the next element.
快速枚举器表现的更像是C的循环,因此你可以使用break关键字截断列举,或者continue关键字进入下一个循环。
如果你列举有序的集合,枚举器会遵循集合的顺序。对于NSArray来说,第一个通过的对象下标为0,第二个是1,等等。如果你需要知道当前的下标,简单的计算一下当前迭代的次数就可以了:
int index = 0; |
for (id eachObject in array) { |
NSLog(@"Object at index %i is: %@", index, eachObject); |
index++; |
} |
在快速枚举的时候你不可以改变一个集合,即使集合是可变的。如果你尝试在循环中添加或者删除一个对象,会有一个运行时的异常。
Most Collections Also Support Enumerator Objects
你也可以列举许多Cocoa和Cocoa Touch集合元素通过使用NSEnumerator对象。
例如,你可以访问NSArray,用一个objectEnumerator或者一个reverseObjectEnumerator对象。这些对象可以使用快速枚举器,像这样:
for (id eachObject in [array reverseObjectEnumerator]) { |
... |
} |
In this example, the loop will iterate over the collected objects in reverse order, so the last object will be first, and so on.
本例中,循环将以相反的顺序迭代数据,因此最后一个对象最先输出,等等。
还可以反复的调用枚举器nextObject方法迭代出内容,像这样:
id eachObject; |
while ( (eachObject = [enumerator nextObject]) ) { |
NSLog(@"Current object is: %@", eachObject); |
} |
In this example, a while
loop is used to set the eachObject
variable to the next object for each pass through the loop. When there are no more objects left, the nextObject
method will return nil
, which evaluates as a logical value of false so the loop stops.
Note: Because it’s a common programmer error to use the C assignment operator (=
) when you mean the equality operator (==
), the compiler will warn you if you set a variable in a conditional branch or loop, like this:
因为有一个很普遍的错误,就是当你想使用操作符==的意思的时候,你使用了操作符=,编译器会警告:
if (someVariable = YES) { |
... |
} |
如果你真想分配变量值,就用():
if ( (someVariable = YES) ) { |
... |
} |
和快速枚举器一样,你不可以改变集合当枚举进行的时候。正如名字显示的那样,使用快速枚举要比手动使用枚举对象快。
Many Collections Support Block-Based Enumeration
It’s also possible to enumerate NSArray
, NSSet
and NSDictionary
using blocks. Blocks are covered in detail in the next chapter.