您可以使用类对象施放Objective-C对象吗?



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]

相关内容

  • 没有找到相关文章

最新更新