ObjectiveC - Typedef, Generic, Enum, @class
typedef
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| // 未使用别名
enum IColor {
kIColorBlack,
kIColorWhite
}
@interface Iphone : NSObject {
enum IColor _color; // 类型名很长
}
@end
@implementation Iphone
@end
// 使用别名
typedef enum {
kIColorBlack,
kIColorWhite
} IColor;
@interface Iphone : NSObject {
IColor _color; // 使用类型别名
}
@end
@implementation Iphone
@end
// 使用
int main(int argc, const char * argv[]) {
Iphone *p = [Iphone new];
p->_color = kIColorBlack;
return 0;
}
|
generic
iOS9+
- 用于限制类型
- 提高代码规划,减少沟通成本
- 限制集合里的存储类型,不限制的话都是 id 类型
- 限制后只能存储对应类型,且由于有了具体类型,取出时可以使用点语法
- id 类型是不能使用点语法的
- 泛型仅仅是报警告
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 不用泛型 --------
// 属性
@property (nonatomic, strong) NSMutableArray *arr;
// 使用
[_arr addObject:@"112"]; // 由于定义时没用泛型,所以这里 add 进去的其实是 id 类型
_arr[0].length; // 报错,_arr[0] 里面是 id 类型,id 类型不能使用点语法
// 使用泛型 --------
// 属性
@property (nonatomic, strong) NSMutableArray<NSString *> *arr;
// 使用
[_arr addObject:@"112"]; // add 的是 NSString 类型
_arr[0].length; // 不报错,数组里是 NSString 类型
|
1
2
| // 参数
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
|
1
2
3
4
5
6
7
8
9
10
11
12
| // 限定类型
@interface Test<ObjectType> : NSObject
@property (nonatomic, strong) ObjectType language;
@end
// 使用
Java *java = [[Java alloc] init];
iOS *ios = [[iOS alloc] init];
Test<iOS *> *t = [[Test alloc] init];
t.language = ios; // 泛型限制了,这里只能设置 iOS 类型
t.language = java; // 报警告
|
enum
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 第一种写法 (无法设置枚举值类型)
typedef enum {
DemoTypeTop,
DemoTypeBottom,
} DemoType;
// 第二种写法 (可设置枚举值类型)
typedef NS_ENUM(NSInteger, DemoType) {
DemoTypeTop,
DemoTypeBottom,
};
// 第三种写法 (位移枚举)
// 更强大,使用时可用并运算
typedef NS_OPTIONS(NSInteger, DemoType) {
DemoTypeTop = 1 << 0, // 1*2^0 = 1
DemoTypeBottom = 1 << 1, // 1*2^1 = 2
DemoTypeLeft = 1 << 2, // 1*2^2 = 4
DemoTypeRight = 1 << 3, // 1*2^3 = 8
};
|
位移枚举可进行位运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // 按位与 &
// 1&1==1 1&0==0 0&0==0
// 总结: 只有有 0 则为 0
// 按位或 |
// 1|1==1 1|0==1 0|0==0
// 总结: 只有有 1 则为 1
- (void)demo:(DemoType)type {
if (type & DemoTypeTop) {
NSLog(@"top --- %zd", type & DemoTypeTop);
}
if (type & DemoTypeBottom) {
NSLog(@"bottom --- %zd", type & DemoTypeBottom);
}
if (type & DemoTypeLeft) {
NSLog(@"left --- %zd", type & DemoTypeLeft);
}
if (type & DemoTypeRight) {
NSLog(@"right --- %zd", type & DemoTypeRight);
}
}
- (void)test {
[self demo:DemoTypeTop | DemoTypeRight]; // 打印 top 和 right
[self demo:0]; // 什么都不会打印
// 小技巧
// 如是位移枚举,可观察第一个枚举值,如该枚举值不为 0
// 那么可默认传 0 做参数,如传 0,那么效率最高 (因为什么额外操作都不会做)
}
|
枚举中的位运算
- 位移枚举,可以使用并运算
|
- 一般情况下,如方法参数是枚举值,那么可用过
|
符号,连接多个枚举值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
// 分别获取 年月日时分秒
// 如方法参数是位移枚举,那么可用过 | 符号,连接多个枚举值
NSCalendarUnit type = NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond;
NSDateComponents *cmps = [calendar components:type fromDate:now];
NSLog(@"year = %ld", cmps.year);
NSLog(@"month = %ld", cmps.month);
NSLog(@"day = %ld", cmps.day);
NSLog(@"hour = %ld", cmps.hour);
NSLog(@"minute = %ld", cmps.minute);
NSLog(@"second = %ld", cmps.second);
|
@class
作用: 可以简单的引用一个类
1
2
3
4
5
| // Person2.h 中
@class Person1; // 该文件中引入 Person1 类
// Person2.m 中
#import "Person1.h"
|
如果在 A 类中要包含 B类,B 类中又要包含 A 类,则 .h
中一定要用 @class
避免循环引入
- 总结:
- 在
.h
中使用 @class
引用一个类
- 在
.m
文件中使用 #import
包含这个类的 .h
文件
@class 其他应用场景
对于循环依赖关系来说,比方 A 类引用 B 类,同时 B 类也引用 A 类
1
2
3
4
5
6
7
8
9
10
11
12
| // 这种嵌套包含的代码编译会报错
#import "B.h"
@interface A : NSObject {
B *_b;
}
@end
#import "A.h"
@interface B : NSObject {
A *_a;
}
@end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 当使用 @class 在两个类相互声明,就不会出现编译报错
@class B
@interface A : NSObject {
B *_b;
}
@end
@class A
@interface B : NSObject {
A *_a;
}
@end
|
@class 和 #import 的区别
- 作用上的区别
- import 会包含引用类的所有信息(内容),包括引用类的变量和方法
- @class 仅仅是告诉编译器有这么个类,具体这个类有什么信息,完全不知
- 效率上的区别
- 如果有上百个头文件都
#import
了同一个文件,或者这些文件依次被 #import
,那么一旦最开始的头文件有所改动,后面引用到这个文件的所有类都需要重新编译一遍,编译效率非常低
- 相对来讲,使用
@class
就不会出现这个问题了