GithubHelp home page GithubHelp logo

steipete / aspects Goto Github PK

View Code? Open in Web Editor NEW
8.4K 275.0 1.3K 504 KB

Delightful, simple library for aspect oriented programming in Objective-C and Swift.

Home Page: https://twitter.com/steipete

License: MIT License

Objective-C 99.20% Ruby 0.80%
objectivec aspects objective-c hooks swift

aspects's People

Contributors

ashfurrow avatar commanda avatar fpillet avatar gamenerds avatar jasperblues avatar jeffreybergier avatar mosheberman avatar nickynick avatar orta avatar ryanworl avatar steipete avatar streeter avatar toco avatar wtachau avatar xeodou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aspects's Issues

Not able to perform aspect_hookSelector

I want to hook to UIControl addTarget:action:forControlEvents but not able to

[UIControl aspect_hookSelector:@selector(addTarget:action:forControlEvents:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
                // the instance getting the callback
                NSLog(@"here");

            } error:&err];

            if (err) {
                NSLog(@"Err %@",err);
            }

But getting this error:

(lldb) po err
Error Domain=AspectErrorDomain Code=6 "Block signature <NSMethodSignature: 0x7fad2dc107e0> doesn't match <NSMethodSignature: 0x7fad2bdf4380>." UserInfo=0x7fad2bdf43c0 {NSLocalizedDescription=Block signature <NSMethodSignature: 0x7fad2dc107e0> doesn't match <NSMethodSignature: 0x7fad2bdf4380>.}

Written methods and function blocks not called

"aspect_hook" blocks are not being called for me for methods (or dynamic swift functions). If I use a comparable implementation such as this, it works for public methods as expected.

It seems the issue lies partly in the aspect_isCompatibleBlockSignature function... if the method being intercepted is a dynamic swift function, there is a mismatch around line 210 in .m:

if (signaturesMatch) {
For loop, the condition for signaturesMatch = NO is not compatible with a dynamic Swift function.
}

commenting out this loop allows a dynamic Swift function to work correctly.

If it is an objective-C method, the mismatch occurs due to the blockSignature's numberOfArguments being larger than the methodSignature.

I am still trying to figure out what all this means, and if I do I will attempt a solution. Otherwise some help would be great because I really want to use it.

why can't hook viewDidLoad (UIViewController) ??

Hi Steipete, why can't hook viewDidLoad (UIViewController) ??
Error:
Aspects: Blog signature <NSMethodSignature: 0x7fbc8bd35740> doesn't match <NSMethodSignature: 0x7fbc8bf0cc40>.

Please help~

Thank u.

aspect_hookSelector in Swift

@steipete I was trying to implement this into one of my swift projects and it isn't allowing me to hook the selector because of the way the block is being recognized by swift. Since the usingBlock:(id)block is defined as an id swift is converting to an AnyObject! which cannot be converted into a closure.
Example code:

let hookBlock:(aspectInfo:AspectInfo, animated:Bool) -> Void = {(aspectInfo, animated) in
  NSLog("%@", aspectInfo.description)
}

UIViewController.aspect_hookSelector(Selector("viewWillAppear:"), withOptions: AspectOptions.PositionAfter, usingBlock: hookBlock as AnyObject!, error: nil)

I always get the following error:

'(aspectInfo: AspectInfo, animated: Bool) -> Void' is not convertible to 'AnyObject!'

I've also tried not casting it as an AnyObject! and that doesn't work either. Let me know if there is a workaround or if I'm just doing something incorrectly. Thanks!

will it support `dealloc`?

sometimes it's convenient to monitor ViewController's dealloc, i.e. popped from stack, but dealloc is not triggered, so we know there is a memory leak. tried it like this with no luck.

[MyViewController aspect_hookSelector:NSSelectorFromString(@"dealloc") atPosition:AspectPositionBefore withBlock:^(id object, NSArray *arguments) {
    NSLog(@"dealloc:%@", object);
}];

Incompatibility with New Relic

Currently this is incompatible with New Relic. We informed their support and I'd love to help, but since their framework is closed source and "protected" against disassembling, I don't see how I can help :/

We have to wait and hope that they are looking into it.

screen shot 2014-05-16 at 10 13 43

Trouble hooking a class method

I'm am trying to swizzle a class method which ordinarily returns a singleton instance of a AFHTTPSessionManager subclass. My class is as follows:

@interface WebServiceClient : AFHTTPSessionManager

+ (instancetype)sharedClient;

@implementation WebServiceClient

+ (instancetype)sharedClient {
    static WebServiceClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = ...; // setup here
    });

    return _sharedManager;
}

@end

I am swizzling like this:

WebServiceClient *webServiceClient = ...; // setup here
NSError *error;
[WebServiceClient aspect_hookSelector:@selector(sharedClient)
                          withOptions:AspectPositionInstead
                           usingBlock:^(id<AspectInfo> info) {
                               NSInvocation *invocation = info.originalInvocation;
                               [invocation setReturnValue:&webServiceClient];
                          } error:&error];

The error gives the following description:

Aspects: Blog signature <NSMethodSignature: 0x8e2bec0> doesn't match (null).

Any ideas what I am doing wrong? Thanks for the great library, I think I am going to be using it a lot in my unit tests to simplify dependency injection.

Typed & named arguments instead of an arguments array?

Hey there! As someone who enjoys hacking runtime a lot too, I couldn't pass by this wonderful thing :)

One potential improvement came to my mind though. If a dependency on libffi is fine with you, there's a way to pass arguments to aspect block properly, something like this:

[self aspect_hookSelector:@selector(viewWillDisappear:)
              withOptions:AspectPositionAfter
               usingBlock:^(NSInvocation *invocation, BOOL animated) { ... } 
                    error:NULL];

Here's a good example of how to do it.

IMO, this would be cleaner than having to deal with an array of stuff, especially for primitive arguments. If you like this idea, I can try to do a pull request :)

Hooking to the class methods

Hi,

I am wondering - what could be the reason I am not able to hook at
+[UIFont fontWithDescriptor:size:] ?

The error says that block signature does not match:

Error Domain=AspectErrorDomain Code=6 "Block signature <NSMethodSignature: 0x17047eb40> doesn't match (null)." UserInfo=0x178275140 {NSLocalizedDescription=Block signature <NSMethodSignature: 0x17047eb40> doesn't match (null).}

But the actual problem is that

NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];

inside

static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error)

returns nil.
object here is UIFont.

Shouldn't be the - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector used for class?

Any thoughts?

Regards,

-[UIViewControllerWrapperView aspects__touchesBegan:withEvent:]: unrecognized selector sent to instance

Reproduction:

  1. Start a test project.
  2. Create a view controller with [UIViewController new]
  3. Hook into its touchesBegan:withEvent: method; do something (e.g. NSLog(@"hello"))
  4. Run the app and touch the view.

Expected:

hello should be printed

Actual:

Program crashes with:

-[UIViewControllerWrapperView aspects__touchesBegan:withEvent:]: unrecognized selector sent to instance 0x7fad8b975220
2015-09-06 14:36:10.575 FormSheetOverlayExperiment[51277:2624244] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIViewControllerWrapperView aspects__touchesBegan:withEvent:]: unrecognized selector sent to instance 0x7fad8b975220'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010f785c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010f41cbb7 objc_exception_throw + 45
    2   CoreFoundation                      0x000000010f78d0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x000000010f6e313c ___forwarding___ + 988
    4   CoreFoundation                      0x000000010f6e2cd8 _CF_forwarding_prep_0 + 120
    5   UIKit                               0x000000010ff46050 forwardTouchMethod + 245
    6   CoreFoundation                      0x000000010f67bdec __invoking___ + 140
    7   CoreFoundation                      0x000000010f67bc42 -[NSInvocation invoke] + 290 
    8   FormSheetOverlayExperiment          0x000000010ec4b174 __ASPECTS_ARE_BEING_CALLED__ + 4212
    9   CoreFoundation                      0x000000010f6e2f4f ___forwarding___ + 495
    10  CoreFoundation                      0x000000010f6e2cd8 _CF_forwarding_prep_0 + 120
    11  UIKit                               0x000000010ff46050 forwardTouchMethod + 245
    12  UIKit                               0x000000010fe107be -[UIWindow _sendTouchesForEvent:] + 325
    13  UIKit                               0x000000010fe11282 -[UIWindow sendEvent:] + 682
    14  UIKit                               0x000000010fdd7541 -[UIApplication sendEvent:] + 246
    15  UIKit                               0x000000010fde4cdc _UIApplicationHandleEventFromQueueEvent + 18265
    16  UIKit                               0x000000010fdbf59c _UIApplicationHandleEventQueue + 2066
    17  CoreFoundation                      0x000000010f6b9431 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    18  CoreFoundation                      0x000000010f6af2fd __CFRunLoopDoSources0 + 269
    19  CoreFoundation                      0x000000010f6ae934 __CFRunLoopRun + 868
    20  CoreFoundation                      0x000000010f6ae366 CFRunLoopRunSpecific + 470
    21  GraphicsServices                    0x0000000112a37a3e GSEventRunModal + 161
    22  UIKit                               0x000000010fdc28c0 UIApplicationMain + 1282
    23  FormSheetOverlayExperiment          0x000000010ec40e2f main + 111
    24  libdyld.dylib                       0x0000000111f7a145 start + 1
    25  ???                                 0x0000000000000001 0x0 + 1

Crashed: com.apple.main-thread EXC_BAD_ACCESS KERN_PROTECTION_FAILURE at 0x0116aebc

0 CoreFoundation
CFStringAppendBytes + 77
1
CoreFoundation
__CFStringAppendFormatCore + 7884
2 CoreFoundation
__CFStringAppendFormatCore + 7884
3
CoreFoundation
_CFStringCreateWithFormatAndArgumentsAux2 + 80
4
Foundation
-[NSPlaceholderString initWithFormat:locale:arguments:] + 144
5
Foundation
-[NSString initWithFormat:arguments:] + 26
6
Foundation
-[NSString stringByAppendingFormat:] + 92
7
apos
Aspects.m line 170
aspect_aliasForSelector
8
apos
Aspects.m line 478
__ASPECTS_ARE_BEING_CALLED

dealloc hooking is broken :(

I just added the following into my -application:didFinishLaunchingWithOptions: and nothing showed up when I know for a fact that some UIViewControllers were in fact deallocated.

    [UIViewController aspect_hookSelector:NSSelectorFromString(@"dealloc") withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
        NSLog(@"View Controller DEALLOC %@ : %tu", aspectInfo.instance, animated);
    } error:NULL];

Aspects and ReactiveCocoa are not friends

Don't know exactly in what library is issue, but I think it will be interest for you

First I subscribes with aspects to any pushViewController

  [ UINavigationController aspect_hookSelector: @selector(pushViewController:animated:)
                                   withOptions: AspectPositionAfter
                                    usingBlock:
   ^( id<AspectInfo> info, UIViewController* controller, BOOL animated )
   {
      //some code
   }
                                         error: 0 ];

Then I am trying to take pushViewController with ReactiveCocoa:

  [[ navigationController rac_signalForSelector: @selector(pushViewController:animated:) ]
  subscribeNext:^(id x) {
     //some code
  }];

But result is unexpected. Pushed view controller is not presented. If I remove subscription with aspects, everything works as expected.

Can you please assist.

Collision with KVO

Hi,

Thnx for this library :)

I have a question about potential issue that I've uncovered while trying to implement something similar in one of my own library.

BaseClass *baseClass = [[BaseClass alloc] init];
[baseClass addObserver:self forKeyPath:@"property" options:0 context:nil];
SubClass *subclass = [[SubClass alloc] init];
[subclass addObserver:self forKeyPath:@"property" options:0 context:nil];

[baseClass aspect_hookSelector:@selector(method) withOptions:0 usingBlock:^(id instance) {
    NSLog(@"class");
} error:nil];

[subclass aspect_hookSelector:@selector(method) withOptions:0 usingBlock:^(id instance) {
    NSLog(@"subclass");
} error:nil];

// Commenting these 2 lines make it to work again because it looks like wrong class is being swizzled.
// KVO will swap `object_getClass` to original after all observers have been removed {
[baseClass removeObserver:self forKeyPath:@"property"];
[subclass removeObserver:self forKeyPath:@"property"];
// }

[subclass method];

... where ...

@interface BaseClass : NSObject

@property (nonatomic, copy) NSString *property;

-(void)method;

@end

@interface SubClass : BaseClass
@end

@implementation BaseClass

-(void)method {
    NSLog(@"called base");
}

@end

@implementation SubClass

-(void)method {
    NSLog(@"called sub");
    [super method];
}

@end

It looks to me that the problem is here

static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    // Already subclassed
    if ([className hasSuffix:AspectsSubclassSuffix]) {
        return baseClass;

        // We swizzle a class object, not a single object.
    }else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);
        // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
    }else if (statedClass != baseClass) {
        return aspect_swizzleClassInPlace(statedClass); // <-- I believe this should be used
        //return aspect_swizzleClassInPlace(baseClass);
    }

If I understand this code correctly, if target object is already being swizzled (like with KVO), the "acted" class should be used because otherwise class returned with object_getClass could be swapped and changed again, thus killing installed observer (like it happens in that example that I've pasted).

It also seems to me that it should be

Class baseClass = self.class;
Class subclass = object_getClass(self);

... instead of ..

Class statedClass = self.class;
Class baseClass = object_getClass(self);

Is this a bug or am I doing something wrong?

Kruno

Multiple hooks

Hi,

I am trying to hook everytime one of my viewcontrollers execute viewDidAppear: But when I do it in the second of my ViewControllers I am getting this error.

Error Domain=AspectErrorDomain Code=3 "Error: viewDidAppear: already hooked in
myViewController. A method can only be hooked once per class hierarchy."
UserInfo=0xdb3fd70 {NSLocalizedDescription=Error: viewDidAppear: already hooked in
myViewController. A method can only be hooked once per class hierarchy.

They have no more relationship than inheriting from UIViewController. Is there any way to do it without having this error?

Won't build on iOS (7+1)

Chances are you're already on top of it, but since there's no filed issue on the subject yet...

The issue is that the PAGE_SIZE macro resolves to extern vm_size_t vm_page_size, which, well, is indeed not defined at compile time.

Aspects.m:616:116: error: initializer element is not a compile-time constant
static const size_t numberOfTrampolinesPerPage = (PAGE_SIZE - AspectsTrampolineInstructionCount * sizeof(int32_t)) / sizeof(AspectsTrampolineEntryPointBlock);
                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Aspects.m:626:32: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
    AspectsTrampolineDataBlock trampolineData[numberOfTrampolinesPerPage];
                               ^
Aspects.m:628:38: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
    AspectsTrampolineEntryPointBlock trampolineEntryPoints[numberOfTrampolinesPerPage];
                                     ^
Aspects.m:632:1: error: variable length array declaration not allowed at file scope
check_compile_time(sizeof(AspectsTrampolinePage) == 2 * PAGE_SIZE);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In module 'Darwin' imported from Aspects.m:9:
/Applications/Xcode(5+1)-Greek.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS(7+1).0.sdk/usr/include/AssertMacros.h:1419:39: note: expanded from macro 'check_compile_time'
                #define check_compile_time( expr )  __Check_Compile_Time( expr )
                                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode(5+1)-Greek.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS(7+1).0.sdk/usr/include/AssertMacros.h:1221:24: note: expanded from macro '__Check_Compile_Time'
            extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) )
                       ^                           ~~~~~~~~~~~~~~~~~
Aspects.m:633:1: error: variable length array declaration not allowed at file scope
check_compile_time(offsetof(AspectsTrampolinePage, trampolineInstructions) == PAGE_SIZE);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In module 'Darwin' imported from Aspects.m:9:
/Applications/Xcode(5+1)-Greek.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS(7+1).0.sdk/usr/include/AssertMacros.h:1419:39: note: expanded from macro 'check_compile_time'
                #define check_compile_time( expr )  __Check_Compile_Time( expr )
                                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode(5+1)-Greek.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS(7+1).0.sdk/usr/include/AssertMacros.h:1221:24: note: expanded from macro '__Check_Compile_Time'
            extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) )

Infinite Loop when overriding a method at two points which includes a call to super

This will infinitely loop:

@interface A : NSObject
- (void)foo;
@end

@implementation A
- (void)foo {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}
@end

@interface B : A @end

@implementation B
- (void)foo {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [super foo];
}
@end

int main(int argc, char *argv[]) {
    [B aspect_hookSelector:@selector(foo) atPosition:AspectPositionBefore withBlock:^(id object, NSArray *arguments) {
        NSLog(@"before -[B foo]");
    }];
    [A aspect_hookSelector:@selector(foo) atPosition:AspectPositionBefore withBlock:^(id object, NSArray *arguments) {
        NSLog(@"before -[A foo]");
    }];

    B *b = [[B alloc] init];
    [b foo];
}

Crash when handle notification in block

I want to add notification to all view using aspects,
it crash wtih error "EXC_BAD_ACCESS" when receive notification

    [UIView aspect_hookSelector:@selector(initWithFrame:)
                     withOptions:AspectPositionAfter
                      usingBlock:^(id<AspectInfo> aspectInfo){
                          [[NSNotificationCenter defaultCenter] addObserverForName:HGDidUpdateViewModeNotification object:nil queue:nil usingBlock:^(NSNotification *note){
                              //Crash
                              if ([[aspectInfo instance] respondsToSelector:@selector(switchViewMode)]){

                              }
                          }];

UIView subclass hook dealloc cause invocation BAD ACCESS

Using aspects to hook my UIView subclass's dealloc method with AspectPositionBefore option, it works fine as a before hook, callback get called.

And it gets BAD ACCESS when Aspects trying to invoke aliasSelector on Line 498:
https://github.com/steipete/Aspects/blob/master/Aspects.m#L498

My question is:

  1. Why we need to invoke aliasSelector again when there's no instead hooks?
  2. I test on UIViewController subclass and NSObject subclass, they works fine with out BAD ACCESS, do you have any idea why UIView subclass gets BAD ACCESS?

32-bit CPU iPhone and 64-bit CPU Mac will crash when return type is CGSize

https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/100-32-bit_PowerPC_Function_Calling_Conventions/32bitPowerPC.html#//apple_ref/doc/uid/TP40002438-SW13

Returning Results
The following list describes where a function’s return value is passed to the caller.

  1. Scalars smaller than 4 bytes (such as char and short) are placed in the low word of GPR3. The register’s high word is undefined.
  2. Scalars 4 bytes in size (such as long, int, and pointers, including array pointers) are placed in GPR3.
  3. Values of type long long are returned in the high word of GPR3 and the low word of GPR4.
  4. Floating-point values are placed in FPR1.
  5. Composite values (such as struct and union) and values larger than 4 bytes are placed at the location pointed to by GPR3. See Passing Arguments for more information.

5685a99e-780d-48fb-ac01-f8126da7a710

x86_64 is more complicated, I don't know how to fix it.

Hook Class Static Function NSDocumentController : sharedDocumentController Failed!

When I use Aspects Hook NSDocumentController : sharedDocumentController

[NSDocumentController aspect_hookSelector:@selector(sharedDocumentController) withOptions:AspectPositionBefore usingBlock:^(id info) {

    AspectInfo *appInfo = info;

    NSDocumentController *dvc = appInfo.instance;



} error:NULL];

Console log indicates NSMethodSignature doesn't match,But I think is OK,Can you give me some sugesstion ?

Aspects: Block signature <NSMethodSignature: 0x600000078600> doesn't match (null).

swizzling, isa-swizzling or proxying?

Hi @steipete !

Fantastic work on the AOP lib! . . (I hadn't actually looked it at until the maniacdev article came out).

What is the mechanism for proxying classes?

  • regular swizzling? - all instances
  • isa swizzling - the specified instance(s)
  • proxying / NSInvocation ?

If its the latter, we may be able to use it in Typhoon (internal feature), as well as make it the recommend AOP lib for Typhoon.

For the internal feature we need the NSInvocation way, because we're doing some funky stuff with capturing an arbitrary number of runtime args.

With swizzling that would require va_args and with arm64 devices va_args were going straight to the CPU registers, instead of onto the stack. . . NSInvocation just works.

Can't hook class methods

Hello,

I have figured out an issue concerning class method (not instance methods).

I have tried to put this into my code :

[NSJSONSerialization aspect_hookSelector:@selector(JSONObjectWithData:options:error:) withOptions:AspectPositionBefore usingBlock:^(id info, NSData _data, NSJSONReadingOptions opt, NSError *_error){
NSLog(@"JSONObjectWithData:options:error: before | object:%@ | instance:%@ | class:%@",info,[info instance],[[info instance] class]);
} error:NULL];

        [NSJSONSerialization aspect_hookSelector:@selector(JSONObjectWithData:options:error:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, NSData *data, NSJSONReadingOptions opt, NSError **error){
            NSLog(@"JSONObjectWithData:options:error: after | object:%@ | instance:%@ | class:%@",info,[info instance],[[info instance] class]);
        } error:NULL];

The Aspects did not intercept class methods examples set in my AppDelegate for example.
Do you know a solution for this issue ?

Thanks

How to solve this academic example

Hello everyone !
Here is the example of AOP from Wikipedia (http://en.wikipedia.org/wiki/Aspect-oriented_programming).

void transfer(Account fromAcc, Account toAcc, int amount, User user,
Logger logger) throws Exception {
logger.info("Transferring money…");

if (!isUserAuthorised(user, fromAcc)) {
logger.info("User has no permission.");
throw new UnauthorisedUserException();
}

if (fromAcc.getBalance() < amount) {
logger.info("Insufficient funds.");
throw new InsufficientFundsException();
}

fromAcc.withdraw(amount);
toAcc.deposit(amount);

database.commitChanges(); // Atomic operation.

logger.info("Transaction successful.");
}

The question is next: How can I do an AOP injection instead of logger.info. The Logger occurs 3 times in the method but I have injections options like before/after method execution. How can I solve this issue.
Thanks!

Unrecognized selector sent to instance

I'm getting a crash on a UIViewController viewDidAppear

Its an awkward situation where LLDB says that the instance responds to selector but when actually sending the message it says it doesn't.

Is this an aspect issue?

(lldb) po [0x7fce2b95d480 class]
XXXViewController
(lldb) e (BOOL)[0x7fce2b95d480 respondsToSelector:@selector(viewDidAppear:)]
(BOOL) $2 = YES
(lldb) e (void)[0x7fce2b95d480 viewDidAppear:YES]
error: Execution was interrupted, reason: breakpoint 1.1.
The process has been returned to the state before expression evaluation.
2015-12-13 18:19:09.316 [13959:7671911] -[XXXViewController viewDidAppear:]: unrecognized selector sent to instance 0x7fce2b95d480

Problem with NSProxy subclasses

Hi there,

I have an application with a class named DynamicUIWebViewDelegate that I want to put hooks on some methods of it.
The problem is that class is a subclass of NSProxy so when I add the hooker I have an exception at runtime :

[DynamicUIWebViewDelegate aspect_hookSelector:withOptions:usingBlock:error:]: unrecognized selector sent to class

How could I react to solve this issue ?

Thanks Peter

why not hook NSBundle infoDictionary function?

[NSBundle aspect_hookSelector:@selector(infoDictionary) withOptions:AspectPositionInstead usingBlock:^(id aspectInfo) {
NSDictionary *dictInfoDictionary;
NSInvocation *invocationInfoDictionary = aspectInfo.originalInvocation;
[invocationInfoDictionary invoke];
[invocationInfoDictionary getReturnValue:&dictInfoDictionary];
NSMutableDictionary *dictInfoDictionaryNew =[NSMutableDictionary dictionaryWithDictionary:dictInfoDictionary];
dictInfoDictionaryNew[@"CFBundleIdentifier"] = @"com.naruto1.demo1";
NSLog(@"[%s,%d] dictInfoDictionary:%@",FUNCTION,LINE,dictInfoDictionaryNew);
[invocationInfoDictionary setReturnValue:&dictInfoDictionaryNew];
} error:NULL];

when I write this code:
NSLog(@"[%s,%d] dictInfoDictionary:%@",FUNCTION,LINE,[[NSBundle mainBundle] infoDictionary]);

console:
*** -[__NSDictionaryM retain]: message sent to deallocated instance 0x155ef1580

app get crash in a non-related Class

Code is like following:

id<AspectToken> token = [UIView aspect_hookSelector:@selector(touchesEnded:withEvent:)
                                                     withOptions:AspectPositionAfter
                                                      usingBlock:^(id<AspectInfo> aspectInfo) { 
                                                       ///Do my stuff
                                                      }
                                                           error:nil];

it is called once.

but app get crash when random touches on screen. Call stack:

-[UINavigationController aspects__touchesEnded:withEvent:]: unrecognized selector sent to instance 0x111798380
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController aspects__touchesEnded:withEvent:]: unrecognized selector sent to instance 0x111798380'

0   CoreFoundation                      0x000000010392ba75 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010633abb7 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000103932d1d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x000000010388a9dc ___forwarding___ + 988
    4   CoreFoundation                      0x000000010388a578 _CF_forwarding_prep_0 + 120
    5   UIKit                               0x000000010465c980 forwardTouchMethod + 245
    6   UIKit                               0x000000010465c980 forwardTouchMethod + 245
    7   CoreFoundation                      0x00000001038235ec __invoking___ + 140
    8   CoreFoundation                      0x0000000103823442 -[NSInvocation invoke] + 290
    9   iAliexpress                         0x000000010072ff9a __ASPECTS_ARE_BEING_CALLED__ + 4410
    10  CoreFoundation                      0x000000010388a7ef ___forwarding___ + 495
    11  CoreFoundation                      0x000000010388a578 _CF_forwarding_prep_0 + 120
    12  UIKit                               0x000000010452cb68 -[UIWindow _sendTouchesForEvent:] + 735
    13  UIKit                               0x000000010452d493 -[UIWindow sendEvent:] + 683
    14  UIKit                               0x00000001044f9fb1 -[UIApplication sendEvent:] + 246
    15  CoreFoundation                      0x00000001038235ec __invoking___ + 140
    16  CoreFoundation                      0x0000000103823442 -[NSInvocation invoke] + 290
    17  iAliexpress                         0x000000010072ff9a __ASPECTS_ARE_BEING_CALLED__ + 4410
    18  CoreFoundation                      0x000000010388a7ef ___forwarding___ + 495
    19  CoreFoundation                      0x000000010388a578 _CF_forwarding_prep_0 + 120
    20  UIKit                               0x0000000104507227 _UIApplicationHandleEventFromQueueEvent + 17700
    21  UIKit                               0x00000001044e223c _UIApplicationHandleEventQueue + 2066
    22  CoreFoundation                      0x0000000103860c91 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    23  CoreFoundation                      0x0000000103856b5d __CFRunLoopDoSources0 + 269
    24  CoreFoundation                      0x0000000103856194 __CFRunLoopRun + 868
    25  CoreFoundation                      0x0000000103855bc6 CFRunLoopRunSpecific + 470
    26  GraphicsServices                    0x00000001074c2a58 GSEventRunModal + 161
    27  UIKit                               0x00000001044e5580 UIApplicationMain + 1282
    28  iAliexpress                         0x000000010000f43a main + 106
    29  libdyld.dylib                       0x0000000106833145 start + 1

Any idea?

Hooking same selector name in different subclasses does not work

I'm currently writing an Xcode plugin and got a situation where two classes (IBICMultipartImageSectionTitleView and IBICMultipartImageView) inherit from NSView, don't inherit from each other, but implement the same method name.
I want to hook the selector in both classes individually, but when I try to hook the selector in the second class I get the following error message:

Aspects: Error: effectiveTitleColor already hooked in IBICMultipartImageView. A method can only be hooked once per class hierarchy.
Failed to hook -[IBICMultipartImageSectionTitleView effectiveTitleColor] with error:
Error Domain=AspectErrorDomain Code=3 "Error: effectiveTitleColor already hooked in IBICMultipartImageView. A method can only be hooked once per class hierarchy." UserInfo=0x600001a6a400 {NSLocalizedDescription=Error: effectiveTitleColor already hooked in IBICMultipartImageView. A method can only be hooked once per class hierarchy.}

I tried to track down the issue and I'm not sure if the following line in static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) is correct:

[tracker.selectorNames addObject:selectorName];

Shouldn't the selector name be only added to the tracker of the class for which the implementation is actually hooked?

block parameters are pretty long

Could maybe do with a typedef in there, typedef BOOL(^AspectReturnBlock)(__unsafe_unretained id instance, NSArray *arguments); ?

Add selector for optional method from protocol

Is it possible to add instance method that may not be defined on target class but defined in protocol as optional?

Currently -aspect_hookSelector reports that it cannot find selector.

p.s.: Adding empty method implementation solves the problem.

App crashes when trying to remove aspect token

I use Aspects for hooking some logging functions into NSViewController instances under OS X.
When I want to disable the aspect hooks I call the remove method. When I do so the app crashes due to an assertion failure:

NSCAssert(originalClass != nil, @"Original class must exist");
This is obivously true as the class name has a NSKVONotifying prefix:
NSKVONotifying_ScheduleViewController_Aspects_

I don't see why this happens. Any ideas on how to fix this?

Using Aspects with non-void return types

This is not a bug - just a request for clarification:
I am hooking a method where I want to change the return value to something that is created within the block. I'm having an issue (which is obvious once I see it) where the return value is deallocated as soon as the block completed and hence the calling class causes a crash when it tries to access it. Here is the example code:

self.aspectTokenSupportedEvents = [SomeObject aspect_hookSelector:@selector(supportedActions)
    withOptions:AspectPositionInstead
    usingBlock:^(id<AspectInfo> aspectInfo) {
        NSInvocation *invocation = aspectInfo.originalInvocation;
        SomeObject *object = aspectInfo.instance;
        NSArray *arrayOfActions = [object.arrayOfActions mutableCopy];
        CFRetain((__bridge CFTypeRef)(arrayOfActions));
        [invocation setReturnValue:&arrayOfActions];
    }
    error:NULL];

Is the CFRetain use the correct approach for the situation?

Question: Is it possible to "replace" the original invocation and perform it in a different context

I came across a situation where I need to replace a method with a private implementation but at a later time perform the original implementation (after my replaced block has run).

For instance:

[SomeViewController aspect_hookSelector:@selector(buttonWasPressed:) withOptions:AspectPositionInstead usingBlock:(id info) {
// Do some things to the UI
}];

// somewhere else in the code....
[invokeOriginalMethod buttonWasPressed]

How can I do this?

hook "dealloc" crash

[UIResponder aspect_hookSelector:NSSelectorFromString(@"dealloc") withOptions:AspectPositionBefore usingBlock:^(id aspectInfo) {
[[NSNotificationCenter defaultCenter] removeObserver:aspectInfo.instance];
} error:nil];

add UITextView or UITextField

crash: *** -[UITextInputController dealloc]: message sent to deallocated instance 0x7fec02fc29b0

Pointcut expression language

Suggest adding a 'point-cut' expression language that allows:

  • Selecting all the places a cross-cutting concern can be woven in.

This would allow:

  • Matching by class-name or protocol (with wild-cards?)
  • Matching by method name (with wild-cards?)
  • Matching by parameters (with wild-cards? objc probably only allows count. . except for props, where it also gives type)
  • Matching by return-type (objc only allows this for properties).

can't access return value's property

Hi, here is my code:

    [self aspect_hookSelector:@selector(initWithCoder:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        __weak UINavigationItem *item;
        NSInvocation *invocation = aspectInfo.originalInvocation;
        [invocation invoke];
        [invocation getReturnValue:&item];
        if (item.title) {
            UILabel *titleLable = [[UILabel alloc] init];
            titleLable.textColor = [UIColor whiteColor];
            titleLable.font = [UIFont systemFontOfSize:16];
            [titleLable setText:item.title];
            [titleLable sizeToFit];
            item.titleView = titleLable;
        }
    } error:nil];

I can't step into if statement, but po item.title in console, I get the right value.

Aspect is called twice

I set an aspect on ViewDidAppear (Tried with ViewWillAppear as well)

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionBefore usingBlock:^(id <AspectInfo>info){
        NSLog(@"Aspect viewWillAppear %@",info.instance);
    }error:nil];
    // Do any additional setup after loading the view, typically from a nib.
}

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"Super viewWillAppear ");
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear %@",self);
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            ViewController *vc = [ViewController new];
            [self presentViewController:vc animated:YES completion:nil];
        });

    });
}



@end

The console outputs the following:

Super viewWillAppear 
Aspect viewWillAppear <ViewController: 0x7fc54040ffe0>
viewWillAppear <ViewController: 0x7fc54040ffe0>
Super viewWillAppear 
Aspect viewWillAppear <ViewController: 0x7fc540416b50>
Aspect viewWillAppear <ViewController: 0x7fc540416b50>
viewWillAppear <ViewController: 0x7fc540416b50>

Any ideas?

Aspects don't intercept the methods of NSFileHandle

Hello,
I tried to intercept the methods of NSFileHandle like that :

[NSFileHandle aspect_hookSelector:@selector(writeData:) withOptions:AspectPositionAfter usingBlock:^(id info, NSData *data){
NSLog(@"writeData: after | object:%@ | instance:%@ | class:%@",info,[info instance],[[info instance] class]);
} error:NULL];
[NSFileHandle aspect_hookSelector:@selector(writeData:) withOptions:AspectPositionBefore usingBlock:^(id info, NSString *data){
NSLog(@"writeData: before | object:%@ | instance:%@ | class:%@",info,[info instance],[[info instance] class]);
} error:NULL];
NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
NSString *filePath=[docPath stringByAppendingPathComponent:@"test.json"];
NSString *resultLine=[NSString stringWithFormat:@"coucou c dhekra"];
NSString *newFilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Documents/test.json"];
NSFileHandle *handler = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
NSLog(@"newFilePath %@ %@",newFilePath,[resultLine dataUsingEncoding:NSUTF8StringEncoding]);
if(!handler) {
handler= [NSFileHandle fileHandleForWritingAtPath:newFilePath];
}
[handler seekToEndOfFile];
[handler writeData:[resultLine dataUsingEncoding:NSUTF8StringEncoding]];

But i didn't have a result why??

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.