我想设计一个类(TrackingClass
),负责跟踪对另一个类(TrackedClass
)的某些方法的调用,即设置从我理解的方法。
因此,假设我加载了一个数组,其中包含我感兴趣的TrackedClass
@selectors
实例方法。这是我想运行的伪代码:
@implementation BCTrackedClass
-(void)doA
{
}
@end
和
@implementation BCTrackingClass
#import "BCTrackingClass.h"
#import "BCTrackedClass.h"
#include <objc/runtime.h>
#include <objc/objc-runtime.h>
@implementation BCTrackingClass
void myMethodIMP(id self, SEL _cmd);
void myMethodIMP(id self, SEL _cmd)
{
//NSLog(@"_cmd : %@",NSStringFromSelector(_cmd));
[BCTrackingClass logCallForMethod:NSStringFromSelector(_cmd)];
objc_msgSend(self,
NSSelectorFromString([NSString stringWithFormat:@"tracked%@",NSStringFromSelector(_cmd)]));
}
+(void)setUpTrackingForClass:(Class)aClass andMethodArray:(NSArray*)anArray //Array of selectorsStrings of methods to track
{
for (NSString* selectorString in anArray)
{
SEL selector = NSSelectorFromString(selectorString);
SEL trackedSelector = NSSelectorFromString([NSString stringWithFormat:@"tracked%@",selectorString]);
class_addMethod(aClass,
trackedSelector,
(IMP) myMethodIMP, "v@:");
//Swizzle the original method with the tracked one
Method original = class_getInstanceMethod(aClass,
selector);
Method swizzled = class_getInstanceMethod(aClass,
trackedSelector);
method_exchangeImplementations(original, swizzled);
}
}
+(void)logCallForMethod:(NSString*)aSelectorString
{
NSLog(@"%@",aSelectorString);
}
@end
从理论上讲,我只是缺少可以有效地创建这个新实例方法的代码trackedSelector
。我能做到吗?
编辑
我用一些新信息更新了代码,我越来越近了吗?
编辑 2
我设置了一个带有演示应用程序的 Github 存储库,如果人们想动态尝试他们的想法。来源 : Github 上的 BCTrackingClass
编辑 3
我终于想出了一个代码的工作版本(参见Github存储库,或上面)。我的下一个问题是:我希望我的类是基于实例的(目前,我所有的方法都是类方法),以便我可以将属性@property NSMutableDictionnary*
分配给类的实例以进行调用记录。我不知道如何实现这一目标。有没有 ?
是否要对该类的所有对象的所有实例执行此操作?对于某些选择器还是所有选择器?...
如果你想要的是跟踪特定的实例,那么最简单的途径是使用isa旋转,或多或少地这样做(代码绝对未经测试)
@interface ClassTracker
+ (void)trackObject:(id)object;
@end
static const char key;
@implementation ClassTracker
+ (void)trackObject:(id)object
{
objc_setAssociatedObject(object, &key, [object class], OBJC_ASSOCIATION_ASSIGN);
object_setClass(object, [ClassTracker class]);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
Class aClass = objc_getAssociatedObject(self, &key);
return [aClass instanceMethodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
Class aClass = objc_getAssociatedObject(self, &key);
// do your tracing here
object_setClass(self, aClass);
[invocation invoke];
object_setClass(self, [ClassTracker class]);
}
// dealloc is magical in the sense that you really want to undo your hooking
// and not resume it ever!
- (void)dealloc
{
Class aClass = objc_getAssociatedObject(self, &key);
object_setClass(self, aClass);
[self dealloc];
}
@end
如果它用于逆向工程或调试目的,那应该(稍作调整)就可以了。
如果您打算快速,则必须执行实例方法重排,了解它们的类型等。
我的"解决方案"有一个缺点,它只会跟踪输入调用,如果选择器调用其他调用,IOW,因为 isa 切换暂停以递归调用,那么在恢复 isa 切换之前,您看不到新的呼叫。
可能有一种方法可以将调用转发到原始类,而无需撤消 isa 滑动,但我认为我懒得搜索它。