Aspects-iOS's AOP cross-section programming library, aspectsaop
Introduction
A concise and efficient library that enables iOS to support AOP for Aspect-Oriented Programming. it helps you change the behavior of a class without changing the code of a class or class instance. it is simpler and more efficient than the traditional iOS AOP method. you can execute a method either before or after it is executed. I used to be a part of PSPDFKit. PSPDFKit has applications in Dropbox and Evernote. It is now open-source for everyone.
Project homepage: Aspects
Latest instance: Click to download
Note: AOP is a completely different design mode from OOP. For more information, refer to: AOP Baidu encyclopedia
Quick Start environment requirements
- ARC
- IOS 7 + or OS X 10.7 +
Install and use CocoaPods
pod "Aspects"
Manual Installation
FileAspects. h/mDrag it to the project.
Application scenarios
Aspects is used to support the AOP (Aspect-Oriented Programming) mode and is used to partially solve specific problems that the OOP (object-oriented) mode cannot solve. specifically, it refers to the operations that are not allowed or are difficult to be effectively classified in multiple methods, such:
- Whenever a user obtains server data through a client, permission check is always required.
- No matter when the user interacts with the market, more user operations should always be provided to purchase reference or related products.
- All operations that require logging.
Interface Overview
AspectsNSObject extends the following method
:
/// Add a block of code before/After a method of a specified class is executed. all objects in this class will work. ///// @ param block when the hook is added, Aspectes copies the signature information of the method. /// the first parameter will be 'id <AspectInfo> ', and the remaining parameters will be the parameters of the called method. /// these parameters are optional and will be used to pass the parameters to the corresponding position of the block code block. /// you even use a block code block without any parameters or with only one 'id <AspectInfo> 'parameter. ///// @ Note: you cannot add a hook to a static method. /// @ return returns a unique value to cancel this hook. + (id <AspectToken>) aspect_hookSelector :( SEL) selector withOptions :( AspectOptions) options usingBlock :( id) block error :( NSError **) error; /// Add a block of code before/After a method of a specified object is executed. only applies to the current object. -(id <AspectToken>) aspect_hookSelector :( SEL) selector withOptions :( AspectOptions) options usingBlock :( id) block error :( NSError **) error; // undo an Aspect hook. /// @ return YES: The operation is successful. Otherwise, NO is returned. id <AspectToken> aspect = ...; [aspect remove];
All calls are thread-safe. aspects uses Objective-C to forward messages, resulting in performance consumption. aspects is not recommended for all calls that are too frequent. aspects is more suitable for view/controller-related code that calls no more than 1000 times per second.
Sample Code
You can use Aspects to dynamically add logging when debugging applications.
[UIViewController conditions: @ selector (viewWillAppear :) witexceptions: AspectPositionAfter usingBlock: ^ (id <AspectInfo> aspectInfo, BOOL animated) {NSLog (@ "Controller % @ will display: % tu ", aspectInfo. instance, animated);} error: NULL];
When you use it, the settings of the analysis function are simple:
Https://github.com/orta/ARAnalytics
You can use it in your test cases to check whether a method is actually called (when it involves inheritance or category extension, it is easy to see if a parent class/subclass method is not called as expected ):
-(Void) testExample {TestClass * testClass = [TestClass new]; TestClass * testClass2 = [TestClass new]; _ block BOOL testCallCalled = NO; [testClass aspect_hookSelector: @ selector (testCall) witexceptions: AspectPositionAfter usingBlock: ^ {testCallCalled = YES;} error: NULL]; [testClass2 testCallAndExecuteBlock: ^ {[testClass testCall];} error: NULL]; XCTAssertTrue (testCallCalled, @ "testCallAndExecuteBlock must call testCall ");}
It will play a major role in application debugging. here I want to know when the gesture state changes (if it is a subclass of your custom gesture, you can rewrite setState: method to achieve similar results; but the real purpose here is to capture the light-click gestures of all types of controls to accurately analyze the reasons ):
[_singleTapGesture aspect_hookSelector:@selector(setState:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) { NSLog(@"%@: %@", aspectInfo.instance, aspectInfo.arguments);} error:NULL];
The following is an example of when the controller you monitor a modal display disappears. generally, you can write a subclass to achieve similar effects, but using Aspects can effectively reduce your code volume:
@implementation UIViewController (DismissActionHook)// Will add a dismiss action once the controller gets dismissed.- (void)pspdf_addWillDismissAction:(void (^)(void))action { PSPDFAssert(action != NULL); [self aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) { if ([aspectInfo.instance isBeingDismissed]) { action(); } } error:NULL];}@end
Benefits for debugging
Aspectes automatically marks itself, so it is easy to check whether a method has been called in the call Stack:
Use Aspects when the return value is not void
You can use the NSInvocation object class to customize the return value:
[PSPDFDrawView aspect_hookSelector: @ selector (shouldProcessTouches: withEvent :) witexceptions: Using usingBlock: ^ (id <AspectInfo> info, NSSet * touches, UIEvent * event) {// call the original implementation of the method. BOOL processTouches; NSInvocation * invocation = info. response; [invocation invoke]; [invocation getReturnValue: & processTouches]; if (processTouches) {processTouches = Response (touches, event); [invocation setReturnValue: & processTouches];} error: NULL];
Compatibility and limitations
- When applied to a class (using the class method to add a hook), the same method of the parent class and the subclass cannot be hooked at the same time; otherwise, the loop call problem may occur. however, this restriction does not apply to examples of a class (using the instance method to add hooks.
- When using KVO, it is bestAspect_hookSelector:Add the observer after the call; otherwise, it may cause a crash.