Basically, I want to print each time a class object is instantiated. The following code shows the intent.
@interface NSObject (ILogger)
(void)initialize;
@end
@implementation NSObject (ILogger)
(void)initialize
{
NSLog(@"Initializing %s", class_getName([self class]));
}
@end
This does not work because NSObject
already has a initialize
method so this approach results in undefined behavior. The compiler also warns about the issue: warning: category is implementing a method which will also be implemented by its primary class
One idea would be to somehow swizzle [NSObject initialize]
and do the logging. How do I do that safely?
EDIT:
Maybe I'm using the wrong terminology but the goal is to know if a class is used at all in the app. If many objects of a class are created, there is no need to log every time, once is sufficient.
CodePudding user response:
After Edit Answer
You are correct about use of [NSObject initialize]
method for tracking the first use of a class. I don't know anything more appropriate for that. The swizzling would look like this:
#import <objc/runtime.h>
@implementation NSObject (InitializeLog)
(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getClassMethod(self, @selector(initialize));
Method swizzledMethod = class_getClassMethod(self, @selector(tdw_initializeLog));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
(void)tdw_initializeLog {
const char *className = class_getName(self);
printf("Initializing %s\n", className);
[self tdw_initializeLog];
}
@end
There are a few things to be advised about:
initialize
doesn't fallback to theNSObject
implementation (which is swizzled above) if derived classes have this method implemented AND don't have[super initialize];
called. Thus for any custom class inherited from Cocoa classes either don't implement this method OR call[super initialize];
somewhere in your implementation:
(void)initialize {
[super initialize];
...
}
Cocoa classes are rarely as straightforward as they look like. Quite a lot of interfaces and classes are hidden under the same name and sometimes the logs will be somewhat misleading (e.g. in place of
NSNumber
you will getNSValue
class reported). Thus, take any logging out of Foundation classes with a grain of salt and always double-check where it comes from (also be ready that those classes won't be reported at all).First use of
NSLog
also triggers some classes to initialise themselves and it make them to call[NSObject initialize]
. In order to avoid an infinite loop or bad_access errors I decided to useprintf
to log the fact of initialisation in my implementation.