如何在Swift中使用协议公开一个Objective-C对象的私有类方法?

考虑UIColor上的两个私有方法:

  1. 实例方法styleString ,它返回颜色的RGBstring
  2. 类方法_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语法更好。