abjective-c对象我的意思是类似myviewController和类对象myviewController.superclass。
例如,在此功能中,如何使用targetClass
施放self
?
// 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
,并且运行盘旋的实现。在ViewController.viewDidAppear
滚动实现运行后,将调用原始实现。好。
当ViewController.viewDidAppear
原始实现运行时,UIViewController.viewDidAppear
由Super.ViewDidAppear((调用。UIViewController.viewDidAppear
的Swizzled实现被称为并运行,并且在该swizzled实施中self
用于调用原始实现,但由于self
是ViewController,而不是运行时的UiviewController,因此ViewController.viewDidAppear
SWIZZLED实现再次调用,因此再次调用递归循环。
换句话说,递归循环开始时,当一个被散布的孩子的方法称为其超级方法时,它也被散布了。在Swizzled方法中,self
用于调用原始实现,并且由于运行时的self
是最多的子类(在此示例viewController中(,Super的Swizzled方法再次调用孩子的原始方法,因此周期重复。
目标:
找到一种调用Swizzled Class的原始实现的方法。
当运行时的self
可能是某个孩子,而父母和孩子都在孩子方法调用父方法的位置都有其方法时,必须有一种方法可以明确选择使用运行时函数运行的类实现的方法class_getInstanceMethod
尝试并失败:
将self
铸造为另一类,因为我找不到如何使用类对象铸造的方法。要在更通用的情况下使用此散布代码,必须使用存储原始类的类对象,而不是明确编写类型。
viewController.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 swizzling.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);
^也是直接调用超级的另一种选择,但是它并不是太安全了,因为您真的需要确定目标类是超级类 - 我需要问,您为什么要这样做?这个问题可能有更简单的解决方案。
将其作为铸件的措辞与读者以及您自己可能令人困惑。类型铸造是纯粹的静态,编译的东西。targetClass
是一个变量,当然是动态的运行时间。在运行时,消息接收器表达式的静态类型与代码的行为无关。当时,这些信息或多或少消失了。[self someMethod...]
和[(AnyType*)self someMethod...]
都将被编译为完全相同的代码。
您只是在寻找:
[targetClass instanceMethodForSelector:selector]
您目前拥有的位置:
[((targetClass *) self) methodForSelector:selector]
?