2016-04-19 2 views
1

После динамического создания метода в AppDelegate с использованием class_addMethod из другого класса он никогда не вызывается, когда ему это нужно.динамически созданный метод, не вызываемый

Метода Я создаю во время выполнения является:

-(BOOL)application:(UIApplication *)application 
      openURL:(NSURL *)url { 
    //Do something... 
    return YES; 
} 

кодом я использовал для создания предыдущего метода динамически является:

{ 
     UIApplication* app = [[UIApplication sharedApplication] delegate]; 
     class_addMethod([app class], @selector(application:openURL:), (IMP)myMethodIMP, @"[email protected]:"); 
    } 

Наряду с моей IMP функции:

bool myMethodIMP(id self, SEL _cmd, UIApplication *application, NSURL *url) 
    { 
     [self application: application openURL:url]; 
     //Do something... 
     return YES; 
    } 

Я проверил, что он действительно был создан на моем делете приложения, используя class_getInstanceMethod метод

Я что-то упустил?

+4

Может быть, что 'UIApplication' кэширует, что делегат не реализует упомянутый метод, когда делегат впервые установлен и, таким образом, никогда не проверяет реализацию снова. В общем, динамически генерирующие методы в этом шаблоне изобилуют такой хрупкостью, потому что это действительно не стандартная модель использования. – bbum

+0

@bbum Это имеет большой смысл, есть ли обходное решение, чтобы исправить это и динамически генерировать методы? –

+0

Лучше всего предоставить пустую реализацию указанного метода (просто «вернуть ДА;») в делегате приложения. Таким образом, если UIApplication делает «отвечает» на кеширование, он пойдет правильно. Затем, при необходимости, подкиньте. Предостережение состоит в том, что некоторые из методов - даже этот метод - имеют возвращаемые значения, и, таким образом, возвращаемое значение по умолчанию может оказаться невозможным в фиктивной реализации. Возможно, вам придется заменить/переопределить/swizzle перед первым вызовом. – bbum

ответ

1

@bbum вы правы, UIApplication кэш, что делегат dones не отвечает на этот метод, так что вы можете сделать, как этот

@interface UIApplication (MBShare) 

@end 

@implementation UIApplication (MBShare) 

+ (void) load 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 

     SEL originalSelector = @selector(setDelegate:); 
     SEL swizzledSelector = @selector(mb_setDelegate:); 

     Method originalMethod = class_getInstanceMethod(self, originalSelector); 
     Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector); 

     BOOL didAddMethod = 
     class_addMethod(self, 
         originalSelector, 
         method_getImplementation(swizzledMethod), 
         method_getTypeEncoding(swizzledMethod)); 

     if (didAddMethod) { 
      class_replaceMethod(self, 
           swizzledSelector, 
           method_getImplementation(originalMethod), 
           method_getTypeEncoding(originalMethod)); 
     } else { 
      method_exchangeImplementations(originalMethod, swizzledMethod); 
     } 

    }); 
} 

- (void) mb_setDelegate: (id) delegate 
{ 
    //hook method here 
    [[MBShareApi shareInstance] hookAppDelegate: delegate]; 
    [self mb_setDelegate: delegate]; 
} 

@end 

более подробно см: https://github.com/Baoge2012/MBShare/blob/master/MBShare/MBShareApi.m

Смежные вопросы