考虑UIColor
上的两个私有方法:
styleString
,它返回颜色的RGB字符串_systemDestructiveTintColor
,它返回破坏性按钮使用的红色。UIColor.h private header for reference
对于实例方法,我可以创建@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
}
}
答案 0 :(得分:3)
但有没有办法公开这些私有类方法
这就像是说你要一份由牛排组成的素食餐。你可以吃牛排,但这不是纯素餐。单词"私人"和"暴露"是对立的。
使用Objective-C的动态消息解决了这个问题。您可以通过performSelector
及其系列方法使用它。你已经知道了这一点,所以很难看到还有什么可以想要的。
如果您更喜欢使用#selector
语法,则可以使用包含目标函数的class
方法创建虚拟static
协议,以便为自己提供引用该方法的方法。然而,你的整个unsafeBitCast
路线无处可去。
编辑您可以通过强制转换为AnyObject将任何已知消息发送到任何Objective-C对象,并且可以使用我的虚拟class
协议使消息为已知消息:
@objc protocol Dummy {
func hiddenMethod()
}
(someObject as AnyObject).hiddenMethod()
但我不明白为什么这比协议和#selector
语法更好。
答案 1 :(得分:3)
unsafeBitCast()
是一种访问私有API的糟糕方式。
有没有办法在纯Swift中公开这些私有类方法?
有问题的答案 - 使用带有计算属性的扩展名。您仍然必须使用执行选择器,但您可以获得类型安全。
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()
的文档说明如下:
- 警告:打破Swift类型系统的保证;小心使用。几乎总有一种更好的方法可以做任何事情。
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/Builtins.def中定义为
/// reinterpretCast has type T -> U.
BUILTIN_SIL_OPERATION(ReinterpretCast, "reinterpretCast", Special)
ReinterpretCast
是C ++标识符,"reinterpretCast"
是Swift中的字符串名称,Special
是函数的属性,表示以下(source):
内置版具有自定义处理功能
那自定义处理在哪里?在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种对象的类型更改为另一种。协议实例方法恰好起作用,因为如果您将@objc protocol UIColorPrivate { func styleString() -> UIColor }
的位视为UIColor.styleString() -> UIColor
,则调用正确的方法。
它与类方法不起作用并不奇怪;事实上,我认为这对于例如方法来说是奇迹般的。
答案 2 :(得分:3)
通过协议实现所需的一种方法是为静态方法使用单独的协议。 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,那么如果Apple决定更改某些类内部的代码,您的代码可能会随时中断。