0

Yet another method swizzling question, not asked before.

I'd like to monitor all release calls in my application, thus I decided to use method swizzling, on NSObject, so that it would be possible to monitor of all classes. The actual code has been taken from here, but I'll also append it here for clarity.

It seems that, although all (most?) of the cases work, this doesn't work for UIView and its children.

I was expecting that, even if UIView overrides this method, or even if swizzles it, at the end of the day this method will end up at the original release method, thus through swizzling my code will be executed.

If I put swizzling on the UIView class and on the NSObject class, then swizzling works perfect. Also Swizzling works fine in UIResponder classes if I put it on NSOBject. It seems that UIView has some kinf of custom implementation that breaks the chain?

Here is the code I am using:

#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation NSObject (SwizzleRelease)

- (void)xxx_release {
    printf("Will release %s #%d\n", [NSStringFromClass([self class]) UTF8String], [self retainCount]);
    [self xxx_release];
}

+ (void)load {
    NSLog(@"LOADED");
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(release);
        SEL swizzledSelector = @selector(xxx_release);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

@end
Panayotis
  • 1,792
  • 23
  • 32
  • 1
    Use instruments and its allocation tracking for this, not swizzling. Down this path lies madness. – Richard J. Ross III Oct 11 '17 at 21:30
  • 1
    Also, in ARC-managed code, `-release` isn't usually called. The fast path in `objc_release` checks a classes metadata (once, if memory serves) when the binary is loaded and determines at that point if that class has custom retain/release logic. Since UIView on the load of the class into the objc runtime doesn't have custom retain-release logic, your swizzling doesn't effect the hot-path that's set in the classes' metadata. Its theoretically possible to disable the hot path in objc_release/objc_retain, (there might even be an env variable for this?) but again, this is madness. **SWIM AWAY**. – Richard J. Ross III Oct 11 '17 at 21:32
  • 1
    Further reading: see https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html, line 527. – Richard J. Ross III Oct 11 '17 at 21:39
  • It's not for debugging, it is for actual runtime monitor which couldn't be performed otherwise... And it is not ARC enabled. If I apply this technique to both UIView and NSObject it works. When it's only in NSObject it doesn't work for UIView. I have updated the question to reflect this. – Panayotis Oct 11 '17 at 22:58
  • About the chain you pointed at, I am sorry I didn't really get it. Is it a way to call release from C++? – Panayotis Oct 11 '17 at 23:02
  • 1
    Any callsites that use ARC (which includes all of UIKit, by the way) will not call through to `-release`, instead, they go through `objc_release`, which calls through to the C++ code path I linked. – Richard J. Ross III Oct 11 '17 at 23:07
  • 1
    "it is for actual runtime monitor which couldn't be performed otherwise." It can't be performed. There is no way to make this reliable. You're relying on extremely implementation-specific details, that even if you get to "work" in some cases, can break at any time. There are parts of Cocoa that cheat about retain counts and does stuff like "if I'm the only one holding this thing, then just destroy it, don't release it." That's insanely dangerous for our code, but Apple does that kind of stuff internally because they know no one is under them. You will need to reframe your problem another way. – Rob Napier Oct 12 '17 at 00:20
  • (Or you'll need to treat this as unreliable debugging code and see what you can happen to get to work from release to release. I'd write a lot of test cases. Low-level details change more often than you might imagine.) – Rob Napier Oct 12 '17 at 00:24

0 Answers0