文章目录
- 1、泛型 `Generics`
- 2、`__kindof` 类型限制
- 3、自定义泛型
- 4、协变 和 逆变
- 4.1、`__covariant`协变
- 4.2、`__contravariant`逆变
- 4.2、系统类举例
1、泛型 Generics
泛型可以让你使用自定义的类型来编写灵活的、可重用的函数和类型,可以避免重复,以清晰、抽象的方式表达其意图。
在2015年的WWDC
上苹果推出了Swift 2.0
版本,为了让开发者能从Objective-C
更好的过度到Swift
上,苹果也为Objective-C
带来了Generics
泛型的支持。
2、__kindof
类型限制
__kindof
:类型限制
使用格式:__kindof XXClass
限制类型是:XXClass类型
或XXXClass子类
的实例
例1:
代码语言:javascript复制// 返回值可以是:`UITableViewCell`或`UITableViewCell子类`的实例
- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
例2:
代码语言:javascript复制// 数组里可以是:`UIView`或`UIView子类`的实例
@property (nonatomic, readonly, copy) NSArray<__kindof UIView *> *subviews;
// 这样写代码就没有警告了:
UIButton *button = view.subviews.lastObject;
3、自定义泛型
声明一个Generics
的格式如下:
@interface 类名 <占位类型名称>
@end
占位类型名称是自定义的,常见的有T
、ObjectType
、ValueType
、KeyType
等等
这个名称的作用域仅限于@interface
至其@end
之间。
@interface MOCollection<T>: NSObject
@property (nonatomic, readonly) NSMutableArray<T> *elements;
- (void)addObject:(T)object;
- (BOOL)insertObject:(T)object atIndex:(NSUInteger)index;
@end
占位类型后也可以加入类型限制,如:
代码语言:javascript复制@interface MOCollection <T: NSString *>
@end
若不加入类型限制,则表示接受id,即任意类型。
默认不加泛型修饰类型的情况下,不同类型的泛型可以互相转换:
代码语言:javascript复制MOCollection *collection;
MOCollection <NSString *> *string_collection;
MOCollection <NSMutableString *> *mString_collection;
collection = string_collection;
string_collection = collection;
collection = mString_collection;
可以在占位泛型名称前加入修饰符__covariant
(协变)或__contravariant
(逆变)来控制转换关系。(详情见下文)
4、协变 和 逆变
介绍协变和逆变前,先回顾一下:subtype
、supertype
:
subtype
、supertype
是面向对象开发中最常见的类型关系,即子类型
和父类型
。通常情况下父类型出现的地方都可以用子类型的替换。
function
、closure
、block
:其实都是函数指针类型,都具备输入输出的能力,源类型的关系影响函数指针类型的关系主要是通过输入参数和返回值决定的,所以函数指针类型的关系受两种源类型的共同影响。
名词解释:
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
协变
用于泛型数据强转类型,可以向上强转,即子类可以转成父类。(如:参数的类型)
代码语言:javascript复制@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
逆变
用于泛型数据强转类型,可以向下强转,即父类可以转成子类。(如:返回值的类型)
代码语言:javascript复制@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中看到的泛型
- 例如
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
- 例如
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