在 iOS 13 之前,一般是使用 KVC 来实现访问私有的实例变量,比如要访问 UITextField 的 _placeholderLabel 一般会这么做:
extension UITextField {
var placeholderLabel: UILabel? {
get {
return value(forKey: "_placeholderLabel") as? UILabel
}
}
}
一直到 iOS 12,这个方法运行的很好,但是升级到了 iOS 13,运行的时候就崩溃了:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug'
意思很明确,就是说禁止访问 ivar,而且这是 APP 造成的 bug。
好吧,KVC 不能用了,既要适配 iOS 13 ,又要保持原有的逻辑,怎么办呢?还有 runtime 可以一试!
extension UITextField {
var placeholderLabel: UILabel? {
get {
return getIvar(name: "_placeholderLabel") as? UILabel
}
}
}
再重新运行,APP 不再崩溃了。
getIvar 是我包装的一个扩展方法,使用 runtime 来访问 _ivar ,代码比较简单:
extension NSObject {
func getIvar(name: String) -> Any? {
guard let _var = class_getInstanceVariable(type(of: self), name) else {
return nil
}
return object_getIvar(self, _var)
}
}
从这些细节变更可以大概了解苹果对于开发者的态度,官方希望开发者尽量不要使用奇技淫巧,规规矩矩按照文档开发就行了。
目前 iOS 13 还处于仅面向开发者测试的版本,作为开发者需要踩的坑之后可能还有不少。