Swift3.0朝圣之路-使用Runtime在分類Extension中添加屬性

我最近自學Swift3.0,由于之前沒學過Swift,只能將OC的代碼“翻譯”成Swift,在此過程慢慢學習Swift,Swift3.0的資料少,遇到了不少坑,今天就介紹一個。

在OC里面,咱給分類添加屬性是這么寫的,即使用Runtime中的objc_setAssociatedObjectobjc_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吧,隊友一看代碼能知道啥意思嗎?慶幸的是StringhashValue (哈希值)可以返回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朝圣之路-全集地址

  1. Swift3.0閉包的使用詳解,簡單封裝GET/POST網絡請求
  2. WKWebView的使用詳解,包括JS交互
  3. 原來MapKit的簡單使用,包括定位+地圖+地理編碼
  4. OC+Swift混編,介紹高德地圖SDK的簡單使用,包括定位+地圖+POI搜索+導航+UISearchController使用
  5. 協議代理的基礎用法
  6. 分類/類別的使用和封裝
  7. 【Then協議庫】-眼前一亮的初始化方式
  8. 使用Runtime在分類Extension中添加屬性
  9. 封裝UIAlertController
  10. 自定義相冊【尚未完成】
  11. 用原生框架掃描、識別二維碼圖片,生成黑白色、彩色二維碼圖片
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 因為要結局swift3.0中引用snapKit的問題,看到一篇介紹Xcode8,swift3變化的文章,覺得很詳細...
    uniapp閱讀 4,502評論 0 12
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,768評論 0 9
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,232評論 4 61
  • 這個月媽媽和我商量把早起的時間推遲五分鐘,6:15起床,因為經過一個月的操練,我早上起床后穿衣、洗漱的時間比之前快...
    xiaoluoer閱讀 220評論 0 0
  • 夏目,一個高高瘦瘦,說話溫柔的少年,無論何時,你與他交談,他都會微笑著回應著你,他的笑容,溫柔的言語,溫暖了他的世...
    梨窩笑臉閱讀 553評論 6 2