hook的使用

OC runtime 提供了動態替換Method 實現的能力,加上category的使用,能夠實現一種叫 swizzle的技術,從而給問題定位提供了一種方法。app

舉個例子,NSNotificationCenter 註冊的observer 是經過assign 方法保留observer引用(而不是weak 或 retain、strong),這樣的話若是某個observer只是註冊而沒有註銷,那麼這個observer釋放後,NSNotificationCenter 發送通知時將找不到這個observer,由於這個observer已經成了野指針,從而發生crash。spa

那麼究竟是那個observer 只註冊而沒有註銷呢? 能夠經過給 NSNotificationCenter 增長一個category ,在category中經過swizzle 給原來的註冊與註銷方法添加log 來查看。指針


category的頭文件:code

增長了swizzle 類方法,在app 啓動時調用server

/*
* NSNotificationCenter+TestHook.h
*/

#import <Foundation/Foundation.h>

@interface NSNotificationCenter (TestHook)

+ (void) swizzle;
@end

實現,增長兩個hook 方法,分別給註冊和註銷方法添加log:遞歸

注意hook方法不能在頭文件暴露以防被直接調用,由於在使用method_exchangeImplimentaions以前,直接調用會發生遞歸。rem

#import "NSNotificationCenter+TestHook.h"
#import <objc/runtime.h>


@implementation NSNotificationCenter (TestHook)

-(void)hook_addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject{
    NSLog(@"====== add observer for:%@, selector: %s, class %s", aName, sel_getName(aSelector), class_getName([observer class]));
    [self hook_addObserver:observer selector:aSelector name:aName object:anObject];
}

- (void)hook_removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject{
        NSLog(@"====== remove observer for:%@,  class %s", aName, class_getName([observer class]));
    [self hook_removeObserver:observer name:aName object:anObject];
    
}

+(void)swizzle{
    Method ori_Method =  class_getInstanceMethod([NSNotificationCenter class], @selector(addObserver:selector:name:object:));
    Method my_Method = class_getInstanceMethod([NSNotificationCenter class], @selector(hook_addObserver:selector:name:object:));
    
    method_exchangeImplementations(ori_Method, my_Method);
    
    Method ori_remove = class_getInstanceMethod([NSNotificationCenter class], @selector(removeObserver:name:object:));
    Method my_remove = class_getInstanceMethod([NSNotificationCenter class], @selector(hook_removeObserver:name:object:));
    method_exchangeImplementations(ori_remove, my_remove);
    
}

@end

而後在應用AppDelegeate中調用 :get

....
#import "NSNotificationCenter+TestHook.h"
...

@implementation MyAppDelegate

...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

...

 [NSNotificationCenter swizzle];

...

}

...

@end


這樣就能清楚的看到哪些observer註冊了哪些通知,以及在何時註銷了通知。it

相關文章
相關標籤/搜索