我想设计一个类(TrackingClass
),负责跟踪对其他类(TrackedClass
)的某些方法的调用,即设置从我的方法调整的方法了解。
因此,假设我使用@selectors
我感兴趣的TrackedClass
实例方法加载一个数组。
这是我想要运行的伪代码:
@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
。我可以实现吗?
我用一些新信息更新了代码,我越来越近了吗?
如果人们想要动态尝试他们的想法,我会在Demo应用程序中设置一个Github存储库。 资料来源:BCTrackingClass on Github
我终于想出了一个工作版本的代码(参见Github repo,或者就在上面)。我的下一个问题是:我希望我的类基于实例(目前,我的所有方法都是类方法),因此我可以为类的实例分配属性@property NSMutableDictionnary*
以进行调用日志记录。
我不知道如何实现这一目标。任何想法?
答案 0 :(得分:0)
您是否要为该类的所有对象的所有实例执行此操作? 对于一些选择者或所有选择者? ...
如果您想要跟踪特定实例,那么最简单的方法是使用isa swizzling,或多或少地执行此操作(代码绝对未经测试)
@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 swizzling。
可能有一种方法可以将调用转发到原始类,而不会撤消isa调配,但我认为我太懒了,无法搜索它。