我最近自學Swift3.0,由于之前沒學過Swift,只能將OC的代碼“翻譯”成Swift,在此過程慢慢學習Swift,Swift3.0的資料少,遇到了不少坑,今天就介紹一個。
在OC里面,咱給分類添加屬性是這么寫的,即使用Runtime
中的objc_setAssociatedObject
和objc_getAssociatedObject
- (void)setQuickTapEnable:(BOOL)quickTapEnable{
objc_setAssociatedObject(self, JKSecurityButtonQuickTapEnableKey, @(quickTapEnable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)quickTapEnable{
return [objc_getAssociatedObject(self, JKSecurityButtonQuickTapEnableKey) boolValue];
}
這次咱用Swift3.0給ViewController的Extension(相當于OC里面的Category)添加一個屬性JKPro,賦值后再取出來打印,練練手。
ViewControll類文件的代碼:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.jkPro = "通過類別拓展的屬性"
NSLog(self.jkPro!)
print("標記")
}
}
ViewControll Extension文件的代碼
extension ViewController {
// 平常寫法[不推薦]
var jkPro: String? {
set {
objc_setAssociatedObject(self, "key", newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
get {
return objc_getAssociatedObject(self, "key") as? String
}
}
}
你覺得有問題嗎? 我告訴你,有
運行多次后會出現隨機性的崩潰,也就是說基本上能正常運行和打印結果,但是偶爾會出現崩潰,我也一直沒找到根本原因,不過找到了解決方案。
崩潰后會進入下面的界面:
運行10次左右會隨機出現崩潰,提示
fatal error: unexpectedly found nil while unwrapping an Optional value
,也就是中間出現了nil再看看崩潰點的代碼: 解包出錯!
那就從源頭找問題,后面改成下面寫法來測試崩潰,運行多次后發現是
objc_getAssociatedObject
返回的值為nil,導致解包崩潰。有時正常,有時nil,什么鬼??????
再看看objc_setAssociatedObject
函數方法,用到了UnsafeRawPointer
類型的參數,沒接觸過,那就從UnsafeRawPointer
入手
public func objc_setAssociatedObject(_ object: Any!, _ key: UnsafeRawPointer!, _ value: Any!, _ policy: objc_AssociationPolicy)
通常咱用字符串來命名以及區分Key值,然而UnsafeRawPointer
并沒有String
參數的init方法,倒是有個Int
參數的init方法,但是咱不能用數字做Key吧,隊友一看代碼能知道啥意思嗎?慶幸的是String
的hashValue (哈希值)
可以返回Int值,而字符串的哈希值和字符串是一一對應的,測試多次都沒出現崩潰,完美。
下面就是一種解決方案,應該還有其他的,只是等著大家去發現
/// 推薦寫法
var jkPro: String? {
set {
let key: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "key".hashValue)
objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
get {
let key: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "key".hashValue)
let obj: String? = objc_getAssociatedObject(self, key) as? String
return obj
}
}
-------------------------繼續改進-------------------------
之前崩潰原因已找到,之所以出現nil,是因為2次使用的Key
內存地址不一樣導致的,即取值和設值的Key內存地址不一樣導致取出nil,解包nil崩潰。
同一字符串的哈希值是一樣的,所以方向是對的,于是下午繼續優化了下。使用結構體struct
作為容器聲明不同的Key,以后改也只要一個改地方就行,便于管理,而且相比上午的寫法代碼量更少,更簡潔。
如果有其他的思路,歡迎一起討論。
// 改進寫法【推薦】
struct RuntimeKey {
static let jkKey = UnsafeRawPointer.init(bitPattern: "JKKey".hashValue)
/// ...其他Key聲明
}
var jkPro: String? {
set {
objc_setAssociatedObject(self, ViewController.RuntimeKey.jkKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
get {
return objc_getAssociatedObject(self, ViewController.RuntimeKey.jkKey) as? String
}
}
我所有Swift3.0練習Demo都放到了Github上,并且在不斷更新。
Swift3.0朝圣之路-全集地址