2

I would like to swizzle an object method after the object has been created.

Example:

@interface MyObj: NSObject
-(NSString*) foo;
-(NSString*) bar;
@end

@implementation MyObj
-(NSString*) foo {return @"foo";}
-(NSString*) bar {return @"bar";}
@end

void Swizzle(object, SEL source, SEL, dest);

...

MyObj* obj = [[[MyObj alloc] init] autorelease];

NSLog(@"%@", [obj foo]);
Swizzle(obj, @selector(foo), @selector(bar));
NSLog(@"%@", [obj foo]);

Output:

foo
bar

I have looked at lots of examples of swizzling before an object is created. However as you can see I want to swizzle after an object is created.

This is because I want to track objects that are going to release (like NSThread). I don't want to swizzle NSObject dealloc because that seems overkill. I would rather swizzle only the objects I am trying to track.

Community
  • 1
  • 1
Peter
  • 83
  • 6
  • Possible duplicate of [How can I make every message an object receives thread-safe?](http://stackoverflow.com/questions/8763028/how-can-i-make-every-message-an-object-receives-thread-safe). I know it doesn't appear to be, but look at the answer, it easily extends to -release, if you need it to. – Richard J. Ross III Mar 30 '12 at 19:54

3 Answers3

3

This honestly seems like an ill-advised attempt to avoid using Instruments. If so, the correct answer is "Just use Instruments."

But to actually answer the question: You can't do this with individual objects. Methods — even instance methods — are defined per-class, not per-object. The only way to override a method on a per-object basis is to dynamically create a subclass of the object's class that overrides the method you're interested in and isa-swizzle the object so it uses the subclass's lookup table. (This is basically how KVO does its magic notifications under the hood.) Again, this is a bit of a hack and generally a lot more trouble than it is worth, so before you do this, I would really think twice (or three times) about whether this is really necessary. For almost any use case I can come up with, there's a cleaner way to accomplish the same goal that does not involve runtime voodoo.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • I am not trying to use Instruments. I want to track the destruction of certain objects so I can perform cleanup. I don't want to subclass the objects class because I am trying to make my functionality not depend on sub classing.Example: NSThread's are created and I want to provide a new object for that thread to access. Once the thread is dead I want to remove this object. The users of this class do not need to manege the threads object because the object is aware when it needs to be removed. – Peter Mar 30 '12 at 20:50
  • @PeterGulyas: For that case, it sounds like you want an NSThread subclass that handles creating and destroying this resource. – Chuck Mar 30 '12 at 20:51
  • I want the ability to track the execution of any method of any object (not just dealloc). It would be nice to know when layoutSubviews and so are called without having to subclass. Yes subclassing is the simple solution but I want something nasty! – Peter Mar 30 '12 at 20:58
  • 1
    @PeterGulyas: Hey, it's your codebase to mess up. My second paragraph tells you how to do it. I just don't feel right telling people how to do something without explaining when it's appropriate. – Chuck Mar 30 '12 at 21:05
1

You cannot swizzle an object, only a specific class. Also, the implementation for a selector is cached, and that cache might not be cleared automatically when you swizzle, which would mean the new method is not used.

I would suggest overriding (or swizzling) dealloc in the class you want to track, and use a property to determine if your tracking code should be performed.

@property (nonatomic) BOOL shouldTrackDealloc;

@synthesize shouldTrackDealloc;
- (void)dealloc {
    if(self.shouldTrackDealloc) {
        NSLog(@"dealloc of %@",self);
    }
    ...
}

If you are tracking framework classes, you won't be able to use @synthesize, since the instance variable won't be in the right class, but you can use associated references instead.

ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123
0

The solution I ended up with was to use objc_setAssociatedObject(thread, key, object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

That way I could detect when the thread was being released and do the cleanup I needed.

Peter
  • 83
  • 6