您的位置:首页 > 移动开发 > Objective-C

7.装箱和拆箱

2015-01-23 16:04 120 查看
其实从上面的例子中我们也可以看到,数组和字典中只能存储对象类型,其他基本类型和结构体是没有办法放到数组和字典中的,当然你也是无法给它们发送消息的(也就是说有些NSObject的方法是无法调用的),这个时候通常会用到装箱(boxing)和拆箱(unboxing)。其实各种高级语言基本上都有装箱和拆箱的过程,例如C#中我们将基本数据类型转化为Object就是一个装箱的过程,将这个Object对象转换为基本数据类型的过程就是拆箱,而且在C#中装箱的过程可以自动完成,基本数据类型可以直接赋值给Object对象。但是在ObjC中装箱的过程必须手动实现,ObjC不支持自动装箱。

在ObjC中我们一般将基本数据类型装箱成NSNumber类型(当然它也是NSObject的子类,但是NSNumber不能对结构体装箱),调用其对应的方法进行转换:

+(NSNumber *)numberWithChar:(char)value;

+(NSNumber *)numberWithInt:(int)value;

+(NSNumber *)numberWithFloat:(float)value;

+(NSNumber *)numberWithDouble:(double)value;

+(NSNumber *)numberWithBool:(BOOL)value;

+(NSNumber *)numberWithInteger:(NSInteger)value;

拆箱的过程就更加简单了,可以调用如下方法:

-(char)charValue;

-(int)intValue;

-(float)floatValue;

-(double)doubleValue;

-(BOOL)boolValue;

简单看一个例子
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

/*可以存放基本类型到数组、字典*/
void test1(){
//包装类NSNumber,可以包装基本类型但是无法包装结构体类型
NSNumber *number1=[NSNumber numberWithChar:'a'];//'a'是一个C语言的char类型我们无法放倒NSArray中,但是我们可以通过NSNumber包装
NSArray *array1=[NSArray arrayWithObject:number1];
NSLog(@"%@",array1);
/*结果:
(
97
)
*/

NSNumber *number2= [array1 lastObject];
NSLog(@"%@",number2);//返回的不是基本类型,结果:97

char char1=[number2 charValue];//number转化为char
NSLog(@"%c",char1); //结果:a
}

int main(int argc, const char * argv[]) {
test1();
return  0;
}



上面我们看到了基本数据类型的装箱和拆箱过程,那么结构体呢?这个时候我们需要引入另外一个类型NSValue,其实上面的NSNumber就是NSValue的子类,它包装了一些基本数据类型的常用装箱、拆箱方法,当要对结构体进行装箱、拆箱操作我们需要使用NSValue,NSValue可以对任何数据类型进行装箱、拆箱操作。

事实上对于常用的结构体Foundation已经为我们提供好了具体的装箱方法:

+(NSValue *)valueWithPoint:(NSPoint)point;

+(NSValue *)valueWithSize:(NSSize)size;

+(NSValue *)valueWithRect:(NSRect)rect;

对应的拆箱方法:

-(NSPoint)pointValue;

-(NSSize)sizeValue;

-(NSRect)rectValue;
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

//NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体
void test1(){
CGPoint point1=CGPointMake(10, 20);
NSValue *value1=[NSValue valueWithPoint:point1];//对于系统自带类型一般都有直接的方法进行包装
NSArray *array1=[NSArray arrayWithObject:value1];//放倒数组中
NSLog(@"%@",array1);
/*结果:
(
"NSPoint: {10, 20}"
)
*/

NSValue *value2=[array1 lastObject];
CGPoint point2=[value2 pointValue];//同样对于系统自带的结构体有对应的取值方法(例如本例pointValue)
NSLog(@"x=%f,y=%f",point2.x,point2.y);//结果:x=10.000000,y=20.000000
}

int main(int argc, const char * argv[]) {
test1();
return  0;
}


 

那么如果是我们自定义的结构体类型呢,这个时候我们需要使用NSValue如下方法进行装箱:

+(NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

调用下面的方法进行拆箱:

-(void)getValue:(void *)value;
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef struct {
int year;
int month;
int day;
} Date;

//NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体
void test1(){
//如果我们自己定义的结构体包装
Date date={2014,2,28};
char *type=@encode(Date);
NSValue *value3=[NSValue value:&date withObjCType:type];//第一参数传递结构体地址,第二个参数传递类型字符串
NSArray *array2=[NSArray arrayWithObject:value3];
NSLog(@"%@",array2);
/*结果:
(
"<de070000 02000000 1c000000>"
)
*/

Date date2;
[value3 getValue:&date2];//取出对应的结构体,注意没有返回值
//[value3 objCType]//取出包装内容的类型
NSLog(@"%i,%i,%i",date2.year,date2.month,date2.day); //结果:2014,2,28

}

int main(int argc, const char * argv[]) {
test1();
return  0;
}



扩展1-NSNull

通过前面的介绍大家都知道无论在数组还是在字典中都必须以nil结尾,否则数组或字典无法判断是否这个数组或字典已经结束(与C语言中的字符串比较类似,C语言中定义字符串后面必须加一个”\0”)。但是我们有时候确实想在数据或字典中存储nil值而不是作为结束标记怎么办呢?这个时候需要使用NSNull,这个类是一个单例,只有一个null方法。简单看一下:
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {

NSNull *nl=[NSNull null];//注意这是一个对象,是一个单例,只有一个方法null创建一个对象
NSNull *nl2=[NSNull null];
NSLog(@"%i",nl==nl2);//由于是单例所以地址相等,结果:1

NSArray *array1=[NSArray arrayWithObjects:@"abc",nl,@123, nil];
NSLog(@"%@",array1);
/*结果:
(
abc,
"<null>",
123
)
*/

return  0;
}


 


扩展2-@符号

我们知道在ObjC中很多关键字前都必须加上@符号,例如@protocol、@property等,当然ObjC中的字符串必须使用@符号,还有就是%@可以表示输出一个对象。其实@符号在新版的ObjC中还有一个作用:装箱。

相信聪明的童鞋在前面的例子中已经看到了,这里简单的介绍一下(在下面的演示中你也将看到很多ObjC新特性)。
//
//  main.m
//  FoundationFramework
//
//  Created by Kenshin Cui on 14-2-16.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef enum {
spring,
summer,
autumn,
winter
} Season;

int main(int argc, const char * argv[]) {
/*装箱*/
NSNumber *number1=@100;
NSArray *array1=[NSArray arrayWithObjects:number1,@"abc",@16,@'A',@16.7,@YES, nil];
NSLog(@"%@",array1);
/*结果:
(
100,
abc,
16,
65,
"16.7"
1
)
*/
NSNumber *number2=@(1+2*3);
NSLog(@"%@",number2); //结果:7
NSNumber *number3=@(autumn);
NSLog(@"%@",number3); //结果:2

NSArray *array2=@[@"abc",@16,@'A',@16.7,@YES];//使用这种方式最后不用添加nil值了
NSLog(@"%@",array2[2]); //结果:65
NSMutableArray *array3=[NSMutableArray arrayWithArray:array2];
array3[0]=@"def";
NSLog(@"%@",array3[0]); //结果:def

NSDictionary *dic1=@{@"a":@123,@"b":@'c',@"c":@YES};
NSLog(@"%@",dic1);
/*结果:
{
a = 123;
b = 99;
c = 1;
}
*/
NSMutableDictionary *dic2=[NSMutableDictionary dictionaryWithDictionary:dic1];
dic2[@"a"]=@456;
NSLog(@"%@",dic2[@"a"]);//结果:456

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  objective-c