steipete / aspects Goto Github PK
View Code? Open in Web Editor NEWDelightful, simple library for aspect oriented programming in Objective-C and Swift.
Home Page: https://twitter.com/steipete
License: MIT License
Delightful, simple library for aspect oriented programming in Objective-C and Swift.
Home Page: https://twitter.com/steipete
License: MIT License
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.
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.
Hi Steipete, why can't hook viewDidLoad (UIViewController) ??
Error:
Aspects: Blog signature <NSMethodSignature: 0x7fbc8bd35740> doesn't match <NSMethodSignature: 0x7fbc8bf0cc40>.
Please help~
Thank u.
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
"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.
Could maybe do with a typedef in there, typedef BOOL(^AspectReturnBlock)(__unsafe_unretained id instance, NSArray *arguments);
?
[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
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.
when i hook - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView, i must implement the method, even it's empty method!
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).
Reproduction:
[UIViewController new]
touchesBegan:withEvent:
method; do something (e.g. NSLog(@"hello")
)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
Currently the first hook to a class wins, so if B subclasses A, and both implement foo, we can either hook foo in A or in B, but not in both classes.
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)]){
}
}];
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];
}
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!
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?
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.
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
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?
[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
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
Returning Results
The following list describes where a function’s return value is passed to the caller.
- 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.
- Scalars 4 bytes in size (such as long, int, and pointers, including array pointers) are placed in GPR3.
- Values of type long long are returned in the high word of GPR3 and the low word of GPR4.
- Floating-point values are placed in FPR1.
- 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.
x86_64 is more complicated, I don't know how to fix it.
Suggest adding a 'point-cut' expression language that allows:
This would allow:
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?
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.
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>.}
It seems like the version tagged here on github isn't available on cocoapods.
https://cocoapods.org/pods/Aspects
Can you pod trunk push Aspects.podspec
?
The AppDelegate, being the overseer of the application's lifecycle, has a lot of responsibilities. Projects like https://github.com/floriankrueger/SOXAppDelegate have attempted to help developers split out those responsibilities.
An example showing Aspects working to the same effect would be great.
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?
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?
@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!
I know you've done some talks about Aspects, like this one that Kyle made me discover. Maybe you should add those to either your README or in the GitHub's Wiki?
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);
}];
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?
I have a Swift subclass of UITableViewCell and an @objc method that takes one parameter (NSError).
Hooking this selector gives no errors, but the aspect hook is NOT invoked.
(Xcode 7 beta 6)
A method can only be hooked once per class hierarchy!
But If I want to hook UIViewController and UINavigationController 's viewDidLoad
method for different purpose.
What should I do?
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:
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
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:
Line 626 in 3fcc13a
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??
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 :)
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 ) )
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?
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
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,
a temporary fix: yanjunz@c90cb1b
I just added the following into my -application:didFinishLaunchingWithOptions:
and nothing showed up when I know for a fact that some UIViewController
s 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];
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.