Home > Back-end >  Swizzling [NSObject initialize] method to log each class instantiation
Swizzling [NSObject initialize] method to log each class instantiation

Time:08-15

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:

  1. initialize doesn't fallback to the NSObject 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];
    ...
}
  1. 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 get NSValue 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).

  2. 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 use printf to log the fact of initialisation in my implementation.


Original Answer

The enter image description here

  • Related