iOS_泛型 Generics __covariant(协变) __contravariant(逆变)

1、泛型 Generics

泛型可以让你使用自定义的类型来编写灵活的、可重用的函数和类型,可以避免重复,以清晰、抽象的方式表达其意图。
在2015年的WWDC上苹果推出了Swift 2.0版本,为了让开发者能从Objective-C更好的过度到Swift上,苹果也为Objective-C带来了Generics泛型的支持。


2、__kindof 类型限制

__kindof:类型限制
使用格式:__kindof XXClass
限制类型是:XXClass类型XXXClass子类的实例

例1:

// 返回值可以是:`UITableViewCell`或`UITableViewCell子类`的实例
- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

例2:

// 数组里可以是:`UIView`或`UIView子类`的实例
@property (nonatomic, readonly, copy) NSArray<__kindof UIView *> *subviews;

// 这样写代码就没有警告了:
UIButton *button = view.subviews.lastObject;

3、自定义泛型

声明一个Generics的格式如下:

@interface 类名 <占位类型名称>
@end

占位类型名称是自定义的,常见的有TObjectTypeValueTypeKeyType等等
这个名称的作用域仅限于@interface至其@end之间。

@interface MOCollection<T>: NSObject

@property (nonatomic, readonly) NSMutableArray<T> *elements;

- (void)addObject:(T)object;
- (BOOL)insertObject:(T)object atIndex:(NSUInteger)index;

@end

占位类型后也可以加入类型限制,如:

@interface MOCollection <T: NSString *>
@end

若不加入类型限制,则表示接受id,即任意类型。


默认不加泛型修饰类型的情况下,不同类型的泛型可以互相转换:

MOCollection *collection;
MOCollection <NSString *> *string_collection;
MOCollection <NSMutableString *> *mString_collection;
        
collection = string_collection;
string_collection = collection;
collection = mString_collection;

可以在占位泛型名称前加入修饰符__covariant(协变)或__contravariant(逆变)来控制转换关系。(详情见下文)


4、协变 和 逆变

介绍协变和逆变前,先回顾一下:subtypesupertype

subtypesupertype是面向对象开发中最常见的类型关系,即子类型父类型。通常情况下父类型出现的地方都可以用子类型的替换。

functionclosureblock:其实都是函数指针类型,都具备输入输出的能力,源类型的关系影响函数指针类型的关系主要是通过输入参数和返回值决定的,所以函数指针类型的关系受两种源类型的共同影响。


名词解释:

  • variant: 即型变
  • co + varian: 共同变化,即协变的(con: 共同)
  • contra + variant:逆变,抗变(contra: 相反;对立面)

例:有父类Person和子类Student

@interface Person : NSObject
@end
@implementation Person
@end

@interface Student : Person
@end
@implementation Student
@end

4.1、__covariant协变

用于泛型数据强转类型,可以向上强转,即子类可以转成父类。(如:参数的类型)

@interface Teacher<__covariant T: Person *> : NSObject
@end
@implementation Teacher
@end

......

Teacher<Person *> *teacher1 = [[Teacher alloc] init];
Teacher<Student *> *teacher2 = [[Teacher alloc] init];

teacher1 = teacher2; // 因为Teacher是协变的,student可以转成person
teacher2 = teacher1; // Warning:Incompatible pointer types assigning to 'Teacher<Student *> *' from 'Teacher<Person *> *'

4.2、__contravariant逆变

用于泛型数据强转类型,可以向下强转,即父类可以转成子类。(如:返回值的类型)

@interface XXTeacher<__contravariant T: XXPerson *> : NSObject
@end
@implementation XXTeacher
@end

......

Teacher<Person *> *teacher1 = [[Teacher alloc] init];
Teacher<Student *> *teacher2 = [[Teacher alloc] init];

teacher1 = teacher2; // Warning:Incompatible pointer types assigning to 'Teacher<Person *> *' from 'Teacher<Student *> *'
teacher2 = teacher1; // 因为Teacher是逆变的,person可以转成student

4.2、系统类举例

我们经常在OC中看到的泛型

  1. 例如NSArray
NSArray<NSString *> *array = @[];

看一下NSArray对泛型的定义:

@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@property (readonly) NSUInteger count;
- (ObjectType)objectAtIndex:(NSUInteger)index;
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
@end
  1. 例如NSDictionary
NSDictionary<NSString *, NSNumber *> *dict = @{
    
    };

看一下NSDictionary对泛型的定义:

@interface NSDictionary<__covariant KeyType, __covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@property (readonly) NSUInteger count;
- (nullable ObjectType)objectForKey:(KeyType)aKey;
- (NSEnumerator<KeyType> *)keyEnumerator;
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects forKeys:(const KeyType <NSCopying> _Nonnull [_Nullable])keys count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;

参考:
iOS 强大的泛型
Covariance and contravariance (computer science)
2015 Objective-C 新特性
Covariance, Contravariance以及Generics在 Swift/OC 中的应用.
Objective-C 自定义泛型


我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=956zf1adnfot

猜你喜欢

转载自blog.csdn.net/Margaret_MO/article/details/125837606