如果派生类实现了这个方法并且没有调用[super initialize];,initialize 不会回退到NSObject 实现(上面已经打乱了)。因此,对于从 Cocoa 类继承的任何自定义类,要么不实现此方法,要么在实现中的某处调用 [super initialize];:
+ (void)initialize {
[super initialize];
...
}
-
Cocoa 类很少像看起来那样简单。相当多的接口和类隐藏在同一个名称下,有时日志会有些误导(例如,代替NSNumber,您将得到NSValue 类报告)。因此,对任何从基金会课程中登出的人都持保留态度,并始终仔细检查它的来源(也准备好根本不会报告这些课程)。
-
第一次使用NSLog 也会触发一些类来初始化自己,并让它们调用+[NSObject initialize]。为了避免无限循环或 bad_access 错误,我决定使用printf 在我的实现中记录初始化的事实。
原始答案
+ (void)initialize 方法与对象实例化关系不大,因为每个 Objective-C 都会调用它班级在您的客户端代码第一次使用它之前不久。如果给定类的子类没有实现此方法并且以后永远不会调用它,则它可能会被多次调用。因此,如果您想跟踪,这只是一个糟糕的选择对象实例化。
但是,您可能仍然需要使用一些选项来跟踪对象实例化的情况。
热血沸腾-[NSObject init]
首先,我会考虑NSObject 的init 方法:
#import <objc/runtime.h>
@implementation NSObject (InitLog)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod(self, @selector(init));
Method swizzledMethod = class_getInstanceMethod(self, @selector(initLog_tdw));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (instancetype)initLog_tdw {
self = [self initLog_tdw];
if (self) {
const char *className = class_getName([self class]);
NSLog(@"Instantiating %s", className);
}
return self;
}
@end
只要实例回退到-[NSObject init] 方法,它就可以正常工作。不幸的是,很多 Cocoa 类都没有这样做。考虑以下场景:
NSObject *obj = [NSObject new]; // NSLog prints "Instantiating NSObject"
NSString *hiddenStr = [[NSMutableString alloc] initWithString:@"Test"]; // NSLog is silent
NSURL *url = [[NSURL alloc] initWithString:@"http://www.google.com"]; // NSLog is silent
-[NSURL initWithString:] 和 -[NSMutableString initWithString:] 以某种方式避免了 NSObject 的默认构造函数被调用。它仍然适用于没有任何花哨初始化的任何自定义类:
@implementation TDWObject
- (instancetype)initWithNum:(int)num {
self = [super init];
if (self) {
_myNum = num;
}
return self;
}
@end
TDWObject *customObj = [TDWObject new]; // NSLog prints "Instantiating TDWObject"
TDWObject *customObjWithNum = [[TDWObject alloc] initWithNum:2]; // NSLog prints "Instantiating TDWObject"
热血沸腾+[NSObject alloc]
或者,您可以调整 alloc 方法:
#import <objc/runtime.h>
@implementation NSObject (AllocLog)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getClassMethod(self, @selector(alloc));
Method swizzledMethod = class_getClassMethod(self, @selector(tdw_allocLog));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
+ (instancetype)tdw_allocLog {
id allocatedObject = [self tdw_allocLog];
if (allocatedObject) {
const char *className = class_getName([allocatedObject class]);
NSLog(@"Allocating %s", className);
}
return allocatedObject;
}
@end
它将拦截几乎所有 Cocoa 类的实例化(例外必须是一些结构方法,在这些方法中会发生特定于类的优化,例如 +[NSNumber numberWith..] 方法族),但还有其他问题需要注意。从alloc 方法返回的分配实例并不总是那么简单。例如。对于NSMutableString 上面的示例NSLog 将打印NSPlaceholderMutableString:
TDWObject *customObj = [TDWObject new]; // NSLog prints "Allocating TDWObject"
TDWObject *customObjWithNum = [[TDWObject alloc] initWithNum:2]; // NSLog prints "Allocating TDWObject"
NSObject *obj = [NSObject new]; // NSLog prints "Allocating NSObject"
NSString *hiddenStr = [[NSMutableString alloc] initWithString:@"Test"]; // NSLog prints "Allocating NSPlaceholderMutableString"
NSURL *url = [[NSURL alloc] initWithString:@"http://www.google.com"]; // NSLog prints "Allocating NSURL"
这是因为 Foundation 框架 uses Class Cluster design pattern heavily 和 alloc 返回的实例通常是某种抽象工厂,后来 Cocoa 类利用它们来生成所请求类的具体实例。
这两种方法都有自己的缺点,但我很难想出更简洁和可靠的方法。