一氛赐、runtime介紹、OC的使用
關(guān)于runtime的介紹先舷,和OC的使用艰管,這里就不再去介紹了,大家可以看下以下這兩篇前輩們寫(xiě)的文章蒋川,一篇是runtime的介紹牲芋,一篇是runtime在OC語(yǔ)言下的使用。
1捺球、runtime介紹
2缸浦、runtime OC介紹和使用
二、runtime使用(swift開(kāi)發(fā)環(huán)境)
這里是這篇文章的主要內(nèi)容氮兵,主要介紹swift的編程語(yǔ)言下runtime的使用裂逐。這里重點(diǎn)介紹了runtime常用的三種功能:
提示:
swift運(yùn)用runtime的時(shí)候不需要再導(dǎo)入runtime庫(kù)了。
1泣栈、類拓展添加屬性
思路:
通過(guò)runtime的關(guān)聯(lián)函數(shù)
用一個(gè)關(guān)聯(lián)key
將需要添加的屬性的值存儲(chǔ)(set方法)
和獲取(get方法)
出來(lái)卜高。
步驟:
- 1、設(shè)置關(guān)聯(lián)key
- 2南片、定義要添加的屬性
- 3掺涛、重寫(xiě)屬性的set和get方法
- 4、set和get方法中需要調(diào)用runtime的關(guān)聯(lián)函數(shù)
** 代碼實(shí)現(xiàn) **
// 屬性關(guān)聯(lián)的key
private var newPropertyKey = "toNewPropertyKey"
extension ViewController {
var newproperty: String {
// 新添加屬性的set方法
set(value) {
/**
* 第一個(gè)參數(shù):關(guān)聯(lián)的對(duì)象:給哪一個(gè)對(duì)象添加關(guān)聯(lián)疼进,這里就傳哪一個(gè)對(duì)象
* 第二個(gè)參數(shù):關(guān)聯(lián)的key薪缆,通過(guò)這個(gè)key設(shè)置(存儲(chǔ))對(duì)應(yīng)的值,這里定義了一個(gè)newPropertyKey的key
* 第三個(gè)參數(shù):關(guān)聯(lián)的值伞广,即通過(guò)key所關(guān)聯(lián)的值拣帽,在get方法中獲取的就是這個(gè)值
* 第四個(gè)參數(shù):關(guān)聯(lián)的方式
*/
objc_setAssociatedObject(self, &newPropertyKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
// 新添加屬性的get方法
get{
/**
* 第一個(gè)參數(shù):關(guān)聯(lián)的對(duì)象:給哪一個(gè)對(duì)象添加關(guān)聯(lián),這里就傳哪一個(gè)對(duì)象
* 第二個(gè)參數(shù):關(guān)聯(lián)的key赔癌,通過(guò)這個(gè)key獲取對(duì)應(yīng)的值
*/
return objc_getAssociatedObject(self, &newPropertyKey) as! String
}
}
// 測(cè)試這個(gè)新屬性
override func viewDidLoad() {
super.viewDidLoad()
// 這里可以設(shè)置成功
newproperty = "newproperty"
// 這里測(cè)試下打印獲取
print(newproperty)
// 打印結(jié)果:
newproperty
}
}
本人不推薦使用上面的方式定義key,特別OC化诞外。。灾票。建議在swift語(yǔ)言下使用以下方式:
// 利用swift強(qiáng)大的結(jié)構(gòu)體去封裝關(guān)聯(lián)key峡谊,然后用swift典型的鏈?zhǔn)骄幊谭绞接眠@些key
extension ViewController {
// 私有的結(jié)構(gòu)體: 這個(gè)類擴(kuò)展下所有新添加屬性的關(guān)聯(lián)值的keys
private struct AssociateKeys {
static var toNewPropertyKey = "toNewPropertyKey" // 關(guān)聯(lián)值的key
}
var newproperty: String {
// 新添加屬性的set方法
set(value) {
/**
* 第一個(gè)參數(shù):關(guān)聯(lián)的對(duì)象:給哪一個(gè)對(duì)象添加關(guān)聯(lián),這里就傳哪一個(gè)對(duì)象
* 第二個(gè)參數(shù):關(guān)聯(lián)的key刊苍,通過(guò)這個(gè)key設(shè)置(存儲(chǔ))對(duì)應(yīng)的值既们,這里定義了一個(gè)newPropertyKey的key
* 第三個(gè)參數(shù):關(guān)聯(lián)的值,即通過(guò)key所關(guān)聯(lián)的值正什,在get方法中獲取的就是這個(gè)值
* 第四個(gè)參數(shù):關(guān)聯(lián)的方式
*/
objc_setAssociatedObject(self, &AssociateKeys.toNewPropertyKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
// 新添加屬性的get方法
get{
/**
* 第一個(gè)參數(shù):關(guān)聯(lián)的對(duì)象:給哪一個(gè)對(duì)象添加關(guān)聯(lián)啥纸,這里就傳哪一個(gè)對(duì)象
* 第二個(gè)參數(shù):關(guān)聯(lián)的key,通過(guò)這個(gè)key獲取對(duì)應(yīng)的值
*/
return objc_getAssociatedObject(self, &AssociateKeys.toNewPropertyKey) as! String
}
}
// 測(cè)試這個(gè)新屬性
override func viewDidLoad() {
super.viewDidLoad()
// 這里可以設(shè)置成功
newproperty = "newproperty"
// 這里測(cè)試下打印獲取
print(newproperty)
}
}
引深:
大家常見(jiàn)的MJRefresh(OC)上拉下拉等刷新框架婴氮,就是通過(guò)這種方式斯棒,給tableView添加一個(gè)mj_header盾致、mj_footer
2、交換方法
當(dāng)系統(tǒng)自帶的方法滿足不了你的個(gè)性化需求時(shí)荣暮,這時(shí)候需要在保證系統(tǒng)方法原有的基礎(chǔ)上庭惜,給其拓展一些功能。
滿足這樣的需求有兩種方式:
- 1穗酥、自定義一個(gè)子類护赊,重寫(xiě)父類的方法,把需要的功能寫(xiě)在里面
- 2砾跃、用runtime 交換 系統(tǒng)的方法骏啰,在調(diào)用系統(tǒng)方式的時(shí)候其實(shí)是調(diào)用自己定義的方法,然后再在自己定義的方法里調(diào)用系統(tǒng)的那個(gè)方法
特別說(shuō)明
交換方法的載體選擇抽高,OC語(yǔ)言中大家可能會(huì)選擇類方法load()或initialize()
方法判耕,但是在swift中,已經(jīng)不允許使用load()
方法了翘骂,只能選擇initialize()
方法祈秕,這個(gè)類方法會(huì)在對(duì)應(yīng)的類初始化的時(shí)候就會(huì)調(diào)用
實(shí)現(xiàn)思路
- 1、用Runtime的動(dòng)態(tài)添加方法函數(shù)將原生方法動(dòng)態(tài)添加到自定義的方法地址上
- 2雏胃、如果1添加成功请毛,再用Runtime的動(dòng)態(tài)添加方法函數(shù)將自定義的方法添加到原生方法的地址上
- 3、如果添加不成功瞭亮,將這兩個(gè)方法再用方法交換函數(shù)進(jìn)行地址交換:method_exchangeImplementations
代碼實(shí)現(xiàn)
extension UIViewController {
// 初始這個(gè)類的時(shí)候進(jìn)行處理:
public override class func initialize() {
// 只保證執(zhí)行一次:initialize()這個(gè)方法方仿,只要有UIViewController這個(gè)的子類,都會(huì)調(diào)用一次這個(gè)方法统翩,因此在這里添加一個(gè)線程鎖仙蚜,讓其只執(zhí)行一次足夠。
struct Static {
static var token: dispatch_once_t = 0
}
// 交換思路:
// 1厂汗、將原生方法動(dòng)態(tài)添加到自定義的方法地址上
// 2委粉、如果1添加成功,將自定義的方法添加到原生方法的地址上
// 3娶桦、如果添加不成功贾节,將這兩個(gè)方法再用方法交換函數(shù)進(jìn)行地址交換:method_exchangeImplementations
dispatch_once(&Static.token) {
// 獲取兩個(gè)方法
let originalSelector = #selector(viewWillAppear)
let swizzledSelector = #selector(nsh_viewWillAppear)
// 通過(guò)Selector獲取方法地址
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
// 將系統(tǒng)的方法動(dòng)態(tài)添加到自定義方法的地址上
// 參數(shù)說(shuō)明:
// 第一個(gè)參數(shù):給哪個(gè)類添加方法
// 第二個(gè)參數(shù):添加方法的方法選擇器
// 第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址)
// 第四個(gè)參數(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 {
// 之前添加不成功衷畦,再交換兩個(gè)方法的地址
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Method Swizzling
func nsh_viewWillAppear(animated: Bool) {
// 調(diào)用自定義的方法去實(shí)現(xiàn)系統(tǒng)原生的方法栗涂,因?yàn)榇藭r(shí)自定義的方法地址已經(jīng)是系統(tǒng)對(duì)應(yīng)方法的地址了
self.nsh_viewWillAppear(animated)
// TODO:自己想要添加的功能
print("nsh_viewWillAppear: \(description)")
}
}
3、遍歷獲取屬性名方法名
- 獲取屬性名:
// 記錄屬性的個(gè)數(shù)
var count : UInt32 = 0
// 獲取所有屬性祈争、個(gè)數(shù)
let ivarList = class_copyIvarList(UINavigationController().classForCoder, &count)
// 遍歷屬性獲取屬性名
for index in 0...count-1 {
// 獲取屬性名的C(C語(yǔ)言的字符串)
let propertyNameC = ivar_getName(ivarList[Int(index)])
// 將C語(yǔ)言字符串轉(zhuǎn)成Swift語(yǔ)言的字符串
let propertyName = String.fromCString(propertyNameC)
// 獲取屬性名的C(C語(yǔ)言的字符串)
let propertyTypeC = ivar_getTypeEncoding(ivarList[Int(index)])
// 將C語(yǔ)言字符串轉(zhuǎn)成Swift語(yǔ)言的字符串
let prorpertyType = String.fromCString(propertyTypeC)
print(propertyName!,prorpertyType!)
}
打印結(jié)果:
- 獲取方法名:
// 記錄方法名的個(gè)數(shù)
var count : UInt32 = 0
// 獲取所有的方法名
let methods = class_copyMethodList(UIView().classForCoder, &count)
// 遍歷數(shù)組獲取每一個(gè)方法名
for index in 0...count-1 {
// 獲取方法
let sel = method_getName(methods[Int(index)])
// 獲取方法名稱(C語(yǔ)言下的)
let methodNameC = sel_getName(sel)
// 將名稱轉(zhuǎn)為swift語(yǔ)言下
let methodName = String.fromCString(methodNameC)
print(methodName!)
}
打印結(jié)果(一部分):
不斷更新中···