Ytrace 0.2.0

Ytrace 0.2.0

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最后发布2014年12月

Oliver Letterer维护。



Ytrace 0.2.0

SPLMessageLogger

License Badge

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:]的原始实现
  • 将《-[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文件。