/SDMagicHook

A safe and influence-restricted method hooking for both Objective-C and Swift.

Primary LanguageObjective-CMIT LicenseMIT

SDMagicHook

中文文档 原理解析1 原理解析2 与Aspects异同

A safe and influence-restricted method hooking for both Objective-C and Swift.

Improvement

Classical method swizzling with method_exchangeImplementations is quite simple, but it has a lot of limitations and defects:

  • You have to add a new method in a class category everytime when you want to swizzle a method.
  • Different method implementation with same selector in different category will case method conflicts.
  • The method swizzling will affect all the instances of the target class,however in most cases it is not necessary but even has side effects.

Now SDMagicHook will solve the problems mentioned above.

Changes Log

2020.08.02 -- fix bug about KVO compatibility, release "1.2.5"

2020.05.01 -- release KVO-compatible version "1.2.4"

2020.03.09 -- KVO compatible

Usage

Example for hooking CALayer's setBackgroundColor: method to find the one who secretly changed the background color of the view:

[self.view.layer hookMethod:@selector(setBackgroundColor:) impBlock:^(CALayer *layer, CGColorRef color){
    [layer callOriginalMethodInBlock:^{
        [layer setBackgroundColor:color];
    }];
    NSLog(@"%@", [NSString stringWithFormat:@"AHA! Catch it! Here are the clues.\n\n%@", [NSThread callStackSymbols]]);
}];

Example for hooking UIButton's pointInside:withEvent: method to expand the hot area:

- (void)expand:(Boolean)yn {

    if (yn) {
        __weak typeof(self) weakSelf = self;
        _hookID = [_button hookMethod:@selector(pointInside:withEvent:) impBlock:^(UIView *v, CGPoint p, UIEvent *e){
            __block BOOL res = false;
            [v callOriginalMethodInBlock:^{
                res = [v pointInside:p withEvent:e];
            }];
            if (res) return YES;

            return [weakSelf pointCheck:p view:v];
        }];
    } else {
        [_button removeHook:@selector(pointInside:withEvent:) strId:_hookID];
    }
}

Example for hooking class method:

- (void)hookClassMethod {
    [[DemoVC3 class] hookMethod:@selector(testClassMethod) key:&testClassMethodTag impBlock:^(id cls){
        [cls callOriginalMethodInBlock:^{
            [[DemoVC3 class] testClassMethod];
        }];
        printf(">> hooked testClassMethod");
    }];
}

+ (void)testClassMethod {
    printf(">> %s", sel_getName(_cmd));
}

- (void)dealloc {
    [[DemoVC3 class] removeHook:@selector(testClassMethod) key:&testClassMethodTag];
}

Example for observe any instance's dealloc event.

[[NSObject new] addObjectDeallocCallbackBlock:^{
    printf("Will dealloc...");
}];

Example for hooking UIViewController's viewDidDisappear: method with Swift:

override func viewDidLoad() {
    super.viewDidLoad()
    let imp: @convention(block) (UIViewController, Bool) -> Void = { (vc, flag) in
        vc.callOriginalMethod {
            vc.viewDidDisappear(flag)
        }
        print(vc)
    }
    rootVC = navigationController?.children.first
    hookID = rootVC?.hookMethod(#selector(UIViewController.viewDidDisappear(_:)), impBlock: imp)
}

License

MIT License.