我所说的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,因此再次调用selfswizzled实现,从而开始递归循环。
换句话说,递归循环在子方法(已被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/

10-14 20:00
查看更多