Latest 1.3.7
Homepage https://github.com/yulingtianxia/BlockHook
License MIT
Platforms ios 8.0, osx 10.8, requires ARC
Authors


BlockHook

Platform
CI Status
Version
Carthage compatible
codecov
Codacy Badge
GitHub release
Twitter Follow

Hook Objective-C blocks with libffi. It’s a powerful AOP tool for blocks. BlockHook can run your code before/instead/after invoking a block. BlockHook can even notify you when a block dealloc. You can trace the whole lifecycle of a block using BlockHook!

📚 Article

🌟 Features

  • [x] Easy to use. Keep your code clear.
  • [x] Support 4 hook modes: Before, Instead, After and Dead.
  • [x] Let you modify return value and arguments.
  • [x] Support invoking original implementation.
  • [x] Remove hook at any time.
  • [x] Traverse all hook tokens of block.
  • [x] Provide block mangle name.
  • [x] Self-managed tokens.
  • [x] Support custom struct.
  • [x] Support Carthage & CocoaPods.

🔮 Example

BlockHook needs libffi, which supports iOS and macOS.
You can run BlockHookSample iOS or BlockHookSample macOS target.

🐒 How to use

You can hook a block using 4 modes (before/instead/after/dead). This method returns a BHToken instance for more control. You can remove a BHToken, or set custom return value to its retValue property. Calling invokeOriginalBlock method will invoke original implementation of the block.

- (BHToken *)block_hookWithMode:(BlockHookMode)mode
                     usingBlock:(id)block

BlockHook is easy to use. Its APIs take example by Aspects. Here is a full set of usage of BlockHook.

This is an example for hooking block in all modes. You can change block return value from 8 to 15. Then remove some hook and check if it is successful. Finally we get callback when block dealloc.

NSObject *z = NSObject.new;
int(^block)(int x, int y) = ^int(int x, int y) {
    int result = x + y;
    NSLog(@"%d + %d = %d, z is a NSObject: %@", x, y, result, z);
    return result;
};

BHToken *tokenInstead = [block block_hookWithMode:BlockHookModeInstead usingBlock:^(BHInvocation *invocation, int x, int y){
    [invocation invokeOriginalBlock];
    NSLog(@"let me see original result: %d", *(int *)(invocation.retValue));
    // change the block imp and result
    *(int *)(invocation.retValue) = x * y;
    NSLog(@"hook instead: '+' -> '*'");
}];

BHToken *tokenAfter = [block block_hookWithMode:BlockHookModeAfter usingBlock:^(BHInvocation *invocation, int x, int y){
    // print args and result
    NSLog(@"hook after block! %d * %d = %d", x, y, *(int *)(invocation.retValue));
}];

BHToken *tokenBefore = [block block_hookWithMode:BlockHookModeBefore usingBlock:^(BHInvocation *invocation){
    // BHToken has to be the first arg.
    NSLog(@"hook before block! invocation:%@", invocation);
}];

__unused BHToken *tokenDead = [block block_hookWithMode:BlockHookModeDead usingBlock:^(BHInvocation *invocation){
    // BHToken is the only arg.
    NSLog(@"block dead! token:%@", invocation.token);
}];

NSLog(@"hooked block");
int ret = block(3, 5);
NSAssert(ret == 15, @"hook failed!");
NSLog(@"hooked result:%d", ret);
// remove token.
[tokenInstead remove];
NSLog(@"remove tokens, original block");
ret = block(3, 5);
NSAssert(ret == 8, @"remove hook failed!");
NSLog(@"original result:%d", ret);
//        [tokenDead remove];

Here is the log:

hooked block
hook before block! invocation:<BHInvocation: 0x600003668940>
3 + 5 = 8, z is a NSObject: <NSObject: 0x6000034245a0>
let me see original result: 8
hook instead: '+' -> '*'
hook after block! 3 * 5 = 15
hooked result:15
remove tokens, original block
hook before block! invocation:<BHInvocation: 0x60000366c7c0>
3 + 5 = 8, z is a NSObject: <NSObject: 0x6000034245a0>
hook after block! 3 * 5 = 8
original result:8
block dead! token:<BHToken: 0x600000422910>

📲 Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate BlockHook into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
target 'MyApp' do
    pod 'BlockHook'
end

You need replace "MyApp" with your project’s name.

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate BlockHook into your Xcode project using Carthage, specify it in your Cartfile:

github "yulingtianxia/BlockHook"

Run carthage update to build the framework and drag the built BlockHook.framework into your Xcode project.

Manual

After importing libffi, just add the two files BlockHook.h/m to your project.

❤️ Contributed

  • If you need help or you’d like to ask a general question, open an issue.
  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

👨🏻‍💻 Author

yulingtianxia, [email protected]

👮🏻 License

BlockHook is available under the MIT license. See the LICENSE file for more info.

Thanks to MABlockClosure and Aspects!

Latest podspec

{
    "name": "BlockHook",
    "version": "1.3.7",
    "summary": "Hook Objective-C blocks.",
    "description": "Hook Objective-C blocks with libffi. It's a powerful AOP tool for blocks. BlockHook can run your code before/instead/after invoking a block. BlockHook can even notify you when a block dealloc. You can trace the whole lifecycle of a block using BlockHook!",
    "homepage": "https://github.com/yulingtianxia/BlockHook",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "yulingtianxia": "[email protected]"
    },
    "social_media_url": "https://twitter.com/yulingtianxia",
    "source": {
        "git": "https://github.com/yulingtianxia/BlockHook.git",
        "tag": "1.3.7"
    },
    "source_files": [
        "BlockHook/*.{h,m}",
        "libffi/*.h"
    ],
    "public_header_files": "BlockHook/*.h",
    "platforms": {
        "ios": "8.0",
        "osx": "10.8"
    },
    "requires_arc": true,
    "ios": {
        "vendored_libraries": "libffi/libffi.a"
    },
    "osx": {
        "vendored_libraries": "libffi/libffi.a"
    }
}

Pin It on Pinterest

Share This