如何在Swift中使用协议公开一个Objective-C对象的私有类方法?
考虑UIColor
上的两个私有方法:
- 实例方法
styleString
,它返回颜色的RGBstring - 类方法
_systemDestructiveTintColor
,它返回破坏性button使用的红色。
UIColor.h私人头文件供参考
例如方法,我可以创build一个@objc
协议,并使用unsafeBitCast
公开私有方法:
@objc protocol UIColorPrivate { func styleString() -> UIColor } let white = UIColor.whiteColor() let whitePrivate = unsafeBitCast(white, UIColorPrivate.self) whitePrivate.styleString() // rgb(255,255,255)
但是,我不知道这是如何工作的类方法。
第一次尝试:
@objc protocol UIColorPrivate { class func _systemDestructiveTintColor() -> String // Error: Class methods are only allowed within classes }
有道理,我会把它改成static
:
@objc protocol UIColorPrivate { static func _systemDestructiveTintColor() -> String } let colorClass = UIColor.self let privateClass = unsafeBitCast(colorClass, UIColorPrivate.self) // EXC_BAD_ACCESS
这导致崩溃。 那么这是不快的。 我可以使用桥接头,只是暴露类的方法作为@interface
,但是有没有办法公开纯Swift私人类方法?
我可以用performSelector
做到这一点,但我宁愿将该方法作为接口或协议公开:
if UIColor.respondsToSelector("_systemDestructiveTintColor") { if let red = UIColor.performSelector("_systemDestructiveTintColor").takeUnretainedValue() as? UIColor { // use the color } }
通过协议实现你想要的一种方法是为静态方法使用单独的协议。 Objective-C
中的静态方法实际上是类的元类上的实例方法,所以你可以放心的采取如下的方法:
@objc protocol UIColorPrivateStatic { func _systemDestructiveTintColor() -> UIColor } let privateClass = UIColor.self as! UIColorPrivateStatic privateClass._systemDestructiveTintColor() // UIDeviceRGBColorSpace 1 0.231373 0.188235 1
这将给你私有方法和协议的使用,并且你摆脱了丑陋的unsafeBitCast
(不是强制转换会更漂亮)。
只要注意,如果你正在使用私有API,那么如果苹果决定改变一些类的内部,你的代码就可以随时中断。
unsafeBitCast()
是访问私有API的一种可怕的方式。
有没有办法在纯Swift中暴露这些私有类的方法?
这个问题有一个答案 – 使用扩展与计算属性。 你仍然必须使用执行select器,但是你得到了types安全。
extension UIColor { static var systemDestructiveTintColor: UIColor { let privateColor = Selector("_systemDestructiveTintColor") if UIColor.respondsToSelector(privateColor), let red = UIColor.performSelector(privateColor).takeUnretainedValue() as? UIColor { return red } return UIColor.redColor() } }
用法
let color = UIColor.systemDestructiveTintColor print(color)
更新 – 打破不安全位置
在评论中解决这个问题:
那么他们为什么会首先公开unsafeBitCast API呢?
unsafeBitCast()
的文档说明如下:
- 警告:打破了Swifttypes系统的保证; 非常小心使用。 几乎总是有更好的方法去做任何事情。
unsafeBitCast
在swift / stdlib / public / core / Builtin.swift中定义为
@_transparent public func unsafeBitCast<T, U>(_ x: T, to: U.Type) -> U { _precondition(sizeof(T.self) == sizeof(U.self), "can't unsafeBitCast between types of different sizes") return Builtin.reinterpretCast(x) }
Builtin.reinterpretCast
在swift / include / swift / AST / Builtin.reinterpretCast
中定义为
/// reinterpretCast has type T -> U. BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special)
ReinterpretCast
是C ++标识符, "reinterpretCast"
是Swift中的string名称, Special
是函数的一个属性,它表示以下内容( 源代码 ):
内build有自定义的进程
那么自定义处理在哪里? 在swift / lib / AST / Builtins.cpp中
case BuiltinValueKind::ReinterpretCast: if (!Types.empty()) return nullptr; return getReinterpretCastOperation(Context, Id); ... ... static ValueDecl *getReinterpretCastOperation(ASTContext &ctx, Identifier name) { // <T, U> T -> U // SILGen and IRGen check additional constraints during lowering. GenericSignatureBuilder builder(ctx, 2); builder.addParameter(makeGenericParam(0)); builder.setResult(makeGenericParam(1)); return builder.build(name); }
概要
不安全位转换的目的是盲目地将1种types的对象改变为另一种types。 协议实例方法只是正常工作,因为如果你把@objc protocol UIColorPrivate { func styleString() -> UIColor }
像UIColor.styleString() -> UIColor
调用正确的方法。
奇怪的是它不适用于类方法。 事实上,我会说这是奇迹,它适用于实例方法。
但有没有办法公开这些私人类的方法
这就像说你想要一顿由牛排组成的素食餐。 你可以吃牛排,但这不会是一顿纯素餐。 “私人”和“揭露”这两个词是相反的。
使用Objective-C的dynamic消息解决了这个问题。 你可以通过performSelector
及其方法来使用它。 你已经知道这一点,所以很难看到还有什么可以被要求的。
如果您更喜欢使用#selector
语法,则可以使用包含目标函数的static
方法创build一个虚拟class
协议,以便为您提供一种引用方法的方法。 然而,你的整个unsafeBitCast
路线无处可去。
编辑你可以发送任何已知的消息到任何Objective-C对象投掷到AnyObject,您可以使用我的虚拟class
协议使消息已知:
@objc protocol Dummy { func hiddenMethod() } (someObject as AnyObject).hiddenMethod()
但我不明白为什么这比协议和#selector
语法更好。