Aspects v1.4.1 [![Build Status](https://travis-ci.org/steipete/Aspects.svg?branch=master)](https://travis-ci.org/steipete/Aspects)
==============
Delightful, simple library for aspect oriented programming by [@steipete](http://twitter.com/steipete).
**Think of Aspects as method swizzling on steroids. It allows you to add code to existing methods per class or per instance**, whilst thinking of the insertion point e.g. before/instead/after. Aspects automatically deals with calling super and is easier to use than regular method swizzling.
This is stable and used in hundreds of apps since it's part of [PSPDFKit, an iOS PDF framework that ships with apps like Dropbox or Evernote](http://pspdfkit.com), and now I finally made it open source.
Aspects extends `NSObject` with the following methods:
``` objc
/// Adds a block of code before/instead/after the current `selector` for a specific class.
///
/// @param block Aspects replicates the type signature of the method being hooked.
/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
/// These parameters are optional and will be filled to match the block signature.
/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
///
/// @note Hooking static methods is not supported.
/// @return A token which allows to later deregister the aspect.
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
/// Deregister an aspect.
/// @return YES if deregistration is successful, otherwise NO.
id<AspectToken> aspect = ...;
[aspect remove];
```
Adding aspects returns an opaque token of type `AspectToken` which can be used to deregister again. All calls are thread-safe.
Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called 1000 times per second.
Aspects calls and matches block arguments. Blocks without arguments are supported as well. The first block argument will be of type `id<AspectInfo>`.
When to use Aspects
-------------------
Aspect-oritented programming (AOP) is used to encapsulate "cross-cutting" concerns. These are the kind of requirements that *cut-accross* many modules in your system, and so cannot be encapsulated using normal Object Oriented programming. Some examples of these kinds of requirements:
* *Whenever* a user invokes a method on the service client, security should be checked.
* *Whenever* a useer interacts with the store, a genius suggestion should be presented, based on their interaction.
* *All* calls should be logged.
If we implemented the above requirements using regular OO there'd be some drawbacks:
Good OO says a class should have a single responsibility, however adding on extra *cross-cutting* requirements means a class that is taking on other responsibilites. For example you might have a **StoreClient** that supposed to be all about making purchases from an online store. Add in some cross-cutting requirements and it might also have to take on the roles of logging, security and recommendations. This is not great:
* Our StoreClient is now harder to understand and maintain.
* These cross-cutting requirements are duplicated and spreading throughout our app.
AOP lets us modularize these cross-cutting requirements, and then cleanly identify all of the places they should be applied. As shown in the examples above cross-cutting requirements can be eithe technical or business focused in nature.
## Here are some concrete examples:
Aspects can be used to **dynamically add logging** for debug builds only:
``` objc
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated);
} error:NULL];
```
-------------------
It can be used to greatly simplify your analytics setup:
https://github.com/orta/ARAnalytics
-------------------
You can check if methods are really being called in your test cases:
``` objc
- (void)testExample {
TestClass *testClass = [TestClass new];
TestClass *testClass2 = [TestClass new];
__block BOOL testCallCalled = NO;
[testClass aspect_hookSelector:@selector(testCall) withOptions:AspectPositionAfter usingBlock:^{
testCallCalled = YES;
} error:NULL];
[testClass2 testCallAndExecuteBlock:^{
[testClass testCall];
} error:NULL];
XCTAssertTrue(testCallCalled, @"Calling testCallAndExecuteBlock must call testCall");
}
```
-------------------
It can be really useful for debugging. Here I was curious when exactly the tap gesture changed state:
``` objc
[_singleTapGesture aspect_hookSelector:@selector(setState:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
NSLog(@"%@: %@", aspectInfo.instance, aspectInfo.arguments);
} error:NULL];
```
-------------------
Another convenient use case is adding handlers for classes that you don't own. I've written it for use in [PSPDFKit](http://pspdfkit.com), where we require notifications when a view controller is being dismissed modally. This includes UIKit view controllers like `MFMailComposeViewController` or `UIImagePickerController`. We could have created subclasses for each of these controllers, but this would be quite a lot of unnecessary code. Aspects gives you a simpler solution for this problem:
``` objc
@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
```
Debugging
---------
Aspects identifies itself nicely in the stack trace, so it's easy to see if a method has been hooked:
<img src="https://raw.githubusercontent.com/steipete/Aspects/master/stacktrace@2x.png?token=58493__eyJzY29wZSI6IlJhd0Jsb2I6c3RlaXBldGUvQXNwZWN0cy9tYXN0ZXIvc3RhY2t0cmFjZUAyeC5wbmciLCJleHBpcmVzIjoxMzk5NzQ3OTI3fQ%3D%3D--97cf7e7bac491149eb8db3d1b9a562ab88154a3c" width="75%">
Using Aspects with non-void return types
----------------------------------------
You can use the invocation object to customize the return value:
``` objc
[PSPDFDrawView aspect_hookSelector:@selector(shouldProcessTouches:withEvent:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> info, NSSet *touches, UIEvent *event) {
// Call original implementation.
BOOL processTouches;
NSInvocation *invocation = info.originalInvocation;
[invocation invoke];
[invocation getReturnValue:&processTouches];
if (processTouches) {
processTouches = pspdf_stylusShouldProcessTouches(touches, event);
[invocation setReturnValue:&processTouches];
}
} error:NULL];
```
Installation
------------
The simplest option is to use `pod "Aspects"`.
You can also add the two files `Aspects.h/m`. There are no further requirements.
Compatibility and Limitations
-----------------------------
Aspects uses quite some runtime trickery to achieve what it does. You can mostly mix this with regular method swizzling.
An important limitation is that for class-based hooking, a method can only be hooked
没有合适的资源?快使用搜索试试~ 我知道了~
swift-HotFix一种轻量级的可以通过苹果审核的热修复方案可以替代JSPatch
共47个文件
m:7个
plist:6个
h:6个
2星 需积分: 33 20 下载量 192 浏览量
2019-08-14
16:49:53
上传
评论 1
收藏 83KB ZIP 举报
温馨提示
一种轻量级的可以通过苹果审核的热修复方案,可以替代JSPatch的热修复方案
资源推荐
资源详情
资源评论
收起资源包目录
swift-HotFix一种轻量级的可以通过苹果审核的热修复方案可以替代JSPatch.zip (47个子文件)
HotFix-master
Pods
Manifest.lock 200B
Target Support Files
Pods-FIX
Pods-FIX-resources.sh 5KB
Pods-FIX-frameworks.sh 6KB
Pods-FIX-acknowledgements.markdown 1KB
Pods-FIX-acknowledgements.plist 2KB
Pods-FIX-dummy.m 120B
Pods-FIX.release.xcconfig 585B
Pods-FIX.debug.xcconfig 585B
Aspects
Aspects.xcconfig 565B
Aspects-prefix.pch 195B
Aspects-dummy.m 118B
Aspects
Aspects.h 4KB
Aspects.m 36KB
LICENSE 1KB
README.md 10KB
Headers
Public
Aspects
Aspects.h 26B
Private
Aspects
Aspects.h 26B
Pods.xcodeproj
xcuserdata
xgf.xcuserdatad
xcschemes
xcschememanagement.plist 547B
Aspects.xcscheme 2KB
Pods-FIX.xcscheme 2KB
project.pbxproj 20KB
Podfile.lock 200B
HotFix.podspec 509B
FIX.xcworkspace
xcshareddata
IDEWorkspaceChecks.plist 238B
contents.xcworkspacedata 221B
xcuserdata
xgf.xcuserdatad
UserInterfaceState.xcuserstate 31KB
Podfile 192B
LICENSE 1KB
HotFix
HotFix.m 7KB
HotFix.h 242B
README.md 3KB
FIX.xcodeproj
project.xcworkspace
xcshareddata
IDEWorkspaceChecks.plist 238B
contents.xcworkspacedata 148B
xcuserdata
xgf.xcuserdatad
UserInterfaceState.xcuserstate 17KB
xcuserdata
xgf.xcuserdatad
xcschemes
xcschememanagement.plist 326B
project.pbxproj 17KB
main
ViewController.m 521B
Info.plist 1KB
main.m 320B
Base.lproj
LaunchScreen.storyboard 2KB
Main.storyboard 2KB
ViewController.h 201B
AppDelegate.h 263B
Assets.xcassets
AppIcon.appiconset
Contents.json 2KB
Contents.json 62B
AppDelegate.m 2KB
.gitignore 431B
共 47 条
- 1
资源评论
- 老青菜2021-05-12aspect 只能替换 继承NSObject的class吧,对于纯swift的class 或者struct 不行吧。
普通网友
- 粉丝: 484
- 资源: 1万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功