方案1.通過動態(tài)創(chuàng)建類和實現(xiàn)其中的方法浸遗,然后把原對象的類型修改成你創(chuàng)建的對象通過消息派發(fā)使對象調(diào)用你新實現(xiàn)的方法
extension NSObject {
//動態(tài)創(chuàng)建類并重寫方法
func dynamicCreatClass(selector: Selector, action: (() -> ())?) {
// 創(chuàng)建類的類名
let classFullName = "ATDynamic_\(self.classForCoder.description()))"
// 獲取原來類
let originalClass = type(of: self)
// 判斷這個類是否已經(jīng)存在
if let dynamicClass = objc_allocateClassPair(originalClass, classFullName, 0) {
// 動態(tài)的創(chuàng)建這個類
objc_registerClassPair(dynamicClass)
// 將原對象修改成新的類型
object_setClass(self, dynamicClass)
// 實現(xiàn)新的方法
let printName: @convention(block) (Any?) -> () = { nullSelf in
guard let _ = nullSelf else { return }
// 獲取原來類中方法的Imp
let originalImp = class_getMethodImplementation(originalClass, selector)
// 定義一個方法類型與msgSend的參數(shù)類似 第一個參數(shù)是對象耿导,第二個參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> ()
// 將imp強轉(zhuǎn)為兼容c的函數(shù)指針
let originalPrintName = unsafeBitCast(originalImp, to: IMPCType.self)
// 執(zhí)行原方法 類似super.originFuncion()
originalPrintName(self, selector)
print("Dynamic")
// 你想要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個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) : 表明這個是一個swift的閉包
@convention(block) :表明這個是一個兼容oc的block的閉包
@convention(c) : 表明這個是兼容c的函數(shù)指針的閉包逛犹。
定義一個People類:
class People: NSObject {
var name: String
override init() {
self.name = ""
super.init()
}
init(name: String) {
self.name = name
}
//需要添加dynamic不然是靜態(tài)派發(fā)會走原方法(或者用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:直接替換當前類中的方法
extension NSObject {
//替換實例方法
class func dynamicChangeInstanceMethod(selector: Selector, action: (() -> Void)?) {
// 獲取實例方法的IMP
let method = class_getInstanceMethod(self, selector)
if let method = method, self.init().responds(to: selector) {
// 獲取原來方法的IMP
let oldImp = method_getImplementation(method)
// 定義一個方法類型與msgSend的參數(shù)類似 第一個參數(shù)是對象,第二個參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> Void
// 將imp強轉(zhuǎn)為兼容c的函數(shù)指針
let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
// 實現(xiàn)新的方法
let newFuncion: @convention(block) (Any?) -> Void = {
(sself) in
// 執(zhí)行原來的方法類似調(diào)用super
oldImpBlock(sself, selector)
print("dynamicChangeInstanceMethod")
// 你要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個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)
// 定義一個方法類型與msgSend的參數(shù)類似 第一個參數(shù)是對象虽画,第二個參數(shù)是SEL
typealias IMPCType = @convention(c) (Any, Selector) -> Void
// 將imp強轉(zhuǎn)為兼容c的函數(shù)指針
let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
// 實現(xiàn)新的方法
let newFuncion: @convention(block) (Any) -> Void = {
(sself) in
// 執(zhí)行原來的方法類似調(diào)用super
oldImpBlock(sself, selector)
print("dynamicChangeClassMethod")
// 你要做的事
action?()
}
// imp_implementationWithBlock的參數(shù)需要的是一個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中是替換了原來的方法實現(xiàn)(替換了IMP指針)