版权声明:原创做品,谢绝转载!不然将追究法律责任。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用块语法作枚举。对于块的详情在下章介绍。