方案1.通過動(dòng)態(tài)創(chuàng)建類和實(shí)現(xiàn)其中的方法,然后把原對(duì)象的類型修改成你創(chuàng)建的對(duì)象通過消息派發(fā)使對(duì)象調(diào)用你新實(shí)現(xiàn)的方法
extension NSObject {
//動(dòng)態(tài)創(chuàng)建類并重寫方法
func dynamicCreatClass(selector: Selector, action: (() -> ())?) {
// 創(chuàng)建類的類名
let classFullName = "ATDynamic_\(self.classForCoder.description()))"
// 獲取原來類
let originalClass = type(of: self)
// 判斷這個(gè)類是否已經(jīng)存在
if let dynamicClass = objc_allocateClassPair(originalClass, classFullName, 0) {
// 動(dòng)態(tài)的創(chuàng)建這個(gè)類
objc_registerClassPair(dynamicClass)
// 將原對(duì)象修改成新的類型
object_setClass(self, dynamicClass)
// 實(shí)現(xiàn)新的方法
let printName: @convention(block) (Any?) -> () = { nullSelf in
guard let _ = nullSelf else { return }
// 獲取原來類中方法的Imp
let originalImp = class_getMethodImplementation(originalClass, selector)
// 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> ()
// 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
let originalPrintName = unsafeBitCast(originalImp, to: IMPCType.self)
// 執(zhí)行原方法 類似super.originFuncion()
originalPrintName(self, selector)
print("Dynamic")
// 你想要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
let implementation = imp_implementationWithBlock(printName)
// 將方法加入到類的方法列表中
class_addMethod(dynamicClass, selector, implementation, "v@:")
} else if let dynamicClass = NSClassFromString(classFullName) {
// 如果類已經(jīng)存在則直接轉(zhuǎn)換
object_setClass(self, dynamicClass)
}
}
}
其中:
@convention(swift) : 表明這個(gè)是一個(gè)swift的閉包
@convention(block) :表明這個(gè)是一個(gè)兼容oc的block的閉包
@convention(c) : 表明這個(gè)是兼容c的函數(shù)指針的閉包。
定義一個(gè)People類:
class People: NSObject {
var name: String
override init() {
self.name = ""
super.init()
}
init(name: String) {
self.name = name
}
//需要添加dynamic不然是靜態(tài)派發(fā)會(huì)走原方法(或者用perform去調(diào)用)
@objc dynamic func logName() {
print(name)
}
@objc dynamic class func decInfo() {
print("People")
}
}
let p = People(name: "Albert")
p.dynamicCreatClass(selector: #selector(People.logName), action: nil)
p.logName()
// 輸出如下
Albert
Dynamic
方案2:直接替換當(dāng)前類中的方法
extension NSObject {
//替換實(shí)例方法
class func dynamicChangeInstanceMethod(selector: Selector, action: (() -> Void)?) {
// 獲取實(shí)例方法的IMP
let method = class_getInstanceMethod(self, selector)
if let method = method, self.init().responds(to: selector) {
// 獲取原來方法的IMP
let oldImp = method_getImplementation(method)
// 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> Void
// 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
// 實(shí)現(xiàn)新的方法
let newFuncion: @convention(block) (Any?) -> Void = {
(sself) in
// 執(zhí)行原來的方法類似調(diào)用super
oldImpBlock(sself, selector)
print("dynamicChangeInstanceMethod")
// 你要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
let imp = imp_implementationWithBlock(newFuncion)
// 用新方法替換舊方法
method_setImplementation(method, imp)
}
}
//替換類方法
class func dynamicChangeClassMethod(selector: Selector, action: (() -> Void)?) {
// 獲取類方法的IMP
let method = class_getClassMethod(self, selector)
if let method = method, self.responds(to: selector) {
// 獲取原來方法的IMP
let oldImp = method_getImplementation(method)
// 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> Void
// 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
// 實(shí)現(xiàn)新的方法
let newFuncion: @convention(block) (Any) -> Void = {
(sself) in
// 執(zhí)行原來的方法類似調(diào)用super
oldImpBlock(sself, selector)
print("dynamicChangeClassMethod")
// 你要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
let imp = imp_implementationWithBlock(newFuncion)
// 用新方法替換舊方法
method_setImplementation(method, imp)
}
}
}
People.dynamicChangeInstanceMethod(selector: #selector(People.logName), action: nil)
People.dynamicChangeClassMethod(selector: #selector(People.decInfo), action: nil)
let p = People(name: "Albert")
p.logName()
People.decInfo()
//輸出如下
Albert
dynamicChangeInstanceMethod
People
dynamicChangeClassMethod
方案1中的方法原方法還在MethodList中只是訪問不到,方案2中是替換了原來的方法實(shí)現(xiàn)(替換了IMP指針)