Objective-C 中的泛型

在 Swift 中用泛型用得很爽了, 回过头来有时候还是想要在 OC 中也用到这项技术。对 iOS 开发来说可能很多人都觉得 OC 是没有泛型的,但其实早在15年,苹果就在 OC 中引入了泛型。虽说引入泛型的目的是更好的实现 Swift 和 OC 的兼容(苹果叫它Lightweight generics)。

很多人都写过这样的代码:

@property NSArray<Model *> *dataSource;
复制代码

这行代码在某个类中声明了一个叫做 dataSource 的数组属性。加上 <Model *> 限制了这个数组当中的 element 必须是 Model 的对象。 在之后的代码中 self.dataSource[0] 就直接是一个 Model 对象了 这就是 OC 中最常用的范型写法。对应的 Swift 代码应该是这样的:

var dataSource: [Model]
复制代码

现在来想想这种场景。水果店、肉店、蔬菜店…. 这种情况:

class Mall<T> {
    private var stock: [T] = []
    
    func buy(_ product: T) {
        stock.append(product)
    }
    func sell() -> T? {
        let res = stock.last
        stock.removeLast()
        return res
    }
}
复制代码

这段代码声明了一个泛型类 Mall 这个类有库存stock 也有采购(buy)销售(sell)两个方法。在使用这个类的时候,我们可以先声明几个代表各类商品的协议或者枚举:

enum Fruits {
    case apple, banana
}
protocol Meats {}
class Vegetables {}
复制代码

然后在通过传入对应的类型,枚举,协议来初始化这个商店泛型类:

let mall = Mall<Fruits>()
let mall2 = Mall<Meats>()
let mall3 = Mall<Vegetables>()
复制代码

使用范型让我们规避了需要声明:FruitMall MeatsMall 等等之类的对象,然后在每个对象里面写几乎同样的代码。可能你这时候想到了使用继承或者协议,但是即使这么做确实减少了不少代码量,但是维护各种类很蛋疼啊。

同样的功能在 OC 中要怎么实现呢?

.h

@interface Mall<T> : NSObject

- (void)buy:(T)product;
- (T)sell;

@end
复制代码

.m

@interface Mall()

@property (nonatomic, strong) NSMutableArray<id> *stock;

@end
    
@implementation Mall

- (instancetype)init {
    self = [super init];
    if (self) {
        _stock = [NSMutableArray array];
    }
    return self;
}

- (void)buy:(id)product {
    [_stock addObject:product];
}

- (id)sell {
    id res = _stock.lastObject;
    [_stock removeLastObject];
    return res;
}

@end
复制代码

这段代码实现了上面同样的功能。在使用上:

还是需要有对应的协议或者类,但是枚举貌似是不可以的。

Mall<Meats *> *mall = [[Mall alloc] init];          // 类
Mall<id<Vegetables>> *mall2 = [[Mall alloc] init];  // 协议
复制代码

甚至我们还可以给 Mall 添加一个 Extension(对应 OC 是 category):

extension Mall {
    func sellAll() -> [T] {
        let res = stock
        stock.removeAll()
        return res
    }
}
复制代码

在 OC 中还有一点需要注意的是类型转换。假如有两个类,一个是另外一个的子类。如下,有饮料商店,还有一个矿泉水商店。其中 DrinksWater 的父类。

Mall<Water *> *waterMall = [[Mall alloc] init];
Mall<Drinks *> *drinkMall = waterMall;
//Warning: Incompatible pointer types initializing 'Mall<Drinks *> *' with an expression of type 'Mall<Water *> *'
复制代码

如果这样之间转换的话,编译器会抛出一个警告⚠️。如果想要将一个包含子类的范型类对象转换成一个包含父类的范型类对象,需要使用到 __covariant 这个关键字。我们再来看看 .h 文件。

@interface Mall<__covariant T> : NSObject

- (void)buy:(T)product;
- (T)sell;

@end
复制代码

相反的,还有 __contravariant 这个关键字, 允许将包含父类的范型对象转换成包含子类的范型对象。唯一的问题是: 两个关键字不能同时存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值