Objective-C 中的轻量泛型(Lightweight Generics)

Aug 14, 2022 • 预计阅读时间 3 分钟

轻量泛型(Lightweight Generics)是 Xocde 7 开始增加的特性。

同时增加的另外两个新特性:可空性(Nullability)和 __kindof。

可空性

可空性是声明传入或输出的类型是否可以是 nil 值:nullable 表示可以是 nil,nonnull 表示不会是 nil,对应 Swift 里的可选类型。

在编写提供给 Swift 使用的 ObjC 类的时候,可空性是必要的。可以防止 Swift 引用的时候可以正确的使用可选类型处理 nil 值,而不是直接解包导致崩溃。

__kindof

__kindof 标识变量是可能是某个类型或其子类型。

轻量泛型

类似于 C++ 中的模板,不过是轻量级的,轻量到它其实只是一个类型占位符

@interface Base<ObjectType>: NSObject
@end

以上定义了一个类 Base<ObjectType> ,叫参数化类(parameterized class),ObjectType 是类型参数(Type arguments)。

类型参数实际上是个类型占位符,是编译器在编译期间进行类型检查的,而且只能定义接口声明 @interface 中,在实现 @implementation 里需要使用 id 来替代类型参数。

类型参数只能是 Objective-C 对象或者 Block 类型,否则编译会报错。

类型参数可以有多个, 比如声明两个类型参数 ObjectType 和 KeyType:

@interface Base<ObjectType, KeyType>: NSObject
@end

在扩展里声明类型参数的数量必须一致,名称可以不同,编译器是按顺序来做类型对应的:

@interface Base<T1, T2> (Extension) // T1 对应 ObjectType, T2 对应 KeyType
@end

如果主类没有类型参数,扩展里不能指定类型参数;反过来如果主类有类型参数,扩展里可以不写类型参数。

可以限定类型参数的范围,例如可以限定为实现了 NSCopying 协议:

@interface Base<ObjectType: id<NSCopying>>: NSObject
@end

Base<NSString *> *a1; // 编译正确
Base<UIView *> *a2; // 编译错误:type argument 'UIView *' does not satisfy the bound ('id<NSCopying>') of type parameter 'ObjectType'

限定类型范围只能写在主类里,在扩展里想实现类似 C++ 的偏特化是不允许的,编译会报错。

@interface Base<ObjectType: id<NSCopying>> (Extension) // 在扩展里限定类型范围会报错
@end

但是,可以在属性和方法中,进一步限定类型范围,例如限定 akey 是一个遵守 NSCopying 协议的对象:

@interface Base<ObjectType, KeyType>: NSObject

- (nullable ObjectType)objectForKey:(KeyType<NSCopying>)aKey;

@end

因为使用泛型后,使用不同占位符实例化的对象,它们的类型是不一样的:

Base<UIView *> *a1;
Base<NSString *> *a2;

a1 的类型是 Base<UIView *> * , a2 的类型是 Base<NSString *> * ,它们是两个不同的类型,而不是都是 Base * 类型, 可以理解为 Base 只是类模板,只有指定了具体类型后才确定最终的类型。

这种类型不匹配问题在某些情况下会限制泛型的使用,为了打破这个限制,增加了两个用于标识类型限定的关键字:

__covariant:协变性,子类型可以转到父类型。

__contravariant:逆变性,父类型可以转到子类型。

@interface NSDictionary<__covariant KeyType, __covariant ObjectType> : NSObject
@end

如果声明了一个类型:NSDictionary<NSString *, NSString *> *,那么可以使用任何 NSString * 的类型或子类型做为它的 Key 和 Value。

参考资料

https://microsoft.github.io/objc-guide/Syntax/Generics.html

iOS
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

Swift 中的 Any 和 AnyObject

双队列(Two Queue)优化 LRU 缓存算法