AspectObjectiveC (AOC) brings aspect oriented programming functionality to Objective-C. Basically, it allows you to execute arbitrary code before, instead of, or after any method on any class at runtime. You can replace entire methods, alter return values of methods, or alter the arguments to methods for which you don't have the code (e.g., in third party frameworks). Note that using AspectObjectiveC to replace methods within itself may rip a hole in the fabric of spacetime.
It is available under the MIT license (see LICENSE.txt).
The project is hosted on github: http://github.com/tomdalling/AspectObjectiveC
AOC works on OSX 10.5 and 10.6, for architectures i386 and x86_64.
It probably works on 10.4 and different architectures like ppc, but has not been tested. If you would like to compile it for 10.4 or another architecture, feel free to contact me via the AspectObjectiveC github page.
AOC is not available on iOS (formerly "iPhone OS") because it relies on the libffi closure API, which is not available on iOS.
You can build the project yourself, or just download the latest compiled version from the project's github download page: http://github.com/tomdalling/AspectObjectiveC/downloads.
The framework can be added to an Xcode project using the following steps.
1. Add the framework folder to the project. Drag and drop the AspectObjectiveC.framework folder into the project. | |
2. Link against the framework. Under the project's target, drag AspectObjectiveC.framework from the place it was dropped in the first step, and drop it into the build phase named Link Binary With Libraries | |
3. Copy the framework into the application bundle as a private framework. Under the project's target, create a New Copy Files Build Phase. In the info window for the new build phase, set Destination to Frameworks. Drag AspectObjectiveC.framework from the place it was dropped in the first step, and drop it under the newly created build phase. | |
4. Include the headers.
In files that use the framework, include the framework headers like so:
#import <AspectObjectiveC/AOC.h> |
The API documentation is available here: AspectObjectiveC API Documentation
A docset for Xcode integration is also available in the doc folder.
Advice objects contain the code that runs before/after/instead of a method. Advice objects must implement the AOCAdviceProtocol protocol. The class AOCAdvice is a base class that is provided for convenience, and implements AOCAdviceProtocol.
Check the API documentation for AOCAdviceProtocol and AOCAdvice.
The following is an example advice class. All three methods are optional.
@interface SomeAdvice : AOCAdvice -(double) adviceBeforeCelciusToFahrenheit:(double)celcius; -(double) adviceInsteadOfCelciusToFahrenheit:(double)celcius; -(double) adviceAfterCelciusToFahrenheit:(double)celcius; @end @implementation SomeAdvice -(double) adviceBeforeCelciusToFahrenheit:(double)celcius; { // Advice before can be used to alter method arguments before they // reach the method. In this case, we're going to set the `celcius` arg // to `10.0`, ignoring whatever the arg actually was. double newCelcius = 10.0; NSLog(@"BEFORE: celcius was %f, but I'm changing it to %f", celcius, newCelcius); [[self invocation] setArgument:&newCelcius atIndex:2]; return 0.0; //return value is ignored } -(double) adviceInsteadOfCelciusToFahrenheit:(double)celcius; { // "Instead of" advice completely replaces a method. // In this case, it will just halve whatever celcius is. double halfCelcius = celcius / 2.0; NSLog(@"INSTEAD OF: celcius is %f. will return %f", celcius, halfCelcius); return halfCelcius; //return value is NOT ignored } -(double) adviceAfterCelciusToFahrenheit:(double)celcius; { // Advice after can be used to change the return value of a method. // In this case, it will triple whatever the return value was double returnValue; [[self invocation] getReturnValue:&returnValue]; double newReturnValue = 3.0 * returnValue; [[self invocation] setReturnValue:&newReturnValue]; NSLog(@"AFTER: return value was %f, but I tripled it to %f", returnValue, newReturnValue); return 0.0; //return value is ignored } @end
Next, the advice object has to be installed on a method of a class. Once installed, the advice will run whenever the method is called on any instance of the class.
Check the API documentation for AOCAspectManager.
Advice is installed like so:
[[AOCAspectManager defaultAspectManager] installAdvice:[[SomeAdvice new] autorelease] forSelector:@selector(celciusToFahrenheit:) ofClass:[SomeOtherClass class] error:NULL];
Running the following code...
SomeOtherClass* soc = [[SomeOtherClass new] autorelease]; double returnValue = [soc celciusToFahrenheit:50.0]; NSLog(@"Final return value is %f", returnValue);
Should produce the following console output...
BEFORE: celcius was 50.000000, but I'm changing it to 10.000000 INSTEAD OF: celcius is 10.000000. will return 5.000000 AFTER: return value was 5.000000, but I tripled it to 15.000000 Final return value is 15.000000
This is the 1.0 release of AOC, and should be considered stable. I will continue active development if there is sufficient interest in the project, otherwise I will only be fixing bugs and accepting patches from other people.
AOC can potentially cause crashes or not work correctly when adding advice to KVC Accessor Methods. This is because AOC works by swizzling method implementations, and valueForKey: caches the method implementations of KVC accessors.
The solution to this problem is to only add advice to KVC accessor methods before valueForKey: ever calls the method. That way, valueForKey: caches the swizzled implementation instead of the original implementation. Also, you must never remove the advice from the KVC accessor method. This is because valueForKey: might have cached the swizzled implementation, and calling a swizzled implementation which has been deallocated (due to advice being removed) will cause a crash.
Thread safety has not been accounted for, so use this on different threads at your own risk.
Adding advice to a method will add an overhead every time the method is called, so you probably don't want to add advice to performance-critical methods.
AOC can't handle methods that return, or take as an argument, a struct with a bit field (e.g. NSDecimal). Structs without bit fields (e.g. NSRect) should be fine.
I'm open to bug fixes, new features, and optimisations. One feature I would like to add is some form of pointcut syntax so that advice can be added to multiple methods at once, instead of one at a time. If you wish to contribute, please do so via the AspectObjectiveC github project page.
So far, I (Tom Dalling) am the only contributor to the project. You can contact me via github.