2017-10-31 7 views
0

私は、アプリケーションのライフサイクルイベントを取得する必要があるフレームワークに取り組んでいます。私は試しています通知センターフレームワークの下で失敗します。だから、私はメソッドSwizzlingを実行することを決めました。問題は、下のコードがエミュレータに期待どおりに正常に動作しています。私はデバイスを実行していたとき、それは失敗します。AppDelegateメソッドをスウィズルする

良いことは、メソッドが呼び出され、拡張され、元のメソッドを呼び出すときに、このメッセージここ

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); 
} 

私はユニバーサルフレームワークのビルドを使用している私のコードです私のサンプルプロジェクトでこのフレームワークを使用してください。親切にも、これで何が間違っているか教えてください。

答えて

1

どうか私にこのことが間違っていることを教えてください。

あなたのコードを実行することはできませんが(不完全ですが)、おそらくあなたの問題にいくつかの組み合わせがあります。

まずapplicationWillResignActiveの宣言は次のとおりです。

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

は、しかし、あなたのextendedApplicationWillResignActive機能であなたが、これは間違っている、va_listUIApplication *を変更しました。それがうまくいくかもしれない場合がありますが、それはシミュレータであなたのためにあるかもしれませんが、あなたのデバイス上で起こったように見える場合があります(シミュレータはx86コードを実行し、デバイスのARMコード)。

ここで明白な質問は、引数のタイプを変更した理由です。

第2に、(誤ってタイプされた)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

+0

おかげCRD、私は私の新しいメソッドの実装を変更したし、それが動作します。 - (void)quixxiApplicationWillResignActive:(UIApplication *)アプリケーション { (void(*)(id、SEL、UIApplication *))originalImplementation)(self、_cmd、application); } – Jegan

関連する問題