Another possible use of +initialize
is for Method Swizzling. Which you shouldn't really use unless you're sure you know what you're doing. Read this SO question for more details.
It allows you to substitute an existing method implementation with your own, and still be able to call the original one. For instance, for faking NSDate in unit tests you could write something like this (note, that there are other ways to do this (OCMock, etc.), this is just an example). This code allows you to set a program-wide fake NSDate
, which will be returned whenever [NSDate date]
is called. And if no fake date is set, then the original implementation is used.
#import "NSDate+UnitTest.h"
#import "MethodSwizzling.h"
@implementation NSDate(UnitTest)
static NSDate *fakeCurrentDate = nil;
+(void)setFakeCurrentDate:(NSDate *)date
{
fakeCurrentDate = date;
}
+(NSDate *)fakeCurrentDate
{
if (fakeCurrentDate) {
return fakeCurrentDate;
}
else {
NSDate *result = [self fakeCurrentDate];
return result;
}
}
+(void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"Swizzling...");
SwizzleClassMethod([self class], @selector(date), @selector(fakeCurrentDate));
});
}
@end
//MethodSwizzling.m:
void SwizzleMethod(Class c, SEL orig, SEL new, BOOL isClassMethod) {
NSLog(@"Swizzling %@ method %@ of class %@ with fake selector %@.",
(isClassMethod ? @"a class" : @"an instance"),
NSStringFromSelector(orig),
NSStringFromClass(c),
NSStringFromSelector(new));
Method origMethod = isClassMethod ? class_getClassMethod(c, orig) : class_getInstanceMethod(c, orig);
Method newMethod = isClassMethod ? class_getClassMethod(c, new) : class_getInstanceMethod(c, new);
method_exchangeImplementations(origMethod, newMethod);
//Actually, it's better to do it using C-functions instead of Obj-C methods and
//methos_setImplementation instead of method_exchangeImplementation, but since this
//is not an open-source project and these components aren't going to be used by other people,
//it's fine. The problem is that method_exchangeImplementations will mess things up if
//the implementation relies on a fact that the selector passed as a _cmd parameter
//matches the function name.
//More about it: http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
}
void SwizzleClassMethod(Class c, SEL orig, SEL new) {
SwizzleMethod(c, orig, new, YES);
}
void SwizzleInstanceMethod(Class c, SEL orig, SEL new) {
SwizzleMethod(c, orig, new, NO);
}