Runtime(swift)使用

一、runtime介紹、OC的使用

關(guān)于runtime的介紹,和OC的使用,這里就不再去介紹了,大家可以看下以下這兩篇前輩們寫的文章,一篇是runtime的介紹,一篇是runtime在OC語言下的使用。

1、runtime介紹

2、runtime OC介紹和使用

二、runtime使用(swift開發(fā)環(huán)境)

這里是這篇文章的主要內(nèi)容,主要介紹swift的編程語言下runtime的使用。這里重點介紹了runtime常用的三種功能:

提示:
swift運用runtime的時候不需要再導(dǎo)入runtime庫了。

1、類拓展添加屬性

思路:

通過runtime的關(guān)聯(lián)函數(shù)用一個關(guān)聯(lián)key將需要添加的屬性的值存儲(set方法)獲取(get方法)出來。

步驟:

  • 1、設(shè)置關(guān)聯(lián)key
  • 2、定義要添加的屬性
  • 3、重寫屬性的set和get方法
  • 4、set和get方法中需要調(diào)用runtime的關(guān)聯(lián)函數(shù)

** 代碼實現(xiàn) **

// 屬性關(guān)聯(lián)的key
private var newPropertyKey = "toNewPropertyKey"

extension ViewController {
    
    var newproperty: String {
        
        // 新添加屬性的set方法
        set(value) {
            /**
             * 第一個參數(shù):關(guān)聯(lián)的對象:給哪一個對象添加關(guān)聯(lián),這里就傳哪一個對象
             * 第二個參數(shù):關(guān)聯(lián)的key,通過這個key設(shè)置(存儲)對應(yīng)的值,這里定義了一個newPropertyKey的key
             * 第三個參數(shù):關(guān)聯(lián)的值,即通過key所關(guān)聯(lián)的值,在get方法中獲取的就是這個值
             * 第四個參數(shù):關(guān)聯(lián)的方式
             */
            objc_setAssociatedObject(self, &newPropertyKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        }
        
        // 新添加屬性的get方法
        get{
            /**
             * 第一個參數(shù):關(guān)聯(lián)的對象:給哪一個對象添加關(guān)聯(lián),這里就傳哪一個對象
             * 第二個參數(shù):關(guān)聯(lián)的key,通過這個key獲取對應(yīng)的值
             */
            return objc_getAssociatedObject(self, &newPropertyKey) as! String
        }
    }

    // 測試這個新屬性
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 這里可以設(shè)置成功
        newproperty = "newproperty"
        
        // 這里測試下打印獲取
        print(newproperty)
        
        // 打印結(jié)果:
           newproperty
    }
}

本人不推薦使用上面的方式定義key,特別OC化。。。建議在swift語言下使用以下方式:

// 利用swift強大的結(jié)構(gòu)體去封裝關(guān)聯(lián)key,然后用swift典型的鏈?zhǔn)骄幊谭绞接眠@些key

extension ViewController {
    
    // 私有的結(jié)構(gòu)體: 這個類擴展下所有新添加屬性的關(guān)聯(lián)值的keys
    private struct AssociateKeys {
        static var toNewPropertyKey = "toNewPropertyKey" // 關(guān)聯(lián)值的key
    }
    
    
    var newproperty: String {
        
        // 新添加屬性的set方法
        set(value) {
            /**
             * 第一個參數(shù):關(guān)聯(lián)的對象:給哪一個對象添加關(guān)聯(lián),這里就傳哪一個對象
             * 第二個參數(shù):關(guān)聯(lián)的key,通過這個key設(shè)置(存儲)對應(yīng)的值,這里定義了一個newPropertyKey的key
             * 第三個參數(shù):關(guān)聯(lián)的值,即通過key所關(guān)聯(lián)的值,在get方法中獲取的就是這個值
             * 第四個參數(shù):關(guān)聯(lián)的方式
             */
            objc_setAssociatedObject(self, &AssociateKeys.toNewPropertyKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        }
        
        // 新添加屬性的get方法
        get{
            /**
             * 第一個參數(shù):關(guān)聯(lián)的對象:給哪一個對象添加關(guān)聯(lián),這里就傳哪一個對象
             * 第二個參數(shù):關(guān)聯(lián)的key,通過這個key獲取對應(yīng)的值
             */
            return objc_getAssociatedObject(self, &AssociateKeys.toNewPropertyKey) as! String
        }
    }
    
    // 測試這個新屬性
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 這里可以設(shè)置成功
        newproperty = "newproperty"
        
        // 這里測試下打印獲取
        print(newproperty)
    }
}

引深:

大家常見的MJRefresh(OC)上拉下拉等刷新框架,就是通過這種方式,給tableView添加一個mj_header、mj_footer

2、交換方法

當(dāng)系統(tǒng)自帶的方法滿足不了你的個性化需求時,這時候需要在保證系統(tǒng)方法原有的基礎(chǔ)上,給其拓展一些功能。

滿足這樣的需求有兩種方式:

  • 1、自定義一個子類,重寫父類的方法,把需要的功能寫在里面
  • 2、用runtime 交換 系統(tǒng)的方法,在調(diào)用系統(tǒng)方式的時候其實是調(diào)用自己定義的方法,然后再在自己定義的方法里調(diào)用系統(tǒng)的那個方法

特別說明

交換方法的載體選擇,OC語言中大家可能會選擇類方法load()或initialize()方法,但是在swift中,已經(jīng)不允許使用load()方法了,只能選擇initialize()方法,這個類方法會在對應(yīng)的類初始化的時候就會調(diào)用

實現(xiàn)思路

  • 1、用Runtime的動態(tài)添加方法函數(shù)將原生方法動態(tài)添加到自定義的方法地址上
  • 2、如果1添加成功,再用Runtime的動態(tài)添加方法函數(shù)將自定義的方法添加到原生方法的地址上
  • 3、如果添加不成功,將這兩個方法再用方法交換函數(shù)進行地址交換:method_exchangeImplementations

代碼實現(xiàn)

extension UIViewController {
    
    // 初始這個類的時候進行處理:
    public override class func initialize() {
        
        // 只保證執(zhí)行一次:initialize()這個方法,只要有UIViewController這個的子類,都會調(diào)用一次這個方法,因此在這里添加一個線程鎖,讓其只執(zhí)行一次足夠。
        struct Static {
            static var token: dispatch_once_t = 0
        }
        // 交換思路:
        // 1、將原生方法動態(tài)添加到自定義的方法地址上
        // 2、如果1添加成功,將自定義的方法添加到原生方法的地址上
        // 3、如果添加不成功,將這兩個方法再用方法交換函數(shù)進行地址交換:method_exchangeImplementations
        dispatch_once(&Static.token) {
            
            // 獲取兩個方法
            let originalSelector = #selector(viewWillAppear)
            let swizzledSelector = #selector(nsh_viewWillAppear)
            
            // 通過Selector獲取方法地址
            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
            
            // 將系統(tǒng)的方法動態(tài)添加到自定義方法的地址上
            // 參數(shù)說明:
            // 第一個參數(shù):給哪個類添加方法
            // 第二個參數(shù):添加方法的方法選擇器
            // 第三個參數(shù):添加方法的函數(shù)實現(xiàn)(函數(shù)地址)
            // 第四個參數(shù):函數(shù)的類型
            let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
            
            // 上一步添加成功,再將自定義的添加到原生方法的地址上
            if didAddMethod {
                class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            } else {
                // 之前添加不成功,再交換兩個方法的地址
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
    }
    
    // MARK: - Method Swizzling
    
    func nsh_viewWillAppear(animated: Bool) {
        // 調(diào)用自定義的方法去實現(xiàn)系統(tǒng)原生的方法,因為此時自定義的方法地址已經(jīng)是系統(tǒng)對應(yīng)方法的地址了
        self.nsh_viewWillAppear(animated)
        
        // TODO:自己想要添加的功能
        print("nsh_viewWillAppear: \(description)")
    }
}

3、遍歷獲取屬性名方法名

  • 獲取屬性名:
 // 記錄屬性的個數(shù)
 var count : UInt32 = 0
 // 獲取所有屬性、個數(shù)
 let ivarList = class_copyIvarList(UINavigationController().classForCoder, &count)
 // 遍歷屬性獲取屬性名
 for index in 0...count-1 {
     // 獲取屬性名的C(C語言的字符串)
     let propertyNameC = ivar_getName(ivarList[Int(index)])
     // 將C語言字符串轉(zhuǎn)成Swift語言的字符串
     let propertyName = String.fromCString(propertyNameC)
     // 獲取屬性名的C(C語言的字符串)
     let propertyTypeC = ivar_getTypeEncoding(ivarList[Int(index)])
     // 將C語言字符串轉(zhuǎn)成Swift語言的字符串
     let prorpertyType = String.fromCString(propertyTypeC)
     print(propertyName!,prorpertyType!)
 }
 

打印結(jié)果:


Snip20160911_2.png
  • 獲取方法名:
// 記錄方法名的個數(shù)
var count : UInt32 = 0
// 獲取所有的方法名
let methods = class_copyMethodList(UIView().classForCoder, &count)
// 遍歷數(shù)組獲取每一個方法名
for index in 0...count-1 {
    // 獲取方法
    let sel = method_getName(methods[Int(index)])
    // 獲取方法名稱(C語言下的)
    let methodNameC = sel_getName(sel)
    // 將名稱轉(zhuǎn)為swift語言下
    let methodName = String.fromCString(methodNameC)
    print(methodName!)
}

打印結(jié)果(一部分):

Snip20160911_4.png

不斷更新中···

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,320評論 4 61
  • 有幸參加了易效能天使6班的學(xué)習(xí)。今天是第11天。非常感動,能和這么一大群積極向上,互相關(guān)愛,互相監(jiān)督,共同進步的天...
    季開宇閱讀 202評論 1 1
  • 可能每個人心中都有個環(huán)球夢,然而終其一生實現(xiàn)的卻沒有幾個! 曾經(jīng)我以為環(huán)球旅行這不能稱之為夢想,可今天我發(fā)現(xiàn)這正是...
    張偉健閱讀 429評論 0 0
  • 不常聯(lián)系的D突然發(fā)信息說他做錯事了,不知道怎么辦,很想逃避,想自殺……我腦子迅速轉(zhuǎn)動,善良如他,必不會做出...
    杏運吧閱讀 704評論 0 1
  • 做服裝銷售,有很多的銷售技巧,對于服裝銷售的技巧,我們或許都知道一點,比如我們經(jīng)常會遇到顧客說:把零頭抹了我就買。...
    瘦朵朵教你瘦閱讀 1,740評論 0 3

友情鏈接更多精彩內(nèi)容