NSArray和NSDictionary只能存储对象,不能直接存储任何基本类型的数据(如int、float和struct)。
但是我们可以用对象来封装基本数值,然后再放入NSArray和NSDictionary中。
如果想要使用对象来处理基本类型,就可以使用NSInteger和NSUInteger,这些类型也要针对32为和64位处理器对数值进行统一。
0x01 NSNumber
Cocoa提供了NSNumber类来封装基本数据类型。
使用类方法创建NSNumber对象
可以使用以下类方法来创建新的NSNumber对象(仅举部分例子):
+ (NSNumber *) numberWithChar: (char) value;
+ (NSNumber *) numberWithInt: (int) value;
+ (NSNumber *) numberWithFloat: (float) value;
+ (NSNumber *) numberWithBool: (BOOL) value;
//示例
int a = 1;
int x = 2;
float f = 3.456;
double d = 7.89;
//int ---> obj
NSNumber *intObj = [NSNumber numberWithInt:a];
NSMutableArray *array = [NSMutableArray arrayWithObjects:intObj, nil];
//float ---> NSNumber
NSNumber *floatObj = [NSNumber numberWithFloat:f];
[array addObject:floatObj];
//double ---> NSNumber
NSNumber *doubleObj = [NSNumber numberWithDouble:d];
[array addObject:doubleObj];
使用字面量语法创建NSNumber对象
NSNumber *number;
number = @'X'; // char
number = @12345; // integer
number = @12345ul; // unsigned long
number = @12345ll; // long long
number = @123.45f; // float
number = @123.45; // double
number = @YES; // BOOL
从NSNumber中提取基本类型
将一个基本类型数据封装到NSNumber中后,我们可以通过一系列方法来重新获得它:
- (char) charValue;
- (int) intValue;
- (float) floatValue;
- (BOOL) boolValue;
- (NSString *)stringValue;
将创建方法和提取方法搭配在一起使用是可以的,如果用numberWithFloat:方法创建了NSNumber对象,然后用intValue方法来提取数值,NSNumber会对数据进行适当的转换:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
float f = 3.14;
NSNumber *fnumber = [NSNumber numberWithFloat:f];
NSLog(@"%@", fnumber);
int i = [fnumber intValue];
NSLog(@"%d", i);
}
return 0;
}
//Output:
//2018-07-27 01:20:16.457 test[1896:33403] 3.14
//2018-07-27 01:20:16.458 test[1896:33403] 3
//Program ended with exit code: 0
//
0x02 NSValue
前面提到的NSNumber实际上是NSValue的子类,NSValue主要用来封装自定义的数据结构。
可以是系统框架提供的CGRect/CGPoint/CGSize等数据结构,也可以是自己定义的struct。
NSValue提供了封装方法:
+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
同时也提供了解封方法:
- (void)getValue:(void *)value;
这里的方法名中出现了get!
它表明我们提供的是一个指针,而指针所指向的空间则是用来存放该方法生成的数据!
所以,看到这里应该明白,为什么前面的章节说,重写方法有set而不能有get!
《Objective-C继承(Inheritance)——重写方法》
下面是示例程序:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
typedef struct testTag
{
int a;
char *b;
}TEST_DATA_ST;
TEST_DATA_ST stValue = {0};
stValue.a = 58;
stValue.b = "test";
//封装struct
NSValue *value = [NSValue valueWithBytes:&stValue objCType:@encode(TEST_DATA_ST)];
NSArray *array = [NSArray arrayWithObjects:value, nil]; //封装成value之后可以装入NSArray
TEST_DATA_ST testValue = {0};
[value getValue:&testValue]; //解封value
NSLog(@"%d, %s", testValue.a, testValue.b);
}
return 0;
}
//Output:
//2018-07-27 02:08:00.450 test[2311:56190] 58, test
//Program ended with exit code: 0
//
@encode是编译器指令之一,返回一个给定类型编码为一种内部表示的字符串(例如,@encode(int) → i)。
类似于 ANSI C 的 typeof 操作。
苹果的 Objective-C 运行时库(runtime)内部利用类型编码帮助加快消息分发。
针对很常见CGRect/CGPoint/CGSize等数据结构,NSValue也提供了封装好的接口:
//Boxing
+ (NSValue *)valueWithCGPoint:(CGPoint)point;
+ (NSValue *)valueWithCGSize:(CGSize)size;
+ (NSValue *)valueWithCGRect:(CGRect)rect;
//Unboxing
- (CGPoint)CGPointValue;
- (CGSize)CGSizeValue;
- (CGRect)CGRectValue;
0x03 NSNull
NSNull是一个类,它只有一个方法:
+ (NSNull *) null;
[NSNull null]用来在NSArray和NSDictionary中加入非nil(表示列表结束)的空值。
[NSNull null]是一个对象,用来表示空,用在不能使用nil的场合。