Foundation框架 之 常见结构体包装数据日期
Posted __Sunshine_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Foundation框架 之 常见结构体包装数据日期相关的知识,希望对你有一定的参考价值。
本文主要介绍以下几部分:
一、常见结构体
常见的结构体除了在NSString中用到的NSRange,还有以下几个:
1、CGPoint / NSPoint (苹果推荐使用CG开头的)
CGPoint与NSPoint同义,因为:
typedef CGPoint NSPoint;
在CGGeometry.h中的定义:
/* Points. */
//CGPoint结构体表示二维坐标中的一个点
struct CGPoint
CGFloat x;
CGFloat y;
;
typedef struct CGPoint CGPoint; //别名
在NSGeometry.h中的定义:
typedef struct _NSPoint
CGFloat x;
CGFloat y;
NSPoint;
对于成员变量的类型:
typedef CGFLOAT_TYPE CGFloat;
#if defined(__LP64__) && __LP64__
# define CGFLOAT_TYPE double //64位下CGFloat是double
#else
# define CGFLOAT_TYPE float //32位下CGFloat是float
#endif
创建:
CG_INLINE CGPoint CGPointMake(CGFloat x, CGFloat y)
CGPoint p; p.x = x; p.y = y; return p;
NS_INLINE NSPoint NSMakePoint(CGFloat x, CGFloat y)
NSPoint p;
p.x = x;
p.y = y;
return p;
2、CGSize / NSSize
CGSize与NSSize同义,因为:
typedef CGSize NSSize;
定义:
/* Sizes. */
//CGSize结构体包含了宽度和高度的值(用来表示物体的尺寸)
struct CGSize
CGFloat width;
CGFloat height;
;
typedef struct CGSize CGSize;
typedef struct _NSSize
CGFloat width; //不能为负数
CGFloat height; //不能为负数
NSSize;
创建:
CG_INLINE CGSize CGSizeMake(CGFloat width, CGFloat height)
CGSize size; size.width = width; size.height = height; return size;
NS_INLINE NSSize NSMakeSize(CGFloat w, CGFloat h)
NSSize s;
s.width = w;
s.height = h;
return s;
3、CGRect / NSRect
CGRect 与NSRect同义,因为:
typedef CGRect NSRect;
定义:
/* Rectangles. */
//表示一个矩形的位置和尺寸
struct CGRect
CGPoint origin;
CGSize size;
;
typedef struct CGRect CGRect;
typedef struct _NSRect
NSPoint origin;
NSSize size;
NSRect;
创建:
CG_INLINE CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
CGRect rect;
rect.origin.x = x; rect.origin.y = y;
rect.size.width = width; rect.size.height = height;
return rect;
NS_INLINE NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h)
NSRect r;
r.origin.x = x;
r.origin.y = y;
r.size.width = w;
r.size.height = h;
return r;
二、包装数据
在Objective-C中需要通过装箱(boxing)把基本数据类型或结构体等包装成OC对象,也要通过拆箱(unboxing)从OC对象转换为基本数据类型或结构体等。
1、NSNumber
NSNumber是一个轻量级的、用来将C的基本数据类型数据打包成面向对象的包装器(wrapper),它的功能就是用来存储和返回基本数据类型的值。
因为NSArray、NSDictionary和其他Foundation框架的集合只能存放OC对象,而不能存放基本数据类型,所以基本数据类型要被打包成NSNumber对象(也是OC对象)才能放进数组或字典等中。
用法如下:
// 创建: C的基本数据类型 --> NSNumber
// 旧写法:
NSNumber *aChar = [NSNumber numberWithChar:'t'];
NSNumber *aUChar = [NSNumber numberWithUnsignedChar:255];
NSNumber *aShort = [NSNumber numberWithShort:500];
NSNumber *aUShort = [NSNumber numberWithUnsignedShort:65535];
NSNumber *aInt = [NSNumber numberWithInt:INT_MAX];
NSNumber *aUInt = [NSNumber numberWithUnsignedInt:4294967295];
NSNumber *aLong = [NSNumber numberWithLong:LONG_MAX];
NSNumber *aULong = [NSNumber numberWithUnsignedLong:123456789];
NSNumber *aLongLong = [NSNumber numberWithLongLong:LONG_LONG_MAX];
NSNumber *aFloat = [NSNumber numberWithFloat:3.14f]; // = @"3.140000"
NSNumber *aDouble = [NSNumber numberWithDouble:3.14]; // = @"3.14"
NSNumber *aBool = [NSNumber numberWithBool:YES]; // = @"1"
// Xcode4.4之后的新写法(推荐):
NSNumber *aBool_2 = @NO;
NSNumber *aChar_2 = @'a';
NSNumber *aInt_2 = @100;
NSNumber *aUInt_2 = @500000U; //注意加 U, L, F
NSNumber *aLong_2 = @111111111L;
NSNumber *aFloat_2 = @3.14F;
NSNumber *aDouble_2 = @3.14;
//还可以加括号将 表达式或变量 包装成 NSNumber对象
NSNumber *result = @(25 * 4); // 把表达式的值转成NSNumber对象
int num = 200;
NSNumber *aNum = @(num); // 把变量转成NSNumber对象
// 取值:NSNumber --> C的基本数据类型
NSLog(@"%c", aChar.charValue); //用的是getter方法,也可以用 [aChar charValue].下同
NSLog(@"%hhu", aUChar.unsignedCharValue);
NSLog(@"%hi", aShort.shortValue);
NSLog(@"%hu", aUShort.unsignedShortValue);
NSLog(@"%i", aInt.intValue);
NSLog(@"%u", aUInt.unsignedIntValue);
NSLog(@"%li", aLong.longValue);
NSLog(@"%lu", aULong.unsignedLongValue);
NSLog(@"%lli", aLongLong.longLongValue);
NSLog(@"%f", aFloat.floatValue);
NSLog(@"%lf", aDouble.doubleValue);
NSLog(@"%@", aBool.boolValue ? @"YES" : @"NO");
NSString *str = [aInt stringValue]; //把 NSNumber对象的值表达成可读的字符串
比较大小:
类似NSString,可以用对象方法isEqualToNumber:比较两NSnumber的数值是否相等:
- (BOOL)isEqualToNumber:(NSNumber *)number;
也可以compare:对象方法比较两者孰大孰小:
- (NSComparisonResult)compare:(NSNumber *)otherNumber;
局限性:
不可修改,每次更新都会创建新对象。如:
NSNumber *count = @0;
for (int i = 0; i < 3; i++)
count = @([count intValue] + 1);
NSLog(@"round %d: count:%@, count addr: %p", i, count, count);
//运行结果:
//2015-11-17 21:07:36.256 FoundationTest2[2425:173572] round 0: count:1, count addr: 0x127
//2015-11-17 21:07:36.256 FoundationTest2[2425:173572] round 1: count:2, count addr: 0x227
//2015-11-17 21:07:36.256 FoundationTest2[2425:173572] round 2: count:3, count addr: 0x327
注意每次循环 count 的地址都发生了变化。
所以在频繁计算的情况下,计算过程中尽量用基本数据类型,而可以把结果存到NSNumber中去。
2、NSValue
NSValue是NSNumber的父类,它是用来存储单个C或OC数据的简单容器。
它可以保存任意标量类型的数据,如int, float, char,还有指针、结构体,以及对象id引用。
它可以将以上数据类型添加到只需要OC对象的集合(NSArray,NSSet等)、KVC等。
NSValue对象是不可变的。
但是,基本数据类型一般让NSNumber去包装,而NSValue包装指针、结构体等。
对于NSPoint等结构体,与NSValue类的转化方法在NSGeometry.h中有为NSValue类添加的类别NSValueGeometryExtensions中,如下:
@interface NSValue (NSValueGeometryExtensions)
//将结构体包装成NSValue对象
+ (NSValue *)valueWithPoint:(NSPoint)point;
+ (NSValue *)valueWithSize:(NSSize)size;
+ (NSValue *)valueWithRect:(NSRect)rect;
+ (NSValue *)valueWithEdgeInsets:(NSEdgeInsets)insets NS_AVAILABLE(10_10, 8_0);
//可以(也只能)用getter方法从NSValue对象中取出结构体
@property (readonly) NSPoint pointValue;
@property (readonly) NSSize sizeValue;
@property (readonly) NSRect rectValue;
@property (readonly) NSEdgeInsets edgeInsetsValue NS_AVAILABLE(10_10, 8_0);
@end
使用如:
//创建一个 NSRect 结构体变量 rect
NSRect rect = NSMakeRect(1, 5, 10, 20);
//将 rect 包装成 NSValue
NSValue *val = [NSValue valueWithRect:rect];
//从 NSValue 对象取出结构体
NSRect rect_2 = [val rectValue];
//打印 rect_2
NSLog(@"%@", NSStringFromRect(rect_2));
//运行结果:2015-11-17 21:54:47.641 FoundationTest2[2464:185943] 1, 5, 10, 20
如果要将自定义的结构体与NSValue进行转换,则用以下方法:
// 类方法,创建一个 NSValue 对象
// 参数: value:指向原数据(如结构体)的指针
// type: value的OC形式,通常是用来描述对象类型和大小的字符串,也通常是用@encode()编译指令来将一个数据类型的名称来生成一个合适的描述字符串。不能直接写C形式的字符串
+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
// 对象方法,从 NSValue 对象中提取数值,复制一份存放在value指针指向的内存中
- (void)getValue:(void *)value;
使用如:
myDate aDate = 2015, 11, 17;
// 结构体变量aDate --> NSValue对象
NSValue *aVal = [NSValue value:&aDate withObjCType:@encode(myDate)];
// 可以把 NSValue对象放进数组
NSArray *arrWithVal = @[aVal];
// NSValue对象 --> 结构体变量
myDate aDate_2;
[aVal getValue:&aDate_2];
// aDate_2 = 2015, 11, 17;
三、 日期
日期比较复杂,在OC中,处理日期的示意图如下:
NSDate 用来表示时间上的某个特定时刻(与日历系统的选择无关),可以通过 NSCalendar 对象的内容来提取(精简)成 NSDateComponets。
NSDate可以通过 NSDateFormatter 转换成可读的版本——NSString。
NSLocale 和 NSTimeZone 也封装了关于日期的操作的本地信息。
1、NSDate
NSDate可以进行一些常见的日期/时间处理。
一个NSDate对象代表一个时间。
//依当前的日期和时间生成一个 NSDate对象 date
NSDate *date = [NSDate date];
NSLog(@"date:%@", date);
// 输出结果:date:2015-11-17 22:51:16 +0000
// 依据时区计算日期
// 用系统返回时区
NSTimeZone *tzone = [NSTimeZone systemTimeZone];
// 计算tzone时区与格林威尔平均时间在时刻date时的差距(用秒表示)
NSInteger interval = [tzone secondsFromGMTForDate:date];
//重新生成时间:将 date + 时间差interval
NSDate *localDate = [date dateByAddingTimeInterval:interval];
NSLog(@"localDate:%@", localDate);
// 输出结果:localDate:2015-11-17 22:51:16 +0000
// 格式化日期
// (1) NSDate --> NSString
// i: 本地格式(Localized Styles)(推荐)
//定义日期时间格式化的类
NSDateFormatter *dateForm0 = [[NSDateFormatter alloc] init];
//将日期设置为短型的风格
[dateForm0 setDateStyle:NSDateFormatterShortStyle];
//将时间设置为短型的风格
[dateForm0 setTimeStyle:NSDateFormatterShortStyle];
NSString*dateStr0 = [dateForm0 stringFromDate:date];
// 结果:dateStr0 = @"15/11/18 上午10:26"
// ii: 自定义格式串(比较灵活)
//定义日期时间格式化的类
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
//设置格式。yyyy,年;MM,月;dd,号;HH,hh,小时;mm,分;ss,秒;Z,时区
dateForm.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
//依dateForm的格式,将 NSDate 对象转换为 NSString 字符串
NSString *dateStr = [dateForm stringFromDate:date];
// dateStr = @"2015-11-17 23:06:34 +0000"
// (2) NSString --> NSDate
//依dateForm的格式,将 NSString 字符串转换为 NSDate 对象
NSDate *date_2 = [dateForm dateFromString:@"2014-10-15 23:06:34 +0000"];
// date_2 = 2014-10-15 23:06:34 GMT
// 比较两时间点
NSComparisonResult timeComRes = [date compare:date_2];
// 结果:timeComRes = NSOrderedDescending ( = 1)
// 比较两时间点,取较早的那个
NSDate *earlierDate = [date earlierDate:date_2];
// 结果:earlierDate = 2014-10-15 23:06:34 GMT
NSDate *laterDate = [date laterDate:date_2];
// 结果:laterDate = 2015-11-18 09:38:16 GMT
也可以用下面类方法来生成其他时刻的 NSDate 对象
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
// 以当前时间为原点,参数secs是用秒来表示的时间差,想生成未来时间则secs是正数,过去则是负数。
// secs的类型 NSTimeInterval 是double类型
除了获取时间点,NSDate 真正的作用在于方便了比较时间。
2、NSCalendar
NSCalendar有三个重要的作用:将NSDate对象转换成components,由components生成NSDate对象,做有关时间的计算。
结合 NSCalendar 与 NSDate 可以作更多日期/时间处理。
如要从 NSDate 中获取想要的信息(如年月日):
// (1)用 NSCalendar 从 NSDate 中获取信息
// 获取 NSCalendar 对象
NSCalendar *cal = [NSCalendar currentCalendar];
// 获取日期
NSDate *dat = [NSDate date];
// 用 cal 从 dat 中提取指定的时间部分 并生成 dateComp
NSDateComponents *dateCmp = [cal components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:dat];
// 通过getter方法从 dateComp 中获取年月日的值,并打印
NSLog(@"%ld-%ld-%ld", dateCmp.year, dateCmp.month, dateCmp.day);
// 输出结果:2015-11-18
// (2)比较两个时间的差距
// (1)用 NSCalendar 从 NSDate 中获取信息
// 获取 NSCalendar 对象
NSCalendar *cal = [NSCalendar currentCalendar];
// 获取日期
NSDate *dat = [NSDate date];
// 用 cal 从 dat 中提取指定的时间部分 并生成 dateComp
NSDateComponents *dateCmp = [cal components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:dat];
// 通过getter方法从 dateComp 中获取年月日的值,并打印
NSLog(@"%ld-%ld-%ld", dateCmp.year, dateCmp.month, dateCmp.day);
// 输出结果:2015-11-18
// (2) 由 Components 生成 Dates
// 获取 NSCalendar 对象
NSCalendar *cal_2 = [NSCalendar currentCalendar];
// 获取 NSDateComponents 对象
NSDateComponents *dateCmp_2 = [[NSDateComponents alloc] init];
// 设置 dateCmp_2
[dateCmp_2 setYear:2015];
[dateCmp_2 setMonth:10];
[dateCmp_2 setDay:10];
// 由 dateCmp_2 生成 NSDate 对象
NSDate *dateFromCmp = [cal_2 dateFromComponents:dateCmp_2];
// 结果:dateFromCmp = 2015-10-10 00:00:00 GMT
// (3) 时间计算
// i: 用 NSDateComponents 对象生成其他时间点的 NSDate 对象
NSCalendar *cal_3 = [NSCalendar currentCalendar];
NSDateComponents *dateCmp_3 = [[NSDateComponents alloc] init];
// 设置月
[dateCmp_3 setMonth:1];
// 将 dateCmp_3 的时间 加到 现在时刻 date 上,生成一个新的时刻 oneMonthLater
NSDate *oneMonthLater = [cal_3 dateByAddingComponents:dateCmp_3 toDate:date options:0];
// oneMonthLater = 2015-12-18 10:09:10 GMT
// ii:比较两个时间的差距
//由 NSString 生成 两个 NSDate 日期对象
NSString *time_1 = @"2013-9-1";
NSString *time_2 = @"2015-11-18";
NSDateFormatter *dfm = [[NSDateFormatter alloc] init];
dfm.dateFormat = @"yyyy-MM-dd";
NSDate *date_3 = [dfm dateFromString:time_1];
NSDate *date_4 = [dfm dateFromString:time_2];
//设置提取的日期组件
NSUInteger unitFlags = NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay;
// 此时 unitFlags = 28, 二进制形式为:0b11100
//计算两日期的时间差,并生成日期组件
NSDateComponents *dateCmpDif = [cal components:unitFlags fromDate:date_3 toDate:date_4 options:0];
NSLog(@"the difference of dates is: %ld year %ld month %ld day", dateCmpDif.year, dateCmpDif.month,dateCmpDif.day);
// 输出结果:the difference of dates is: 2 year 2 month 17 day
// 所以 NSDateComponents 对象代表的是一段时间
上面方法中 components: 后的参数为 NSCalendarUnit 的成员,NSCalendarUnit是位枚举,表示日期的单元,定义如下:
typedef NS_OPTIONS(NSUInteger, NSCalendarUnit)
NSCalendarUnitEra = kCFCalendarUnitEra,
NSCalendarUnitYear = kCFCalendarUnitYear,
NSCalendarUnitMonth = kCFCalendarUnitMonth,
NSCalendarUnitDay = kCFCalendarUnitDay,
NSCalendarUnitHour = kCFCalendarUnitHour,
......;
CFCalendarUnit的定义如下:
typedef CF_OPTIONS(CFOptionFlags, CFCalendarUnit)
kCFCalendarUnitEra = (1UL << 1), // = 2
kCFCalendarUnitYear = (1UL << 2), // = 4
kCFCalendarUnitMonth = (1UL << 3), // = 8
kCFCalendarUnitDay = (1UL << 4), // = 16
kCFCalendarUnitHour = (1UL << 5), // = 32
......;
所以上面方法可以理解为:
3、NSLocale
NSLocale代表了特定的语言、地区、文化等的习惯。NSCalendar和NSDateFormatter的操作都依赖于NSLocale的信息。
可以生成指定地区的NSLocale对象,也可以生成当前地区的NSLocale对象(默认)。
简略使用如下:
NSDateFormatter *dfm_2 = [[NSDateFormatter alloc] init];
// 返回当前locale(默认)
NSLocale *loc = [NSLocale currentLocale];
// 把 loc 设置到 时间格式 dfm_2 去
[dfm_2 setLocale:loc];
dfm_2.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
NSString *date_loc = [dfm_2 stringFromDate:date];
// date_loc NSString * @"2015-11-18 10:53:37 +0000"
4、NSTimeZone
NSTimeZone代表了时区。
可以生成指定时区的NSTimeZone对象,也可以生成当前时区的NSTimeZone对象(默认)。
简略使用如下:
NSDateFormatter *dfm_3 = [[NSDateFormatter alloc] init];
// 返回当地时区(默认)
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
// 结果:timeZone = @"Atlantic/Reykjavik"
// 把 loc 设置到 时间格式 dfm_2 去
[dfm_3 setTimeZone:timeZone];
dfm_3.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
NSString *date_tm = [dfm_3 stringFromDate:date];
// 结果: date_tm = @"2015-11-18 11:04:06 +0000"
以上是关于Foundation框架 之 常见结构体包装数据日期的主要内容,如果未能解决你的问题,请参考以下文章