如何理解IOS中泛型

在平时开发过程中,几乎所有人都接触过泛型这个概念,苹果从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)];

经典的报错信息如下

image.png

大概意思,你明明定义了一个只会存放字符串的数组,却要往里面塞数字。所以,编译器就告诉了我们这是不允许的。所以,这就是泛型的一个好处了提前申明

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;
}
image.png

为了解决上面那个编译器警告,我们可以添加协变关键字,来避免编译器警告.

#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;

结果报错如下:


image.png

对应的,还有个__contravariant,它表示逆变, 可用于泛型数据强转换,可以向下强转,允许父类可以转成子类

#import 

NS_ASSUME_NONNULL_BEGIN

@interface Person<__contravariant ObjectType> : NSObject

@property (nonatomic, strong) ObjectType langguage;

@end

NS_ASSUME_NONNULL_END

这样就可以避免编译器错误。

你可能感兴趣的:(如何理解IOS中泛型)