-2

I am trying to method swizzling for NSTimer in a category. But the swizzling method swizzling_invalidate is never call when send message invalidate to NSTimer object.

#import "NSTimer+Test.h"
#import <objc/runtime.h>

@implementation NSTimer (Test)

+ (void)load {
    Method originalMethod, swizzlingMethod;
    originalMethod = class_getInstanceMethod(self, @selector(invalidate));
    swizzlingMethod = class_getInstanceMethod(self, @selector(swizzling_invalidate));
    method_exchangeImplementations(originalMethod, swizzlingMethod);
}

- (void)swizzling_invalidate {
    [self swizzling_invalidate];
}

@end

It is so strange that method swizzling for other NSObject is working. Like that:

#import "NSUserDefaults+Timing.h"
#import <objc/runtime.h>

@implementation NSUserDefaults (Timing)

+ (void)load {
    Method original, swizzling;
    original = class_getInstanceMethod([self class], @selector(synchronize));
    swizzling = class_getInstanceMethod([self class], @selector(swizzling_synchronize));
    method_exchangeImplementations(original, swizzling);
}

- (BOOL)swizzling_synchronize {
    NSDate *start = [NSDate date];
    BOOL returnValue = [self swizzling_synchronize];
    NSLog(@"%f", [[NSDate date] timeIntervalSinceDate:start]);
    return returnValue;
}

@end
yehuanwen
  • 13
  • 3
  • 2
    And where is this code located? – Caleb Jul 14 '14 at 08:01
  • What exactly is not working? Is `load` not called or `swizzling_invalidate`? How did notice the error? What did you try? – Nikolai Ruhe Jul 14 '14 at 08:05
  • In NSTimer category, I have try method swizzling on other NSObject, it work. It is strange method swizzling not working on NSTimer selector. @Caleb – yehuanwen Jul 14 '14 at 08:06
  • `swizzling_invalidate ` is not calling when `[timer invalidate]`.@NikolaiRuhe – yehuanwen Jul 14 '14 at 08:07
  • 2
    What are you trying to achieve by swizzling `invalidate`? Perhaps there is a better way to achieve the same result. – mttrb Jul 14 '14 at 08:57
  • Because I want to remove associated object for NSTimer when `invalidate`. Any ideas to run my own code after `invalidate`? @mttrb – yehuanwen Jul 14 '14 at 09:04

2 Answers2

4

The NSTimer documentation says:

You should not attempt to subclass NSTimer.

This almost certainly means that the internals of NSTimer are not simple and that method swizzling will be tricky.

I suspect what this actually means is that NSTimer is implemented as a class cluster or something similar. The fact that NSTimer is toll-free bridged to Core Foundation strongly suggests it might be.

This article, Playing with NSTimer, suggests that NSTimer is actually an abstract class.

mttrb
  • 8,297
  • 3
  • 35
  • 57
0

You have to make sure that your load method is called only once. Otherwise the loading of NSTimer subclasses would switch swizzling back to the original implementation.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • It is called only once. I can add `dispatch_once` to make sure it is called once. But it won't worked either. – yehuanwen Jul 14 '14 at 08:10