我所说的objective-c对象是指类似myviewcontroller和一个类对象myviewcontroller.superclass。
例如,在这个函数中,如何使用self
来转换targetClass
?
// This code doesn't compile
- (void) useOtherClassImplementation :(Class) targetClass :(SEL) targetSelector {
if ([self isKindOfClass: targetClass]) {
((void (*)(id, SEL))[((targetClass *) self) methodForSelector:selector])(self, selector);
}
}
有没有办法像
((targetClass *) self)
这样不编译的东西?案例研究
概述:
当viewcontroller出现时,将调用
ViewController.viewDidAppear
,并运行swizzled实现。swizzle实现运行后,将调用原始实现。很好。当
ViewController.viewDidAppear
原始实现运行时,ViewController.viewDidAppear
由super.viewDidAppear()调用。调用并运行UIViewController.viewDidAppear
的swizzled实现,在该swizzled实现中UIViewController.viewDidAppear
用于调用原始实现,但由于self
在运行时是viewcontroller而不是uiviewcontroller,因此再次调用self
swizzled实现,从而开始递归循环。换句话说,递归循环在子方法(已被swizzle)调用其super方法(也已被swizzle)时开始。在swizzled方法中,使用
ViewController.viewDidAppear
调用原始实现,并且由于运行时的self
是大多数子类(在本例中为viewcontroller),因此super的swizzled方法再次调用子类的原始方法,因此循环重复。目标:
找到调用swizzled类的原始实现的方法。
当运行时
self
可以是某个子类,并且父类和子类的方法都在子方法调用父方法的地方被swizzle时,必须有一种方法通过使用运行时函数self
显式地选择要运行哪个类的实现。尝试但失败:
将
class_getInstanceMethod
转换为另一个类,因为我无法找到如何使用类对象进行转换。要在更一般的情况下使用这种快速变化的代码,必须使用存储原始类的类对象,而不是显式地编写类类型。视图控制器.swift
// Child class ViewController inherits from parent class UIViewController
class ViewController: UIViewController {
override func viewDidLoad() {
_ = ViewController.swizzleViewDidAppearParentAndChild
}
override func viewDidAppear(_ animated: Bool) {
// NOTICE the call to parent's function
super.viewDidAppear(animated)
// never reaches here
print("In viewcontroller viewdidappear")
}
// swizzles in the block for both UIViewController and ViewController
// recursively prints
// TestApp.ViewController is about to perform viewDidAppear:
//
static var swizzleViewDidAppearParentAndChild: Void = {
SwizzledObject.createTrampoline(for: UIViewController.self, selector: #selector(UIViewController.viewDidAppear(_:)), with: printBeforePerforming)
SwizzledObject.createTrampoline(for: ViewController.self, selector: #selector(ViewController.viewDidAppear(_:)), with: printBeforePerforming)
}()
// a block to be used before a method call
static var printBeforePerforming: SelectorTrampolineBlock {
return { target, selector in
print("\(NSStringFromClass(type(of: target as AnyObject))) is about to perform \(NSStringFromSelector(selector!))")
}
}
}
nsobject+swizzling.h
#import <Foundation/Foundation.h>
@interface SwizzledObject : NSObject
typedef void (^ SelectorTrampolineBlock)(id target, SEL targetSelector);
+ (SEL) createTrampolineForClass:(Class)targetClass selector:(SEL)targetSelector withBlock:(SelectorTrampolineBlock) block;
@end
nsobject+旋转.m
#import "NSObject+Swizzling.h"
#import <objc/runtime.h>
@implementation SwizzledObject
// creates a method at runtime that calls the trampolineBlock, and then performs original method
+ (SEL) createTrampolineForClass:(Class)targetClass selector:(SEL)targetSelector withBlock:(SelectorTrampolineBlock) block {
SEL trampolineSelector = NSSelectorFromString([NSString stringWithFormat:@"performBefore__%@", NSStringFromSelector(targetSelector)]);
Method originalMethod = class_getInstanceMethod(targetClass, targetSelector);
if (originalMethod == nil) {
return nil;
}
IMP dynamicImp = imp_implementationWithBlock(^(id self, bool param) {
block(self, targetSelector);
if (!self || ![self respondsToSelector:trampolineSelector]) {return;}
((void (*)(id, SEL, bool))[self methodForSelector:trampolineSelector])(self, trampolineSelector, param);
});
class_addMethod(targetClass, trampolineSelector, dynamicImp, method_getTypeEncoding(originalMethod));
Method newMethod = class_getInstanceMethod(targetClass, targetSelector);
if (newMethod == nil) {
return nil;
}
[SwizzledObject injectSelector:targetClass :trampolineSelector :targetClass :targetSelector];
return trampolineSelector;
}
// Switches/swizzles method
+ (BOOL) injectSelector:(Class) swizzledClass :(SEL) swizzledSelector :(Class) originalClass :(SEL) orignalSelector {
NSLog(@"Injecting selector %@ for class %@ with %@", NSStringFromSelector(orignalSelector), NSStringFromClass(originalClass), NSStringFromSelector(swizzledSelector));
Method newMeth = class_getInstanceMethod(swizzledClass, swizzledSelector);
IMP imp = method_getImplementation(newMeth);
const char* methodTypeEncoding = method_getTypeEncoding(newMeth);
BOOL existing = class_getInstanceMethod(originalClass, orignalSelector) != NULL;
if (existing) {
class_addMethod(originalClass, swizzledSelector, imp, methodTypeEncoding);
newMeth = class_getInstanceMethod(originalClass, swizzledSelector);
Method orgMeth = class_getInstanceMethod(originalClass, orignalSelector);
method_exchangeImplementations(orgMeth, newMeth);
}
else {
class_addMethod(originalClass, orignalSelector, imp, methodTypeEncoding);
}
return existing;
}
@end
输出
2018-04-04 17:50:43.201458-0700 TestApp[26612:6527489] Injecting selector viewDidAppear: for class UIViewController with performBefore__viewDidAppear:
2018-04-04 17:50:43.202641-0700 TestApp[26612:6527489] Injecting selector viewDidAppear: for class TestApp.ViewController with performBefore__viewDidAppear:
TestApp.ViewController is about to perform viewDidAppear:
TestApp.ViewController is about to perform viewDidAppear:
TestApp.ViewController is about to perform viewDidAppear:
(infinitely prints previous line)
最佳答案
下面是一个示例,说明如何执行此操作:
- (void)useSuperclassImplementation:(Class)targetClass targetSelector:(SEL)targetSelector {
if ([self isKindOfClass: targetClass] && [targetClass respondsToSelector:targetSelector]) {
((void (*)(id, SEL))[targetClass methodForSelector:targetSelector])(self, targetSelector);
}
}
您可以使用
[targetClass performSelector:targetSelector];
忽略警告关于这个答案有一个详细的解释:https://stackoverflow.com/a/20058585/1755720
编辑:
struct objc_super superInfo = {
.receiver = [self class],
.super_class = targetClass
};
id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper;
(*objc_superAllocTyped)(&superInfo, targetSelector);
^也是另一个直接调用super的选项,但它并不太安全,因为您需要确定目标类是super类-我需要问,为什么要这样做?这个问题也许有一个更简单的解决办法。
关于objective-c - 您可以使用Class对象强制转换Objective-C对象吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49661094/