Swizzle AppDelegate方法

时间:2017-10-31 06:56:44

标签: ios objective-c runtime

我正在使用Framework,需要获取应用程序生命周期事件。我尝试使用 NotificationCenter ,它在框架下失败了。所以我决定使用obj c运行时实现 Method Swizzling 。问题是以下代码在模拟器中正常工作。当我运行设备时,它会失败。

好的方法是调用Extended方法,当调用原始方法时,这会因消息

而失败
Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)

这是我的代码

IMP originalImplementation;

+ (instancetype)initWith:(NSString *)bundleIdentifier{
    Demo *instance = [[Demo alloc] init];
    [instance swizzlingLifeCycleMethods];
    return instance;
}

- (void)swizzlingLifeCycleMethods{

    //Prepare the injected class name to be injecting
    Class originalClass = NSClassFromString(@"AppDelegate");

    //Prepare the methods to swizzling
    SEL originalWillResignAction = @selector(applicationWillResignActive:);
    SEL extendedWillResignActive = NSSelectorFromString(@"extendedApplicationWillResignActive");

    //Get original method and method encoding
    originalResignMethod = class_getInstanceMethod(originalClass, originalWillResignAction);
    originalImplementation = method_getImplementation(originalResignMethod);
    const char *originalResignMethodEncoding = method_getTypeEncoding(originalResignMethod);

    //Add swizzling method into targetted class
    class_addMethod(originalClass, extendedWillResignActive, (IMP)extendedApplicationWillResignActive, originalResignMethodEncoding);

    //Swizzling the methods
    Method extendedResignMethod = class_getInstanceMethod(originalClass, extendedWillResignActive);
    method_exchangeImplementations(originalResignMethod, extendedResignMethod);
}

//Called at the time of user enters into background
void extendedApplicationWillResignActive(id self, SEL _cmd, va_list args1)
{
    //Implement our logic here

    //Call the original function after our stuff done
    ((void(*)(id, SEL, ...))originalImplementation)(self, _cmd, args1);
}

我在我的示例项目中使用Universal Framework构建来使用此框架。请告诉我这是错误的。

1 个答案:

答案 0 :(得分:1)

  

请告诉我这是错误的。

我无法运行您的代码(它不完整),但以下内容可能会对您的问题有所帮助。

首先,applicationWillResignActive的声明是:

- (void)applicationWillResignActive:(UIApplication *)application;

但是,在extendedApplicationWillResignActive功能中,您已将UIApplication *更改为va_list,这是错误的。有些情况下它可能会起作用,它似乎可能在你的模拟器中,但有时会爆炸,就像你的设备上发生的那样(注意这里模拟器运行x86代码,设备ARM代码。)

这里显而易见的问题是你为什么要改变论证的类型?

其次,您绕过编写方法并直接使用(错误键入的)C函数进行调整。虽然没有错误本身,但它可能无法帮助你。

第三,您正在尝试添加方法:

- (void)extendedApplicationWillResignActive:

到一个类,没有检查这样的方法是否已经存在。这是不明智的,你的框架如何知道使用它的应用程序没有定义这样的方法?可能性可能很小,但大于零,并且是不必要的风险。那说这可能不是你的问题(因为你写的是你正在调整的课程,所以知道没有这样的现有方法)。

最后,您已经将您正在调配的班级名称AppDelegate硬连线。应用程序委托可以使用不同的名称。您的initWith:方法会使用当前未使用的参数bundleIdentifier,因此我认为这表明您打算解决此问题。

让我们看看我们是否可以帮助您解决这个问题。你在这里尝试做的是跨类调配。为此,您可以:

  • 在整个目标方法/实施功能中使用正确的类型;
  • 写下您的替代品作为一种方法;和
  • 只需替换目标方法的实现,而无需向类添加新方法

以下是包含这些更改的代码。 此代码只是在线复制和编辑,尚未运行,预计错误,仅作为大纲。

@implementation Demo
{

// define a typedef for the target method's implementation function
typedef void (*OriginalImpType)(id self, SEL selector, UIApplication *application);
// declare this static as it is private to this class
static OriginalImpType originalImplementation;

+ (instancetype)initWith:(NSString *)bundleIdentifier{
    Demo *instance = [[Demo alloc] init];
    [instance swizzlingLifeCycleMethods:bundleIdentifier];
    return instance;
}

- (void)swizzlingLifeCycleMethods:(NSString *)bundleIdentifier
{

    // Get the class for the application delegate
    Class originalClass = ... however you plan to do this using bundleIdentifier ...;
    // Get the swizzling class
    Class demoClass = [Demo class];

    // Prepare the methods to swizzling
    SEL originalWillResignAction = @selector(applicationWillResignActive:);
    SEL extendedWillResignActive = @selector(extendedApplicationWillResignActive:);

    // Get original method and save it
    Method originalResignMethod = class_getInstanceMethod(originalClass, originalWillResignAction);
    originalImplementation = (OriginalImpType)method_getImplementation(originalResignMethod);

    // Swizzling the method
    Method extendedResignMethod = class_getInstanceMethod(demoClass, extendedWillResignActive);
    method_setImplementation(originalResignMethod, method_getImplementation(extendedResignMethod));
}

// Called at the time of user enters into background
- (void) extendedApplicationWillResignActive:(UIApplication *)application
{
    //Implement our logic here

    //Call the original function after our stuff done
    originalImplementation(self, _cmd, application);
}

} // end of @implementation Demo

HTH