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 hard to find where.
How can I find all the lines in which this object is being retained when using ARC?
If I weren't using ARC I guess I could simply override retain and check from where it's called. Can I do something similar with ARC?
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.
I managed to find the offending retain by doing the following:
Temporarily add -fno-objc-arc to the object class Compiler Flags
to disable ARC for that class.
Temporarily override retain (just call super) and put a breakpoint on it.
Debug and check the call stack each time retain is called.
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!
}
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.
#import <objc/runtime.h>
...
+ (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 %#",#((uintptr_t)[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 %#", #((uintptr_t)[self performSelector:NSSelectorFromString(#"retainCount")]));
SEL selector = NSSelectorFromString(#"myrelease");
return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}
If you are using ARC you would never get the option to add retain,
and if you have converted the project to ARC using below option, you would be prompted with error
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.
Related
I'm getting the following warning by the ARC compiler:
"performSelector may cause a leak because its selector is unknown".
Here's what I'm doing:
[_controller performSelector:NSSelectorFromString(#"someMethod")];
Why do I get this warning? I understand the compiler can't check if the selector exists or not, but why would that cause a leak? And how can I change my code so that I don't get this warning anymore?
Solution
The compiler is warning about this for a reason. It's very rare that this warning should simply be ignored, and it's easy to work around. Here's how:
if (!_controller) { return; }
SEL selector = NSSelectorFromString(#"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);
Or more tersely (though hard to read & without the guard):
SEL selector = NSSelectorFromString(#"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);
Explanation
What's going on here is you're asking the controller for the C function pointer for the method corresponding to the controller. All NSObjects respond to methodForSelector:, but you can also use class_getMethodImplementation in the Objective-C runtime (useful if you only have a protocol reference, like id<SomeProto>). These function pointers are called IMPs, and are simple typedefed function pointers (id (*IMP)(id, SEL, ...))1. This may be close to the actual method signature of the method, but will not always match exactly.
Once you have the IMP, you need to cast it to a function pointer that includes all of the details that ARC needs (including the two implicit hidden arguments self and _cmd of every Objective-C method call). This is handled in the third line (the (void *) on the right hand side simply tells the compiler that you know what you're doing and not to generate a warning since the pointer types don't match).
Finally, you call the function pointer2.
Complex Example
When the selector takes arguments or returns a value, you'll have to change things a bit:
SEL selector = NSSelectorFromString(#"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
func(_controller, selector, someRect, someView) : CGRectZero;
Reasoning for Warning
The reason for this warning is that with ARC, the runtime needs to know what to do with the result of the method you're calling. The result could be anything: void, int, char, NSString *, id, etc. ARC normally gets this information from the header of the object type you're working with.3
There are really only 4 things that ARC would consider for the return value:4
Ignore non-object types (void, int, etc)
Retain object value, then release when it is no longer used (standard assumption)
Release new object values when no longer used (methods in the init/ copy family or attributed with ns_returns_retained)
Do nothing & assume returned object value will be valid in local scope (until inner most release pool is drained, attributed with ns_returns_autoreleased)
The call to methodForSelector: assumes that the return value of the method it's calling is an object, but does not retain/release it. So you could end up creating a leak if your object is supposed to be released as in #3 above (that is, the method you're calling returns a new object).
For selectors you're trying to call that return void or other non-objects, you could enable compiler features to ignore the warning, but it may be dangerous. I've seen Clang go through a few iterations of how it handles return values that aren't assigned to local variables. There's no reason that with ARC enabled that it can't retain and release the object value that's returned from methodForSelector: even though you don't want to use it. From the compiler's perspective, it is an object after all. That means that if the method you're calling, someMethod, is returning a non object (including void), you could end up with a garbage pointer value being retained/released and crash.
Additional Arguments
One consideration is that this is the same warning will occur with performSelector:withObject: and you could run into similar problems with not declaring how that method consumes parameters. ARC allows for declaring consumed parameters, and if the method consumes the parameter, you'll probably eventually send a message to a zombie and crash. There are ways to work around this with bridged casting, but really it'd be better to simply use the IMP and function pointer methodology above. Since consumed parameters are rarely an issue, this isn't likely to come up.
Static Selectors
Interestingly, the compiler will not complain about selectors declared statically:
[_controller performSelector:#selector(someMethod)];
The reason for this is because the compiler actually is able to record all of the information about the selector and the object during compilation. It doesn't need to make any assumptions about anything. (I checked this a year a so ago by looking at the source, but don't have a reference right now.)
Suppression
In trying to think of a situation where suppression of this warning would be necessary and good code design, I'm coming up blank. Someone please share if they have had an experience where silencing this warning was necessary (and the above doesn't handle things properly).
More
It's possible to build up an NSMethodInvocation to handle this as well, but doing so requires a lot more typing and is also slower, so there's little reason to do it.
History
When the performSelector: family of methods was first added to Objective-C, ARC did not exist. While creating ARC, Apple decided that a warning should be generated for these methods as a way of guiding developers toward using other means to explicitly define how memory should be handled when sending arbitrary messages via a named selector. In Objective-C, developers are able to do this by using C style casts on raw function pointers.
With the introduction of Swift, Apple has documented the performSelector: family of methods as "inherently unsafe" and they are not available to Swift.
Over time, we have seen this progression:
Early versions of Objective-C allow performSelector: (manual memory management)
Objective-C with ARC warns for use of performSelector:
Swift does not have access to performSelector: and documents these methods as "inherently unsafe"
The idea of sending messages based on a named selector is not, however, an "inherently unsafe" feature. This idea has been used successfully for a long time in Objective-C as well as many other programming languages.
1 All Objective-C methods have two hidden arguments, self and _cmd that are implicitly added when you call a method.
2 Calling a NULL function is not safe in C. The guard used to check for the presence of the controller ensures that we have an object. We therefore know we'll get an IMP from methodForSelector: (though it may be _objc_msgForward, entry into the message forwarding system). Basically, with the guard in place, we know we have a function to call.
3 Actually, it's possible for it to get the wrong info if declare you objects as id and you're not importing all headers. You could end up with crashes in code that the compiler thinks is fine. This is very rare, but could happen. Usually you'll just get a warning that it doesn't know which of two method signatures to choose from.
4 See the ARC reference on retained return values and unretained return values for more details.
In the LLVM 3.0 compiler in Xcode 4.2 you can suppress the warning as follows:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.ticketTarget performSelector: self.ticketAction withObject: self];
#pragma clang diagnostic pop
If you're getting the error in several places, and want to use the C macro system to hide the pragmas, you can define a macro to make it easier to suppress the warning:
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
You can use the macro like this:
SuppressPerformSelectorLeakWarning(
[_target performSelector:_action withObject:self]
);
If you need the result of the performed message, you can do this:
id result;
SuppressPerformSelectorLeakWarning(
result = [_target performSelector:_action withObject:self]
);
My guess about this is this: since the selector is unknown to the compiler, ARC cannot enforce proper memory management.
In fact, there are times when memory management is tied to the name of the method by a specific convention. Specifically, I am thinking of convenience constructors versus make methods; the former return by convention an autoreleased object; the latter a retained object. The convention is based on the names of the selector, so if the compiler does not know the selector, then it cannot enforce the proper memory management rule.
If this is correct, I think that you can safely use your code, provided you make sure that everything is ok as to memory management (e.g., that your methods do not return objects that they allocate).
In your project Build Settings, under Other Warning Flags (WARNING_CFLAGS), add
-Wno-arc-performSelector-leaks
Now just make sure that the selector you are calling does not cause your object to be retained or copied.
As a workaround until the compiler allows overriding the warning, you can use the runtime.
You need header:
#import <objc/message.h>
Then try below:
// For strict compilers.
((id(*)(id,SEL))objc_msgSend)(_controller, sel_getUid("someMethod"));
OR
// Old answer's code:
objc_msgSend(_controller, NSSelectorFromString(#"someMethod"));
Instead of:
[_controller performSelector:NSSelectorFromString(#"someMethod")];
To ignore the error only in the file with the perform selector, add a #pragma as follows:
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
This would ignore the warning on this line, but still allow it throughout the rest of your project.
Strange but true: if acceptable (i.e. result is void and you don't mind letting the runloop cycle once), add a delay, even if this is zero:
[_controller performSelector:NSSelectorFromString(#"someMethod")
withObject:nil
afterDelay:0];
This removes the warning, presumably because it reassures the compiler that no object can be returned and somehow mismanaged.
Here is an updated macro based on the answer given above. This one should allow you to wrap your code even with a return statement.
#define SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(code) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
code; \
_Pragma("clang diagnostic pop") \
SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(
return [_target performSelector:_action withObject:self]
);
This code doesn't involve compiler flags or direct runtime calls:
SEL selector = #selector(zeroArgumentMethod);
NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation invoke];
NSInvocation allows multiple arguments to be set so unlike performSelector this will work on any method.
Well, lots of answers here, but since this is a little different, combining a few answers I thought I'd put it in. I'm using an NSObject category which checks to make sure the selector returns void, and also suppresses the compiler warning.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Debug.h" // not given; just an assert
#interface NSObject (Extras)
// Enforce the rule that the selector used must return void.
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object;
- (void) performVoidReturnSelector:(SEL)aSelector;
#end
#implementation NSObject (Extras)
// Apparently the reason the regular performSelect gives a compile time warning is that the system doesn't know the return type. I'm going to (a) make sure that the return type is void, and (b) disable this warning
// See http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown
- (void) checkSelector:(SEL)aSelector {
// See http://stackoverflow.com/questions/14602854/objective-c-is-there-a-way-to-check-a-selector-return-value
Method m = class_getInstanceMethod([self class], aSelector);
char type[128];
method_getReturnType(m, type, sizeof(type));
NSString *message = [[NSString alloc] initWithFormat:#"NSObject+Extras.performVoidReturnSelector: %#.%# selector (type: %s)", [self class], NSStringFromSelector(aSelector), type];
NSLog(#"%#", message);
if (type[0] != 'v') {
message = [[NSString alloc] initWithFormat:#"%# was not void", message];
[Debug assertTrue:FALSE withMessage:message];
}
}
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Since the selector (aSelector) is returning void, it doesn't make sense to try to obtain the return result of performSelector. In fact, if we do, it crashes the app.
[self performSelector: aSelector withObject: object];
#pragma clang diagnostic pop
}
- (void) performVoidReturnSelector:(SEL)aSelector {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector: aSelector];
#pragma clang diagnostic pop
}
#end
For posterity's sake, I've decided to throw my hat into the ring :)
Recently I've been seeing more and more restructuring away from the target/selector paradigm, in favor of things such as protocols, blocks, etc. However, there is one drop-in replacement for performSelector that I've used a few times now:
[NSApp sendAction: NSSelectorFromString(#"someMethod") to: _controller from: nil];
These seem to be a clean, ARC-safe, and nearly identical replacement for performSelector without having to much about with objc_msgSend().
Though, I have no idea if there is an analog available on iOS.
Matt Galloway's answer on this thread explains the why:
Consider the following:
id anotherObject1 = [someObject performSelector:#selector(copy)];
id anotherObject2 = [someObject performSelector:#selector(giveMeAnotherNonRetainedObject)];
Now, how can ARC know that the first returns an object with a retain count of 1 but the second
returns an object which is autoreleased?
It seems that it is generally safe to suppress the warning if you are ignoring the return value. I'm not sure what the best practice is if you really need to get a retained object from performSelector -- other than "don't do that".
#c-road provides the right link with problem description here. Below you can see my example, when performSelector causes a memory leak.
#interface Dummy : NSObject <NSCopying>
#end
#implementation Dummy
- (id)copyWithZone:(NSZone *)zone {
return [[Dummy alloc] init];
}
- (id)clone {
return [[Dummy alloc] init];
}
#end
void CopyDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy copy];
}
void CloneDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy clone];
}
void CopyDummyWithLeak(Dummy *dummy, SEL copySelector) {
__unused Dummy *dummyClone = [dummy performSelector:copySelector];
}
void CloneDummyWithoutLeak(Dummy *dummy, SEL cloneSelector) {
__unused Dummy *dummyClone = [dummy performSelector:cloneSelector];
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
Dummy *dummy = [[Dummy alloc] init];
for (;;) { #autoreleasepool {
//CopyDummy(dummy);
//CloneDummy(dummy);
//CloneDummyWithoutLeak(dummy, #selector(clone));
CopyDummyWithLeak(dummy, #selector(copy));
[NSThread sleepForTimeInterval:1];
}}
}
return 0;
}
The only method, which causes memory leak in my example is CopyDummyWithLeak. The reason is that ARC doesn't know, that copySelector returns retained object.
If you'll run Memory Leak Tool you can see the following picture:
...and there are no memory leaks in any other case:
To make Scott Thompson's macro more generic:
// String expander
#define MY_STRX(X) #X
#define MY_STR(X) MY_STRX(X)
#define MYSilenceWarning(FLAG, MACRO) \
_Pragma("clang diagnostic push") \
_Pragma(MY_STR(clang diagnostic ignored MY_STR(FLAG))) \
MACRO \
_Pragma("clang diagnostic pop")
Then use it like this:
MYSilenceWarning(-Warc-performSelector-leaks,
[_target performSelector:_action withObject:self];
)
Do not suppress warnings!
There are no less than 12 alternative solutions to tinkering with the compiler.
While you are being clever at the time the first implementation, few engineer on Earth can follow your footsteps, and this code will eventually break.
Safe Routes:
All these solutions will work, with some degree of of variation from your original intent. Assume that param can be nil if you so desire:
Safe route, same conceptual behavior:
// GREAT
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
Safe route, slightly different behavior:
(See this response)
Use any thread in lieu of [NSThread mainThread].
// GOOD
[_controller performSelector:selector withObject:anArgument afterDelay:0];
[_controller performSelector:selector withObject:anArgument afterDelay:0 inModes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorInBackground:selector withObject:anArgument];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO modes:#[(__bridge NSString *)kCFRunLoopDefaultMode]];
Dangerous Routes
Requires some kind of compiler silencing, which is bound to break. Note that at present time, it did break in Swift.
// AT YOUR OWN RISK
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];
Because you are using ARC you must be using iOS 4.0 or later. This means you could use blocks. If instead of remembering the selector to perform you instead took a block, ARC would be able to better track what is actually going on and you wouldn't have to run the risk of accidentally introducing a memory leak.
Instead of using the block approach, which gave me some problems:
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
I will use NSInvocation, like this:
-(void) sendSelectorToDelegate:(SEL) selector withSender:(UIButton *)button
if ([delegate respondsToSelector:selector])
{
NSMethodSignature * methodSignature = [[delegate class]
instanceMethodSignatureForSelector:selector];
NSInvocation * delegateInvocation = [NSInvocation
invocationWithMethodSignature:methodSignature];
[delegateInvocation setSelector:selector];
[delegateInvocation setTarget:delegate];
// remember the first two parameter are cmd and self
[delegateInvocation setArgument:&button atIndex:2];
[delegateInvocation invoke];
}
If you don't need to pass any arguments an easy workaround is to use valueForKeyPath. This is even possible on a Class object.
NSString *colorName = #"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
UIColor *brightPink = [uicolor valueForKeyPath:colorName];
...
}
You could also use a protocol here. So, create a protocol like so:
#protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
#end
In your class that needs to call your selector, you then have a #property.
#interface MyObject
#property (strong) id<MyProtocol> source;
#end
When you need to call #selector(doSomethingWithObject:) in an instance of MyObject, do this:
[self.source doSomethingWithObject:object];
I have stumbled upon an issue for which I can't find answer elsewhere. When I am calling a method which returns pointer to an object which is later used and at the end set to nil, it is still allocated in memory (according to Instruments). I'm using XCode 4.6.3 and iOS 6.1. ARC is turned on.
Here is sample code:
ClassA.h
#interface ClassA : NSObject
-(void)runSomething;
#end
ClassA.m
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassA
-(void)runSomething {
int counter = 0;
while (true) {
ClassB *instance = [self makeClassBWithNumber:counter];
NSLog(#"%d", [instance getNumber]);
[NSThread sleepForTimeInterval:0.01];
instance = nil;
counter++;
}
}
-(ClassB*) makeClassBWithNumber:(int)number {
return [[ClassB alloc] initWithNumber:number];
}
#end
ClassB.h
#interface ClassB : NSObject
#property int number;
-(id)initWithNumber:(int)number;
-(int)getNumber;
#end
ClassB.m
#import "ClassB.h"
#implementation ClassB
-(id)initWithNumber:(int)number {
self = [super init];
if(self) {
_number = number;
}
return self;
}
-(int)getNumber {
return [self number];
}
#end
ClassB is created in view controller and method runSomething is called. This sample code produces that created object (ClassB) is never released from memory. If I change code from
ClassB *instance = [self makeClassBWithNumber:counter];
to
ClassB *instance = [[ClassB alloc] initWithNumber:counter];
created object is properly released in each of loop cycle. What is the reason for such behaviour? I found some old answers here on stackoverflow that makeClassBWithNumber should return result invoking autorelease return [result autorelease], but this can't be done if ARC is enabled.
The difference is that +alloc returns an object with a +1 retain, which ARC will balance with a release at the end of its scope, and so immediately deallocate. +make… returns an object with a +1 retain and a matching autorelease. The autorelease pool will send a release message when it drains. Since you stay in loop "while true," the autorelease pool never drains and you accumulate memory.
The solution is to give your loop an autorelease pool:
while (true) {
#autoreleasepool { // <== Add an autorelease block here.
ClassB *instance = [self makeClassBWithNumber:counter];
//NSLog(#"%d", [instance getNumber]);
NSLog(#"%d", [instance number]); // Fix naming; do not prefix accessors with `get`
[NSThread sleepForTimeInterval:0.01];
// instance = nil; // Does nothing in this loop.
counter++;
}
}
This will cause the pool to drain on every iteration. In any case the instance=nil is unnecessary.
EDIT: Do read MartinR's answer. It gives some more details on the implementation details, and particularly why this may behave differently depending on the optimization level, and whether the called method is in the same compile unit (.m file) as the calling method. That is only an optimization detail; you still need to put this #autoreleasepool in the loop for correctness.
makeClassBWithNumber returns an autoreleased object, even with ARC.
(More precisely, it can return an autoreleased object, depending on the optimization.)
The difference to manual reference counting is that the ARC compiler inserts the autorelease call where required, not you.
From the Clang/ARC documentation:
3.2.3 Unretained return values
A method or function which returns a retainable object type but does
not return a retained value must ensure that the object is still valid
across the return boundary.
When returning from such a function or method, ARC retains the value
at the point of evaluation of the return statement, then leaves all
local scopes, and then balances out the retain while ensuring that the
value lives across the call boundary.
In the worst case, this may involve an autorelease, but callers must not assume that the value is
actually in the autorelease pool.
makeClassBWithNumber is not a alloc, copy, init, mutableCopy, or new
method and therefore returns an unretained return value.
The operative word in your question is "OLD". Old answers are no longer relevant.
This is what ARC is for.
You no longer need to worry about any memory management.
If ARC tells you not to do it... don't.
In this case, you don't need autorelease.
As others have said the difference you are seeing is down to whether a method returns and object the caller owns or an object the caller does not own.
In the former category are methods in the alloc, init, new, copy & mutableCopy categories. These all return an object owned by the caller and ARC will ensure it is released.
In the latter category are all the methods not in the first! These return an object which is not owned by the caller, ARC will ensure that this object is retained if needed and released if it was retained. Return values in this category may be in the auto-release pool, which means they will live at least as long as they are in the pool (maybe longer if they've been retained by ARC) and the standard pool is emptied at the end of each run loop cycle. This means that in loops which generate a lot of entries into the auto-release pool that a lot of no longer needed objects can accumulate in the pool - this is what you are seeing.
Under MRC the solution was to introduce a local auto-release pool, within such loops, to avoid accumulation of no longer needed objects. However under ARC this is probably not the best choice.
Under ARC the better solution is probably to follow the naming conventions, and in this case you want to use the new pattern - new is the standard pattern for alloc + init in one method. So rename your makeClassBWithNumber as newClassBWithNumber:
// *create* a new ClassB object
- (ClassB *) newClassBWithNumber:(int)number
{
return [[ClassB alloc] initWithNumber:number];
}
This indicates the method returns an object the caller owns, it is a "creation" method, and ARC will handle the rest without no longer objects accumulating.
(Adding a newWithNumber method to ClassB itself is often a good idea under ARC.)
I'm new to ARC but understand how it works and I'm trying it out. I'm on iOS so memory is a severe concern.
I have a MyObject class which contains lots of big data. I want to release it, and load a new set of data.
MyObject *object;
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1
// later...
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2
This works fine without leaks, and I'm guessing the ARC inserts a [object release] before the new assignment. My problem is the data inside 'object' is released after the new set is allocated, and I run out of memory. What I really want to be able to do is:
object = nil;
<function to pop the pool, wait till everything is deallocated>
object = [MyObject alloc] initWithData:folder2]; // load data from folder2
but I'm not sure how to do that. I could run the new allocation on a performselector afterdelay, but it feels like I'm shooting in the dark and a bit of hack. There's probably a proper way to do this?
P.S I've tried searching for an answer, but all results are about memory leaks and how to make sure variables go out of scope and set variables to nil etc. My issue isn't about that, it's more of a timing thing.
UPDATE
Thanks for the answers, I'd already tried
object = nil;
object = [MyObject alloc] initWithData:folder2];
and it hadn't worked. I wasn't sure whether it was supposed to or not. Now I understand that it is supposed to work, but I must have something else holding on to it for that fraction of a second. I have NSLogs in all of my init/dealloc methods, and I can see first all the inits of the new instances of classes (of MyObject's ivars) being called, and then almost immediately after (within a few ms), the dealloc of MyObject, followed by the deallocs of its ivars.
I also tried the #autorelease but the same thing happens.
I've searched throughout the project and pasted all the code which I think may be relevant to this.
#interface AppDelegate : UIResponder <UIApplicationDelegate>;
#property PBSoundSession *soundSession;
#end
//--------------------------------------------------------------
#implementation AppDelegate
// onTimer fired at 60Hz
-(void)onTimer:(NSTimer *) theTimer {
[oscReceiver readIncoming]; // check incoming OSC messages
// then do a bunch of stuff with _soundSession;
}
#end
//--------------------------------------------------------------
#implementation OscReceiver
-(void)readIncoming {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// parse all incoming messages
if(bLoadNewSoundBank) {
NSString *newFolder = parseNewFolder();
appDelegate.soundSession = nil;
appDelegate.soundSession = [MyObject alloc] initWithData:newFolder];
}
}
#end
//--------------------------------------------------------------
#implementation GuiController
// onTimer fired at 10Hz
-(void)onTimer:(NSTimer *) theTimer {
PBSoundSession *soundSession = appDelegate.soundSession;
// update gui with received values
}
#end
I thought it might be the 'soundSession' local variable in the GuiController::onTimer holding onto the old appDelegate.soundSession for the duration of that method, but to my surprise commenting out all of the GUI code (in fact disabling the timer), made no difference.
Is there a way of finding out at that point who is still holding onto my appDelegate.soundSession? I placed a breakpoint where I set it to nil, but couldn't find any useful information. I tried Instruments in Allocation template, but couldn't find anything useful there either (probably because I don't know where to look).
This is what my allocations track looks like, you can see the memory is all deallocated a bit too late!
.
This might not be an an ARC problem. What you could be seeing is your autorelease pool not draining soon enough—your MyObject is getting released, but the data it loaded is getting held onto by the pool because of some internal -retain/-autorelease pair. Try wrapping your -initWithData: calls in an #autoreleasepool block, like this:
#autoreleasepool {
object = [[MyObject alloc] initWithData:folder1];
// do things
}
// later…
#autoreleasepool {
object = [[MyObject alloc] initWitData:folder2];
// do other things
}
Setting the object to nil immediately before setting it to something else as Gabriele suggests might cause the compiler to insert the appropriate release before the second -alloc/-initWithData:, but it might be smart enough to do that already—if that doesn’t work, it’s most likely the autorelease-pool thing.
There is no delay when draining an #autoreleasepool {...}; the objects in the pool have release invoked immediately. If an object survives that, it is because there is either a strong reference elsewhere or because the object was autoreleased into the next pool out.
If you do:
a = [[Foo alloc] initBigThing];
a = nil;
a = [[Foo alloc] initBigThing];
The first instance of Foo will be released prior to the allocation of the second
With one big caveat; if any of the code paths that a is invoked upon happen to retain/autorelease it, then it'll stick around until the pool is drained. Surrounding it in #autoreleasepool{ ... }; should do the trick.
Note that the compiler will sometimes emit retain/autorelease sequences in non-optimized builds that are eliminated in optimized builds.
A bit more general answer, I found how you can force release an object:
#import <objc/message.h>
// ---
while ([[object valueForKey:#"retainCount"] integerValue] > 1) {
objc_msgSend(object, NSSelectorFromString(#"release"));
}
objc_msgSend(object, NSSelectorFromString(#"release"));
But you shouldn't do this because ARC will probably release the object later and this will cause a crash. This method should be only used in debug!
I need to perform an action in the dealloc method of a category. I've tried swizzling but that doesn't work (nor is it a great idea).
In case anyone asks, the answer is no, I can't use a subclass, this is specifically for a category.
I want to perform an action on delay using [NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:] or [self performSelector:withObject:afterDelay:] and cancel it on dealloc.
The first issue is that NSTimer retains the target, which I don't want. [self performSelector:withObject:afterDelay:] doesn't retain, but I need to be able to call [NSObject cancelPreviousPerformRequestsWithTarget:selector:object:] in the dealloc method or we get a crash.
Any suggestions how to do this on a category?
I still think it would be better to subclass your class and not mess with the runtime, but if you are definitely sure you need to do it in a category, I have an option in mind for you. It still messes with the runtime, but is safer than swizzling I think.
Consider writing a helper class, say calling it DeallocHook which can be attached to any NSObject and perform an action when this NSObject gets deallocated. Then you can do something like this:
// Instead of directly messing with your class -dealloc method, attach
// the hook to your instance and do the cleanup in the callback
[DeallocHook attachTo: yourObject
callback: ^{ [NSObject cancelPrevious... /* your code here */ ]; }];
You can implement the DeallocHook using objc_setAssociatedObject:
#interface DeallocHook : NSObject
#property (copy, nonatomic) dispatch_block_t callback;
+ (id) attachTo: (id) target callback: (dispatch_block_t) block;
#end
Implementation would be something like this:
#import "DeallocHook.h"
#import <objc/runtime.h>
// Address of a static global var can be used as a key
static void *kDeallocHookAssociation = &kDeallocHookAssociation;
#implementation DeallocHook
+ (id) attachTo: (id) target callback: (dispatch_block_t) block
{
DeallocHook *hook = [[DeallocHook alloc] initWithCallback: block];
// The trick is that associations are released when your target
// object gets deallocated, so our DeallocHook object will get
// deallocated right after your object
objc_setAssociatedObject(target, kDeallocHookAssociation, hook, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return hook;
}
- (id) initWithCallback: (dispatch_block_t) block
{
self = [super init];
if (self != nil)
{
// Here we just copy the callback for later
self.callback = block;
}
return self;
}
- (void) dealloc
{
// And we place our callback within the -dealloc method
// of your helper class.
if (self.callback != nil)
dispatch_async(dispatch_get_main_queue(), self.callback);
}
#end
See Apple's documentation on Objective-C runtime for more info about the associative references (although I'd say the docs are not very detailed regarding this subject).
I've not tested this thoroughly, but it seemed to work. Just thought I'd give you another direction to look into.
I just stumbled on a solution to this that I haven't seen before, and seems to work...
I have a category that--as one often does--needs some state variables, so I use objc_setAssociatedObject, like this:
Memento *m = [[[Memento alloc] init] autorelease];
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And, I needed to know when the instances my category extending were being dealloced. In my case it's because I set observers on self, and have to remove those observers at some point, otherwise I get the NSKVODeallocateBreak leak warnings, which could lead to bad stuff.
Suddenly it dawned on me, since my associated objects were being retained (because of using OBJC_ASSOCIATION_RETAIN_NONATOMIC), they must be being released also, and therefore being dealloced...in fact I had implemented a dealloc method in the simple storage class I had created for storing my state values. And, I postulated: my associated objects must be dealloced before my category's instances are! So, I can have my associated objects notify their owners when they realize they are being dealloced! Since I already had my retained associated objects, I just had to add an owner property (which is not specified as retain!), set the owner, and then call some method on the owner in the associated object's dealloc method.
Here's a modified part of my category's .m file, with the relevant bits:
#import <objc/runtime.h> // So we can use objc_setAssociatedObject, etc.
#import "TargetClass+Category.h"
#interface TargetClass_CategoryMemento : NSObject
{
GLfloat *_coef;
}
#property (nonatomic) GLfloat *coef;
#property (nonatomic, assign) id owner;
#end
#implementation TargetClass_CategoryMemento
-(id)init {
if (self=[super init]) {
_coef = (GLfloat *)malloc(sizeof(GLfloat) * 15);
}
return self;
};
-(void)dealloc {
free(_coef);
if (_owner != nil
&& [_owner respondsToSelector:#selector(associatedObjectReportsDealloc)]) {
[_owner associatedObjectReportsDealloc];
}
[super dealloc];
}
#end
#implementation TargetClass (Category)
static NSString *kMementoTagKey = #"TargetClass+Category_MementoTagKey";
-(TargetClass_CategoryMemento *)TargetClass_CategoryGetMemento
{
TargetClass_CategoryMemento *m = objc_getAssociatedObject(self, kMementoTagKey);
if (m) {
return m;
}
// else
m = [[[TargetClass_CategoryMemento alloc] init] autorelease];
m.owner = self; // so we can let the owner know when we dealloc!
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return m;
}
-(void) doStuff
{
CCSprite_BlurableMemento *m = [self CCSprite_BlurableGetMemento];
// do stuff you needed a category for, and store state values in m
}
-(void) associatedObjectReportsDealloc
{
NSLog(#"My associated object is being dealloced!");
// do stuff you need to do when your category instances are dealloced!
}
#end
The pattern here I learned somewhere (probably on S.O.) uses a factory method to get or create a memento object. Now it sets the owner on the memento, and the memento's dealloc method calls back to let the owner know it's being dealloced
CAVEATS:
Obviously, you have to have your associated object set with OBJC_ASSOCIATION_RETAIN_NONATOMIC, or it won't be retained and released for you automatically.
This becomes trickier if your memento/state associated object gets dealloced under other circumstances than the owner being dealloced...but you can probably train one object or the other to ignore that event.
The owner property can't be declared as retain, or you'll truly create a strong reference loop and neither object will ever qualify to be dealloced!
I don't know that it's documented that OBJC_ASSOCIATION_RETAIN_NONATOMIC associated objects are necessarily released before the owner is completely dealloced, but it seems to happen that way and almost must be the case, intuitively at least.
I don't know if associatedObjectReportsDealloc will be called before or after the TargetClass's dealloc method--this could be important! If it runs afterwards, if you try to access member objects of the TargetClass you will crash! And my guess is that it's afterwards.
This is a little messy, because you're double-linking your objects, which requires you to be very careful to keep those references straight. But, it doesn't involve swizzling, or other interference with the runtime--this just relies on a certain behavior of the runtime. Seems like a handy solution if you already have an associated object. In some cases it might be worth creating one just to catch your own deallocs!
Your proposed solution unfortunately won't work: because NSTimer retains its target, the target will never run its dealloc until the timer has been invalidated. The target's retain count will always be hovering at 1 or above, waiting for the timer to release it. You have to get to the timer before dealloc. (Pre-ARC, you could override retain and release and destroy the timer, although that's really not a good solution.)
NSThread also has this problem, and the solution is simple: a bit of redesigning separates the controller of the thread from the "model". The object which creates and owns the thread, or timer in this case, should not also be the target of the timer. Then, instead of the retain cycle you currently have (timer owns object which owns timer), you have a nice straight line: controller owns timer which owns target. Outside objects only need to interact with the controller: when it is deallocated, it can shut down the timer without you having to play games with overriding dealloc or other memory management methods.
That's the best way to handle this. In the case that you can't do that for some reason -- you're talking about category overrides, so apparently you don't have the code for the class which is the target of the timer (but you can still probably make a controller even in that case) -- you can use weak references. Unfortunately I don't know any way to make an NSTimer take a weak reference to its target, but GCD will give you a fair approximation via dispatch_after(). Get a weak reference to the target and use that exclusively in the Block you pass. The Block will not retain the object through the weak reference (the way NSTimer would), and the weak reference will of course be nil if the object has been deallocated before the Block runs, so you can safely write whatever message sends you like.
Given the following simple implementation:
#implementation RTUDeallocLogger
-(void)dealloc
{
NSLog(#"deallocated");
}
#end
we run the following code under ARC:
#implementation RTURunner
{
NSArray* arr;
}
-(void)run{
arr = [NSArray
arrayWithObjects:[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
nil];
NSLog(#"nulling arr");
arr = NULL;
NSLog(#"finished nulling");
}
#end
we get the following log output:
nulling arr
finished nulling
deallocated
deallocated
deallocated
I'd like to perform an action after all the deallocations have finished. Is this possible?
The aim of this question is really to understand a little more about the mechanics of ARC, in particular, at what point ARC triggers these deallocations, and whether or not this can ever happen synchronously when I drop references.
-dealloc is always synchronous, and occurs when the last strong reference is removed. In the case of your code though, +arrayWithObjects: is likely (if compiled at -O0 at least) putting the array in the autorelease pool, so the last strong reference is removed when the pool drains, not when you set the variable to NULL (you should use nil for ObjC objects, btw).
You can likely avoid having the object in the autorelease pool by using alloc/init to create, and you may (implementation detail, bla bla) be able to avoid it by compiling with optimizations turned on. You can also use #autoreleasepool { } to introduce an inner pool and bound the lifetime that way.
If I were an engineer from Apple I'd probably argue that your problem is probably your design. There are almost no reasons you'd want effectively to act by watching dealloc rather than having dealloc itself act.
[a huge edit follows: weak properties don't go through the normal property mechanisms, so they aren't KVO compliant, including for internal implicit KVO as originally proposed]
That said, what you can do is bind the lifetime of two objects together via object associations and use the dealloc of the latter as a call-out on the dealloc of the former.
So, e.g.
#import <objc/runtime.h>
#interface DeallocNotifier;
- (id)initWithObject:(id)object target:(id)target action:(SEL)action;
#end
#implementation DeallocNotifier
- (id)initWithObject:(id)object target:(id)target action:(SEL)action
{
... blah ...
// we'll use a static int even though we'll never access by this key again
// to definitely ensure no potential collisions from lazy patterns
static int anyOldKeyWellNeverUseAgain;
objc_setAssociatedObject(object, &anyOldKeyWellNeverUseAgain, self, OBJC_ASSOCIATION_RETAIN);
... blah ...
}
- (void)dealloc
{
[_target performSelector:_action];
}
#end
-(void)run{
arr = ...
[[DeallocNotifier alloc]
initWithObject:arr target:self action:#selector(arrayDidDealloc)];
/* you may not even need *arr in this case; I'm unclear as
to why you have an instance variable for something you don't
want to keep, so I guess it'll depend on your code */
} // end of run
- (void)arrayDidDealloc
{
NSLog(#"array was deallocated");
}
I've assumed you're able to tie the lifecycle of all the objects you're interested in to that of a single container; otherwise you could associate the notifier to all relevant objects.
The array has definitely gone by the time you get arrayDidDealloc.
at what point ARC triggers these deallocations
ARC inserts allocations/deallocations into your code based on static analysis. You can see where it does this by looking at the assembly of your source -- go to Product -> Generate Output in Xcode.
whether or not this can ever happen synchronously when I drop references
Retain/release/autorelease is always synchronous.
Your code
arr = [NSArray arrayWithObjects:[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
nil];
will be implicitly placing the objects into an autorelease pool. After the object is allocated, you don't want it retained (because the NSArray will do the retain once it receives the object), but you can't release it immediately, otherwise it will never make it to the NSArray alive. This is the purpose of autorelease - to cover the case where the object would otherwise be in limbo between two owners.
The retain count at alloc time is 1, then it's retained by the autoreleasepool and released by you, so the retain count remains 1. Then, it's retained by the NSArray, so the retain count becomes 2.
Later, the NSArray is released and so the retain count returns to 1, and the objects are finally cleaned up when the autorelease pool gets its chance to run.
You can make the autorelease act faster by nesting another pool - by wrapping your NSArray creation with an #autorelease{} clause.