Find where object is retained with ARC

后端 未结 5 1463
逝去的感伤
逝去的感伤 2020-12-07 12:03

I have an object that is being retained more than necessary (most likely due to a property that is strong instead of weak). Big codebase, so it\'s

相关标签:
5条回答
  • 2020-12-07 12:45

    I managed to find the offending retain by doing the following:

    1. Temporarily add -fno-objc-arc to the object class Compiler Flags to disable ARC for that class.
    2. Temporarily override retain (just call super) and put a breakpoint on it.
    3. Debug and check the call stack each time retain is called.
    0 讨论(0)
  • 2020-12-07 12:46

    Last week I was helping some friends debug leaks in their ARC project. Some tips:

    1/ Build for Profiling and start Instruments with Leak Detection. Then explore the currently allocated objects, find the object you want (you can sort them by name) and look into its retain/release history. Note that retain count is not very helpful with ARC. You have to check it manually step by step.

    Try to comment all the code which could be the source of leak and then uncomment it step by step.

    2/ Put a NSLog into your init and into your dealloc to watch when the object is created and destroyed.

    3/ Don't look only to property definitions, watch if property setters are implemented manually. I found a problem in my friends' project looking like this:

    @property (weak, nonatomic) id<...> delegate;
    
    @interface ... {
        id<...> _delegate;
    }
    
    @synthesize delegate = _delegate;
    
    - (void)setDelegate(id<...>)delegate {
        _delegate = delegate;  //with ARC this retains the object!
    }
    0 讨论(0)
  • 2020-12-07 12:57

    This solution was somewhat helpful for me. It basically uses method swizzling to tricks the ARC compiler into thinking you're not overriding retain and release.

        + (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = [self class];
    
        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
    
        SEL originalSelector1 = NSSelectorFromString(@"retain");
        SEL swizzledSelector1 = NSSelectorFromString(@"myretain");
    
        SEL originalSelector2 = NSSelectorFromString(@"release");
        SEL swizzledSelector2 = NSSelectorFromString(@"myrelease");
    
        Method originalMethod1 = class_getInstanceMethod(cls, originalSelector1);
        Method swizzledMethod1 = class_getInstanceMethod(cls, swizzledSelector1);
        Method originalMethod2 = class_getInstanceMethod(cls, originalSelector2);
        Method swizzledMethod2 = class_getInstanceMethod(cls, swizzledSelector2);
    
        BOOL didAddMethod1 =
        class_addMethod(cls,
                        originalSelector1,
                        method_getImplementation(swizzledMethod1),
                        method_getTypeEncoding(swizzledMethod1));
    
        if (didAddMethod1) {
            class_replaceMethod(cls,
                                swizzledSelector1,
                                method_getImplementation(originalMethod1),
                                method_getTypeEncoding(originalMethod1));
        } else {
            method_exchangeImplementations(originalMethod1, swizzledMethod1);
        }
    
        BOOL didAddMethod2 =
        class_addMethod(cls,
                        originalSelector2,
                        method_getImplementation(swizzledMethod2),
                        method_getTypeEncoding(swizzledMethod2));
    
        if (didAddMethod2) {
            class_replaceMethod(cls,
                                swizzledSelector2,
                                method_getImplementation(originalMethod2),
                                method_getTypeEncoding(originalMethod2));
        } else {
            method_exchangeImplementations(originalMethod2, swizzledMethod2);
        }
    
    
    });
    }
    
    -(id)myretain {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    NSLog(@"tracking retain now %d",(int)[self performSelector:NSSelectorFromString(@"retainCount")]);
    SEL selector = NSSelectorFromString(@"myretain");
    return [self performSelector:selector withObject:nil];
    #pragma clang diagnostic pop
    }
    
    -(id)myrelease {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    NSLog(@"tracking release now %d",(int)[self performSelector:NSSelectorFromString(@"retainCount")]);
    
    SEL selector = NSSelectorFromString(@"myrelease");
    return [self performSelector:selector withObject:nil];
    #pragma clang diagnostic pop
    

    }

    0 讨论(0)
  • 2020-12-07 12:58

    To track growth of an application, Heapshot Analysis has proven very effective. It will capture both true leaks and accretion of memory where the allocations are not accounted for by leaks.

    You can see all of the retain/release events, and their backtrace, using the Allocations instrument. Hit the little (i) button on the Allocations instrument and turn on "Record reference counts". Turning on "Only track active allocations" reduces the amount of data collected by Instruments, making it snappier (and dead allocations aren't really useful in this context, but can be in others).

    With that, you can dive into any allocation (by clicking on the right-arrow in the address field), see all the retain/release events and see exactly where they occurred.

    enter image description here

    0 讨论(0)
  • 2020-12-07 13:10

    If you are using ARC you would never get the option to add retain,

    secreenshot1

    and if you have converted the project to ARC using below option, you would be prompted with error

    screenshot2

    If you have set the property as strong, then you should allocate the object once through the whole project e.g. self.yourobject = [[NSMutableArray alloc]init];. There is no shortcut for this.

    0 讨论(0)
提交回复
热议问题