你能用 Class object 投射 Objective-C object 吗?
Can you cast an Objective-C object using a Class object?
Objective-C object 我的意思是像 MyViewController 和 class object 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
并调用混合实现 运行s。在 ViewController.viewDidAppear
swizzled 实现 运行s 之后,调用原始实现。好
当ViewController.viewDidAppear
原始实现运行s时,UIViewController.viewDidAppear
被super.viewDidAppear()调用。 UIViewController.viewDidAppear
的调配实现被调用 运行,并且在该调配实现中 self
用于调用原始实现但是因为 self
是 ViewController 而不是UIViewController 在 运行 时,ViewController.viewDidAppear
再次调用 swizzled 实现,从而开始递归循环。
换句话说,递归循环在 child 的方法(已被调配)调用它的超方法(也已被调配)时开始。在swizzled方法中使用self
调用原实现,由于self
在运行时最childclass(本例ViewController) super 的 swizzled 方法再次调用 child 的原始方法,如此循环重复。
目标:
找到一种方法调用 swizzled class 的原始实现。
当 self
在 运行 时可能是一些 child,并且 parent 和 child 都有他们的方法在 child 方法调用 parent 方法,必须有一种方法可以通过使用 运行time 函数 class_getInstanceMethod
尝试失败:
将 self
转换为另一个 class 因为我不知道如何使用 Class object 进行转换。要在更通用的情况下使用此混合代码,必须使用存储原始 class 的 Class object 而不是显式写入 class 类型。
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];
并忽略警告
这个答案有详细的解说:
编辑:
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 的另一种选择,但它不太安全,因为你真的需要确定目标 class 是 superclass - 我需要问,你为什么做这个?可能有更简单的解决方案。
对于 reader 和您自己来说,将其表述为演员只是令人困惑。类型转换是纯静态的,compile-time。 targetClass
,作为一个变量,当然是动态的,run-time。在 run-time,消息接收者表达式的静态类型与代码的行为无关。该信息或多或少在这一点上消失了。 [self someMethod...]
和 [(AnyType*)self someMethod...]
都将被编译为完全相同的代码。
您是否正在寻找:
[targetClass instanceMethodForSelector:selector]
您目前所在的位置:
[((targetClass *) self) methodForSelector:selector]
?
Objective-C object 我的意思是像 MyViewController 和 class object 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
并调用混合实现 运行s。在 ViewController.viewDidAppear
swizzled 实现 运行s 之后,调用原始实现。好
当ViewController.viewDidAppear
原始实现运行s时,UIViewController.viewDidAppear
被super.viewDidAppear()调用。 UIViewController.viewDidAppear
的调配实现被调用 运行,并且在该调配实现中 self
用于调用原始实现但是因为 self
是 ViewController 而不是UIViewController 在 运行 时,ViewController.viewDidAppear
再次调用 swizzled 实现,从而开始递归循环。
换句话说,递归循环在 child 的方法(已被调配)调用它的超方法(也已被调配)时开始。在swizzled方法中使用self
调用原实现,由于self
在运行时最childclass(本例ViewController) super 的 swizzled 方法再次调用 child 的原始方法,如此循环重复。
目标:
找到一种方法调用 swizzled class 的原始实现。
当 self
在 运行 时可能是一些 child,并且 parent 和 child 都有他们的方法在 child 方法调用 parent 方法,必须有一种方法可以通过使用 运行time 函数 class_getInstanceMethod
尝试失败:
将 self
转换为另一个 class 因为我不知道如何使用 Class object 进行转换。要在更通用的情况下使用此混合代码,必须使用存储原始 class 的 Class object 而不是显式写入 class 类型。
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];
并忽略警告
这个答案有详细的解说:
编辑:
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 的另一种选择,但它不太安全,因为你真的需要确定目标 class 是 superclass - 我需要问,你为什么做这个?可能有更简单的解决方案。
对于 reader 和您自己来说,将其表述为演员只是令人困惑。类型转换是纯静态的,compile-time。 targetClass
,作为一个变量,当然是动态的,run-time。在 run-time,消息接收者表达式的静态类型与代码的行为无关。该信息或多或少在这一点上消失了。 [self someMethod...]
和 [(AnyType*)self someMethod...]
都将被编译为完全相同的代码。
您是否正在寻找:
[targetClass instanceMethodForSelector:selector]
您目前所在的位置:
[((targetClass *) self) methodForSelector:selector]
?