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框架 之 常见结构体包装数据日期的主要内容,如果未能解决你的问题,请参考以下文章

iOS-Foundation框架—结构体(转载)

object-c 框架之经常使用结构体

Swift之深入解析SwiftUI属性包装器如何处理结构体

cocoa和foundation框架的区别

Foundation框架 之 NSFileManager 与 copy & mutableCopy

Foundation框架 之 NSFileManager 与 copy & mutableCopy