Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force release on iOS

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! Valid XHTML.

like image 424
memo Avatar asked Aug 31 '13 18:08

memo


People also ask

How do I factory reset my iPhone with just the buttons?

Simply press and hold the Volume buttons and Home button at the same time. iPhone X, 8 & Newer: Press and hold the Volume up button, then the Volume Down button, and the side button at the same time. iPhone 7 / 7 Plus: Press and hold the Volume Down and the side button at the same time.

How do I force my iPhone to factory reset?

Tap Settings > General > Transfer or Reset [Device]Then tap Erase All Content and Settings.

How do I do a soft reset on my iPhone?

If a power cycle/restart does not resolve your issue, perform a soft reset. Press and quickly release the Volume up button > press and quickly release the Volume down button > press and hold the Side button until you see the Apple logo.


1 Answers

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.

like image 136
Noah Witherspoon Avatar answered Sep 22 '22 14:09

Noah Witherspoon