I have been working on iOS project, which used data downloading in background. Well, honestly say - implementation wasn't the best one (and was changed later to remove such nonobvious pattern), and I've got some problems with not-deallocated objects, and can't say, that docs gave me a clear understanding of what's going on. Code, demonstrating common idea:
- (void)loadModelAtIndex:(NSUInteger)index {
Model *model = [self modelAtIndex:index];
if (model) {
model.index = index;
[self performSelectorInBackgroundThread:#selector(loadModelInBackgroundThread) withObject:model]
}
}
- (void)loadModelInBackgroundThread:(Model *)model {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
model.data = [NSData dataWithContentsOfURL:model.url];
//and some other changes can be here
[self performSelectorOnMainThread:#selector(modelDidLoad) withObject:model waitUntilDone:NO]'
[pool drain];
}
- (void)modelDidLoad:(Model *)model {
[self saveModel:model atIndex:model.index];
[self loadModelAtIndex:model.index + 1];
}
Well, almost all time it has been working as expected. Except if caller has beed deallocated while downloading in background - Model object stayed in memory, without being released (I've got growing memory at this point).
Can anyone explain me, what will happen in case of deallocation, while background thread is running? I'm not sure this code can be suitable at all, but still interested. Any modification will make things run well?
Except if caller has beed deallocated while downloading in background - Model object stayed in memory
the performSelector | …Thread… calls retain their arguments. If your objects are deallocated in that time, the problem lies elsewhere. Run with zombies enabled to locate it - you can record every reference count.
Related
There is a similar question here, which doesn't explain exactly what I want: Objective C Blocks as Async-callbacks & BAD ACCESS
I have a view controller, which calls a service with an async callback. The callback is done using a block, which references variables on the view controller to populate them.
It looks like so:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
However, if I dealloc the view controller, 'me' is then badly accessed by the callback.
What is the simplest way of making 'me' NULL? If i put it as an iVar, it then brings back the circular reference... i think?
I think I'm missing something obvious....
Thanks
Are you targeting iOS 5.0 or later (or Mac OS X 10.7 or later)? If so, you can use ARC and a __weak variable (instead of a __block one). This will automatically zero out when the referenced object is deallocated. Your code would look like
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
If you need support for an older OS then you need to find a different solution. One solution is to just go ahead and let the block retain self. If the service is guaranteed to execute the completion block (and then release it), this will only produce a temporary cycle that will break automatically when the completion block is run. Alternatively if you have some way to cancel the service (in a way that guarantees the block cannot be called after the cancellation), you can stick with the __block and just be sure to cancel the service in your -dealloc. There's other alternatives too but they're more complicated.
I did a combination of things above from the suggestions. Including nilling the blocks. Although, my objects are still not getting released immediately. i.e. I'd put a breakpoint on dealloc of MyViewController, and without the __block variable it would get called at a much later point in time (probably due to the async connection) and sometimes not at all.
The code is fairly complex - so I imagine there are other things going on for it to not work as suggested above.
What I have also done, is used Mike Ash's MAZeroingWeakRef, which i guess is the same as using __weak - which #KevinBallard suggested.
Below is how I've implemented it, and it appears to be working. Dealloc is called immediately on disposal of the view controller, which i want. And I can't get it to crash... and with the log comment that i've put in, I can already see that I'm dodging bullets.
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(#"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
Is there a real retain cycle problem that you're trying to avoid? Is there a reason that self should not simply be retained until -executeWithCompletion: completes? Is there any real chance that it won't complete?
So long as it really will eventually complete (even with failure) and so long as it releases the block after invoking it (perhaps by setting a property to nil), then the retain cycle will eventually be broken and all will be well.
I'm running a large number of NSOperation tasks and my application is using a great deal of memory. While it should use quite a bit, it's using magnitudes more than it should, and I'm thinking, from Instruments, that it's because the NSOperation objects aren't being fully deallocated. The code for my NSOperation subclass is as such:
- (id)initFromNode:(BKObject *)sender withNumber:(NSNumber *)number; {
self = [super init];
if (self) {
_number = [number retain];
_sender = [sender retain];
}
return self;
}
- (void)main {
[_sender go:_number];
}
- (void)dealloc {
[_number release];
_number = nil;
[_sender release];
_sender = nil;
[super dealloc];
}
My suspicions are as such because in Instruments, when I use the Allocations utility, it shows enormous amounts of data for my _NSOperationInternal and also my subclasses of NSOperation, but the Live Bytes number is always equal to the Overall Bytes number. I've also checked with the Leaks utility, which never finds any memory leaks. I'm careful about releasing any operation objects after adding them to the queue.
I also stuck in a completion block to test it out if it actually finishes, and I can confirm that at least some of them do. Confirming all of them would be more work, and the live data in Instruments should go down a bit, even if only, say 10% of them, were finishing.
I'm at a loss. Let me know if any of my understanding of what I'm doing is off or if more code would be helpful. Do you know what might be going on that this is using more memory than it should?
To debug this, try to see if -dealloc gets even called. The simplest way to do this is to use an NSLog, the correct way would be a breakpoint.
My guesses are:
You're not correctly releasing the NSOperations.
or you've got retain cycles.
Some objects in your NSOperation don't get released, try adding an autorelease pool around your main method.
Quite a lot of people seem to use a macro such as
#define SAFE_RELEASE(X) [X release]; X = nil;
(myself included).
I've been reassessing why I am using it and wanted to canvas some opinion.
The purpose (I think) for using this macro is so that if you were to accidentally use your object after releasing it then you won't get a bad access exception because objective-c will quite happily ignore it when the object is nil.
It strikes me that this has the potential to mask some obscure bugs. Maybe it would actually be preferable for the program to crash when you try to use X again. That way during testing you can find the issue and improve the code.
Does this macro encourage lazy programming?
Thoughts?
I think you discuss all the pros and cons in your question, so I don't have a huge amount to add. Personally I don't use the construct. As you suggest, it can be used to paper over areas where people don't understand the memory management correctly. My preference is to fix the bug and not the symptom.
However, one compromise that I have seen from time to time is:
Make it crash during development
Do the var = nil; in production code
That way it might be more reliable with paying customers and still crashes early during development.
I'm not keen on this either, as you're using different code to your users and just because the buggy version keeps running doesn't mean it's doing the right thing. Not crashing but corrupting your database is not desirable behaviour...
I think it, or an equivalent such as self.myVar = nil where applicable, is a good. There are many cases where you simply can't just assign nil and assume any later access is a bug.
For example in UIKit it's good behavior to free up as many resources as possible when the OS ask. E.g.
- (void)didReceiveMemoryWarning
{
[myCachedData release];
[super didReceiveMemoryWarning];
}
Now, when my class is next used how am I to know that myCachedData is now invalid? The only way (short of having ANOTHER variable acting as a flag) is to set myCachedData to nil after releasing it. And condensing those two tedious lines into one is exactly what SAFE_RELEASE is for.
You don't need it, but it is handy to have. I use something similar in my apps. You can consider it "lazy", but when you have somewhere around 20 objects, writing these out manually gets tedious.
I was looking into the same question. With the bit of reading I did I just have something like this:
#define DEBUGGING
//#define PRODUCTION
#ifdef DEBUGGING
#define SAFE_RELEASE(X) [X release];
#else
#define SAFE_RELEASE(X) [X release]; X = nil;
#endif
So that way if I'm developing, I get the Crashes. In Production I don't.
Scott<-
As Andrew pointed out, there are cases where assigning nil isn't only avoiding bugs, but necessary. Just consider typical UIViewController code
- (void)viewDidLoad {
button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // autoreleased
[button retain]; // keep alive
}
- (void)viewDidUnload { // view has been removed
[button release];
}
- (void)dealloc { // destroying the view controller
[button release]; // <-- problem
}
In case the controller is loaded, later unloaded (because another view controller is displayed a memory is running low) and finally destroyed, the [button release] in dealloc would over-release the button (send a message to a released object). Therefore it it's necessary to assign nil. The safe solution would be:
- (void)viewDidUnload { // view has been removed
[button release];
button = nil;
}
- (void)dealloc { // destroying the view controller
[button release]; // <-- safe
}
For those cases a macro is nice and useful. To be more explicit about what it does, better name it RELEASE_AND_NIL
#define RELEASE_AND_NIL(X) [X release]; X = nil;
Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.
I'm fairly new to Cocoa and Objective-C. Currently I'm developing a fairly basic application to test my knowledge and put some of the stuff I've been reading about into practice. Everything is working, but Leaks reports a number of issues.
None of these leaks seems to be directly applicable to code that I've written (I have read and tried to follow Apple's rules on memory allocation). Currently my project makes use of Garbage Collection and I'm developing on Snow Leopard. Running AnalysisTool finds no issues with my code (aside from a few naming convention warnings).
Currently my application makes use of an NSTableView which I have hooked up to an NSArrayController. Interacting with the NSTableView seems to cause leaks to report issues (actions such as sorting table columns and other standard user interaction). This leads me to believe that my use of the NSArrayController (and my implementation of its content source) is to blame.
Currently the NSArrayController receives its content from an NSMutableArray (timers) handled in my Application's delegate like so:
- (id) init
{
if (self = [super init])
{
timers = [NSMutableArray array];
}
return self;
}
- (void) dealloc
{
[timers release];
[super dealloc];
}
Within Interface Builder my NSArrayController has its Object Controller set to the Timing class, which is defined below:
#interface Timing : NSObject {
NSString *desc;
NSDate *timestamp;
bool active;
}
#end
#implementation Timing
-(id) init
{
if (self = [super init])
{
desc = #"New timing";
timestamp = [[NSDate alloc] init];
active = false;
}
return self;
}
-(void) dealloc
{
[timestamp release];
[super dealloc];
}
#end
I've used standard Cocoa bindings to hook up Add and Remove buttons to manipulate the TableView and these seem to work correctly (clicking Add will create a row in the TableView with the value of 'New timing', for instance).
Leaks reports that the libraries responsible are AppKit and CoreGraphics. Although, honestly, I'm still new to the Leaks tool - so I could be reading its output incorrectly. If it helps, I've placed a screenshot of its output here. If anyone could point me in the right direction, that would really be appreciated.
As an aside, I've also been experimenting with manually adding objects to the timers array without the use of Cocoa bindings. Here's what I came up with:
Timing *timingInstance = [[Timing alloc] init];
[timers addObject:timingInstance];
[timingInstance release];
[timersController setContent:timers];
[timersTableView reloadData];
Again, this seems to work, but I thought it best to ask the experts!
Your memory management for the timers array is not quite correct. Using the array factory method will return an instance of NSMutableArray that has already been autoreleased, so the lifetime of that object is (probably) limited to the end of the current run loop, and it will be over-released when you call release in your dealloc method. The proper way to do it is as follows:
- (id) init
{
if (self = [super init])
{
timers = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
This method will give you an instance of NSMutableArray with a retain count of 1, which will then drop to zero (and properly release the memory) when you call release in your dealloc method. The call to alloc in your init method is balanced out by the call to release in your dealloc method. I notice that this is the exact pattern that you used for your NSDate object in the Timing class, so you are already familiar with the idea.
Your code as written is not leaking. The Cocoa frameworks will sometimes generate false leak reports when run under leaks, as certain things such as singletons and caches which are used in the implementation of the frameworks will sometimes show up as leaks even though they're not.
You're probably better off running the ObjectAlloc and/or ObjectGraph instruments to get an idea of when your objects are being allocated and deallocated.
Currently my project makes use of
Garbage Collection and I'm developing
on Snow Leopard
I don't understand. You're using garbage collection right? If so then GC takes care of releasing objects for you so anywhere you use "release" does absolutely nothing. Release calls are ignored when GC is enabled. You only use release when you're managing the memory yourself which is when GC is off. Also, your dealloc methods do nothing too. In GC that method is never used. When you create an object and then you finish with it you tell GC that it's OK to get rid of the object by setting it to nil. That's all you have to do. There is no "release" needed or dealloc method needed. Just set things to nil or or not as needed.