SPLMessageLogger可以拦截并记录所有objc消息到您的控制台
-> <UINavigationBar:0x167a9960>: -[UIView setCenter:NSPoint: {384, 42}]
=> void
-> <UIView:0x167b7e40>: -[UIView setCenter:NSPoint: {384, 512}]
=> void
-> <UIImageView:0x167c56c0>: -[UIView setCenter:NSPoint: {0, 0}]
=> void
SPLMessageLogger可以通过CocoaPods获得,要安装它,只需将以下行添加到您的Podfile中
pod "SPLMessageLogger"
SPLMessageLogger可以拦截并记录所有objc消息。因此,它在NSObject
上定义了以下接口
@interface NSObject (SPLMessageLogger)
+ (instancetype)messageLogger;
@end
其中消息记录器记录它接收到的每一个消息,并挂钩到运行时以拦截和记录这些记录下来的消息。让我们看看一个例子
@interface SPLView : UIView
- (CGPoint)setCenter:(CGPoint)center atIndex:(NSInteger)index forObject:(id)object;
@end
@implementation SPLView
- (CGPoint)setCenter:(CGPoint)center atIndex:(NSInteger)index forObject:(id)object
{
[super setCenter:center];
return self.center;
}
@end
要拦截并记录到-[SPLView setCenter:atIndex:forObject:]
和-[UIView setCenter:]
的所有消息,只需运行
SPLView *firstMessageLogger = [SPLView messageLogger];
[firstMessageLogger setCenter:CGPointZero atIndex:0 forObject:nil];
UIView *secondMessageLogger = [UIView messageLogger];
[secondMessageLogger setCenter:CGPointZero];
在运行时,这将产生以下输出
-> <SPLView:0x166c41f0>: -[SPLView setCenter:NSPoint: {0.5, 0.5} atIndex:5 forObject:<ICAppDelegate: 0x166b0570>]
--> <SPLView:0x166c41f0>: -[UIView setCenter:NSPoint: {0.5, 0.5}]
==> void
=> NSPoint: {0.5, 0.5}
-> <SPLView:0x166c41f0>: -[UIView setCenter:NSPoint: {0.5, 0.5}]
=> void
-> <UINavigationBar:0x167a9960>: -[UIView setCenter:NSPoint: {384, 42}]
=> void
-> <UIView:0x167b7e40>: -[UIView setCenter:NSPoint: {384, 512}]
=> void
-> <UIImageView:0x167c56c0>: -[UIView setCenter:NSPoint: {0, 0}]
=> void
对于这个库,我已经写了一个定制的子程序跳板(您可以在这里阅读关于跳板的内容),可以将任何objc消息转发到新选择器。
IMP imp_implementationForwardingToSelector(SEL forwardingSelector, BOOL returnsAStructValue);
下面是一个例子
IMP forwardingImplementation = imp_implementationForwardingToSelector(@selector(setCenter:), NO);
class_addMethod([UIView class], @selector(thisSetCenterDoesNotExistYet:), forwardingImplementation, typeEndoding);
突然,所有的UIView
实例都响应-[UIView thisSetCenterDoesNotExistYet:]
并将此消息转发到-[UIView setCenter:]
。如果您想了解更多关于跳板的信息,或者可能是一篇关于“为初学者编写自定义跳板及所有陷阱”的博客文章,请通过Twitter联系我。
+[NSObject messageLogger]
返回一个实现了-[NSObject methodSignatureForSelector:]
并使用-[NSObject forwardInvocation:]
(类似于UIAppearance所使用的)作为进入点挂钩到运行时的SPLMessageLoggerRecorder
实例。运行时钩子用上面的例子最好解释
SPLView *firstMessageLogger = [SPLView messageLogger];
[firstMessageLogger setCenter:CGPointZero atIndex:0 forObject:nil];
一旦firstMessageLogger
收到setCenter:atIndex:forObject:
消息,它会执行以下操作
-[SPLView __SPLMessageLogger_original_SPLView_setCenter:atIndex:forObject:]
存储-[SPLView setCenter:atIndex:forObject:]
的原始实现imp_implementationForwardingToSelector
获得的新实现,该实现将转发到__SPLMessageLogger_SPLView_setCenter:atIndex:forObject:
。这里的诀窍是永不响应转发到-[SPLView __SPLMessageLogger_SPLView_setCenter:atIndex:forObject:]
的实例。在这些情况下,objc 运行时调用-[NSObject forwardingTargetForSelector:]
,对于这个选择器,它返回实际SPLMessageLogger
类的实例。然后,此实例接收__SPLMessageLogger_SPLView_setCenter:atIndex:forObject:
消息。它实现-[NSObject methodSignatureForSelector:]
,然后使用-[NSObject forwardInvocation:]
记录调用,然后将调用转发回原始对象的原始实现。
-[SPLView setCenter:] // gets forwarded
↓
-[SPLView __SPLMessageLogger_SPLView_setCenter:atIndex:forObject:] // no implementation is found
↓
-[SPLView forwardingTargetForSelector:] // return an instance of SPLMessageLogger
↓
-[SPLMessageLogger methodSignatureForSelector:] // lets the runtime construct an invocation
↓
-[SPLMessageLogger forwardInvocation:]
// 1) log the invocation
// 2) forward invocation
↓
---> `-[SPLView __SPLMessageLogger_original_SPLView_setCenter:atIndex:forObject:]` // execute original implementation
<--- `-[SPLView __SPLMessageLogger_original_SPLView_setCenter:atIndex:forObject:]`
// 3) log return argument of invocation
此方法使用编写在原始汇编语言中的自定义跳板,目前仅在i386、armv7、armv7s和arm64上提供。
Oliver Letterer
SPLMessageLogger可按照MIT许可使用。有关更多信息,请参阅LICENSE文件。