在平时开发过程中,几乎所有人都接触过泛型这个概念,苹果从IOS9以后引入了泛型这个概念,从系统库到我们自定义泛型中,或多或少都有一定的理解。所谓的泛型,即为了在使用某个对象时,提前申明好这里应该放什么类型的数据,避免使用者对数据类型的捉摸不清。
下面举几个例子分别说明,让大家影响更为的深刻。
1.泛型是什么
泛型是对集合中对象的一种抽象,俗话说:越抽象的功能越好用。从集合中取出对象时,不需要进行类型转换,可直接调用方法。
NSArray *arrays;
arrays.firstObject.length;
2.泛型语法
@interface 类名 < __covariant ObjectType : id >
我们以NSArray
来举例分析:
@interface NSArray<__covariant ObjectType> : NSObject
@property (readonly) NSUInteger count;
- (ObjectType)objectAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@end
可以指定泛型数据类型,默认为id(可以不写),作用域为@ interface到@end之间
3.集合类型中泛型应用
1.1 NSArray中的自定义泛型
比如说,创建了一个数组对象,只允许里面存放字符串类型的数据。那么我们该如何设计呢?这里典型的一个泛型思想。
@property (nonatomic,strong)NSMutableArray <__kindof NSString *>*list;
就是这么简单。我们再向其中添加几个number数据,看下会有什么情况发生?
[self.list addObject:@(1)];
经典的报错信息如下
大概意思,你明明定义了一个只会存放字符串的数组,却要往里面塞数字。所以,编译器就告诉了我们这是不允许的。所以,这就是泛型的一个好处了
提前申明
。
1.2 NSArray官方定义泛型
查看NSArray源码,我们可以发现有几个奇怪的关键字不是很熟悉。
@interface NSArray<__covariant ObjectType> : NSObject
@property (readonly) NSUInteger count;
- (ObjectType)objectAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@end
我们可以看到有个关键字__covariant
,它表示协变
, 用于泛型数据强转类型,可以向上强转,子类可以转成父类。
4. 普通对象中的自定义泛型
当我们申明一个Person类型,也可以自定义泛型(替代id这种古老的写法)
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, strong) ObjectType langguage;
@end
NS_ASSUME_NONNULL_END
iOS *ios = [[iOS alloc] init];
Java *java = [[Java alloc] init];
Person *john = [[Person alloc] init];
john.language = iOS;
Person *david = [[Person alloc] init];
david.language = java;
其实这里的ObjectType类型就是上面说的泛型。这里我们把per1指向per。(没有添加协变)
- (void)test2 {
IOS *iOS = [IOS new];
JAVA *java = [JAVA new];
Person *per = [Person new];
per.langguage = iOS;
Person *per1 = [Person new];
per1 = per;
}
为了解决上面那个编译器警告,我们可以添加协变关键字,来避免编译器警告.
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person<__covariant ObjectType> : NSObject
@property (nonatomic, strong) ObjectType langguage;
@end
NS_ASSUME_NONNULL_END
协变
允许子类转化为父类。系统默认都是这种行为。
同样的我们把代码稍微改一下,让per指向per1
IOS *iOS = [IOS new];
JAVA *java = [JAVA new];
Person *per = [Person new];
per.langguage = iOS;
Person *per1 = [Person new];
per = per1;
结果报错如下:
对应的,还有个__contravariant
,它表示逆变
, 可用于泛型数据强转换,可以向下强转,允许父类可以转成子类
。
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person<__contravariant ObjectType> : NSObject
@property (nonatomic, strong) ObjectType langguage;
@end
NS_ASSUME_NONNULL_END
这样就可以避免编译器错误。