如何强制NSObject基类的子类重写类级变量?(含协议实现场景)
如何强制基于NSObject的基类的子类重写类级变量?
咱们先把核心需求掰扯清楚:我们有个FooBase类,因为受不可控的外部框架限制,必须实现ObjCProtocolRequiringBla协议,目前的代码是这样的:
class FooBase : NSObject, ObjCProtocolRequiringBla { class var possibleValues:[String] { // 注意:这种场景推荐用preconditionFailure而非fatalError // 运行时二者都会终止执行,但preconditionFailure更偏向于前置条件检查,日志输出也更贴合这类场景 preconditionFailure("子类必须重写这个类级变量") } }
接下来聊聊怎么实现强制子类重写这个类级变量,分几个实用的方案:
1. 编译期强制检查(最优选择)
Swift里没法直接给类级属性加@required来强制子类重写,但我们可以换个思路——把类级属性转成带@required标记的类方法,再用属性做一层桥接,这样编译期就能直接拦截没重写的子类:
class FooBase : NSObject, ObjCProtocolRequiringBla { // 用@required强制子类实现这个类方法 @required class func possibleValues() -> [String] { preconditionFailure("子类必须实现这个方法") } // 桥接成协议要求的类级属性,对外保持接口一致 class var possibleValues:[String] { return self.possibleValues() } }
这样一来,子类要是没实现possibleValues()方法,编译时直接报错,从根源上避免漏写的问题,还完全符合协议的要求。
2. 运行时检查(退而求其次)
如果因为协议限制,必须直接用类级属性的形式,那只能靠运行时检查兜底了。除了在基类属性的getter里抛preconditionFailure,还可以在类的初始化阶段主动触发检查,提前暴露问题:
class FooBase : NSObject, ObjCProtocolRequiringBla { class var possibleValues:[String] { preconditionFailure("子类必须重写这个类级变量") } override class func initialize() { super.initialize() // 只检查子类,跳过基类本身 guard self !== FooBase.self else { return } // 主动访问属性,没重写就会触发崩溃 _ = self.possibleValues } }
不过这种方式是运行时才会发现问题,不如编译期检查靠谱,但如果实在没法改属性形式,这也是个有效的兜底方案。
3. 适配ObjC协议的特性
因为这个协议是ObjC的,而ObjC本身没有类属性的概念,协议里的“类级属性”本质是用类方法模拟的。所以我们可以直接在ObjC协议里定义对应的类方法,然后在Swift中实现时,用属性桥接,同时强制子类实现类方法,也能达到强制重写的效果。
内容的提问来源于stack exchange,提问作者Mark A. Donohoe
相关产品推荐
相关产品推荐

