一、runtime介紹、OC的使用
關(guān)于runtime的介紹,和OC的使用,這里就不再去介紹了,大家可以看下以下這兩篇前輩們寫的文章,一篇是runtime的介紹,一篇是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
不斷更新中···