NSProxy AOP
iOS AOP文章系列
前导知识:
AOP框架:
介绍
iOS 有一个原生的 AOP
方法,就是利用 NSProxy
代理类!,我们先看下官网介绍:
Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy
can be used to implement transparent distributed messaging (for example, NSDistantObject
) or for lazy instantiation of objects that are expensive to create.
NSProxy
implements the basic methods required of a root class, including those defined in the NSObjectProtocol
protocol. However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to. A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation(_:)
and methodSignatureForSelector:
methods to handle messages that it doesn’t implement itself.
说明两点:
NSProxy
本身就是用来做代理转发消息的NSProxy
的子类必须通过forwardInvocation
和methodSignatureForselector
来创建方法
NSProxy
的声明:
NS_ROOT_CLASS
@interface NSProxy <NSObject> {
__ptrauth_objc_isa_pointer Class isa;
}
+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;
- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
// - (id)forwardingTargetForSelector:(SEL)aSelector;
@end
包含 isa
指针,说明其本身就是一个对象。并且遵守 NSObjcet
协议,还存在 forwardInvocation
和 methodSignatureForSelector
方法。
🌰 🌰 在目
新增一个 MyProxy
类继承 NSProxy
,并在其中实现一个目标对象的代理引用,同时实现两个关键方法:
// MyProxy.h
@interface MyProxy : NSProxy {
id _target;
}
+ (id)proxyWithTarget:(id)target;
@end
// MyProxy.m
@implementation MyProxy
+ (id)proxyWithTarget:(id)target {
MyProxy *mp = [MyProxy alloc];
mp->_target = target;
return mp;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [_target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if (invocation && [_target respondsToSelector:invocation.selector]) {
NSString *sn = NSStringFromSelector(invocation.selector);
NSLog(@"do something before %@", sn);
[invocation invokeWithTarget:_target];
NSLog(@"do something after %@", sn);
}
}
@end
// main.m 中添加代码
// ==== NSProxy ====
Person *p = [MyProxy proxyWithTarget:[[Person alloc] init]];
[p sleep];
[p walk];
原理
proxyWithTarget:
实现将一个对象引用到自实现的代理类中,在真正调用对象方法时,代理类没有方法定义,找不到对应的 IMP
,就会进入消息转发流程,从而执行 forwardInvocation:
,而我们在 forwardInvocation:
中执行原方法前后加入额外逻辑实现 AOP
。
这里不仅可以打印方法名和修改方法逻辑,因为 NSInvocation 中封装了方法的全部信息,因此还能够打印或修改入参及返回值。