How to force release on iOS

I am new to ARC, but I understand how it works, and I'm trying to do it. I am on iOS, so memory is a major concern.

I have a MyObject class that contains a lot of big data. I want to release it and upload a new dataset.

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 assume that ARC inserts [object release] before the new assignment. My problem is that the data inside the "object" is freed up after allocating a new set, and my memory runs out. I really want this to be:

 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 this. I could start a new selection by artist after holding, but it seems to me that I shoot in the dark and hack a little. Perhaps the right way to do this?

PS I tried to find the answer, but all the results relate to memory leaks and how to make the variables go out of scope and set the variables to zero, etc. My problem is not that this is a longer time thing.

UPDATE
Thanks for the answers, I already tried

 object = nil; object = [MyObject alloc] initWithData:folder2]; 

and it didn’t work. I was not sure if this was supposed or not. Now I understand that it should work, but I must have something else that holds it for this split second. I have NSLogs in all my init / dealloc methods, and I see all the names of the new class instances (MyObject ivars) first, and then almost immediately after (within a few ms) dealloc MyObject, and then deallocates its ivars. I also tried @autorelease, but the same thing happens.

I searched the whole project and pasted all the code that 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 could be the local variable soundSession in GuiController :: onTimer, holding the old appDelegate.soundSession throughout this method, but, to my surprise, commenting on all the GUI code (actually disabling the timer) does not matter.

Is there any way to find out at that moment who is still holding onto my appDelegate.soundSession? I set a breakpoint, where I set it to zero, but could not find any useful information. I tried the tools in the Allocation template, but could not find anything useful there (perhaps because I do not know where to look).

This is what my highlight path looks like, you can see that the memory is freed too late! Valid XHTML .

+6
source share
3 answers

This may not be a problem with ARC. What you could see is your auto-update pool , which does not expire soon enough - your MyObject is freed, but the downloaded data is captured by the pool due to some internal -retain / -autorelease . Try wrapping calls to -initWithData: in the @autoreleasepool block, for example:

 @autoreleasepool { object = [[MyObject alloc] initWithData:folder1]; // do things } // later… @autoreleasepool { object = [[MyObject alloc] initWitData:folder2]; // do other things } 

Setting the object to nil just before setting it up for something else, as Gabriele suggests, can cause the compiler to insert the corresponding release before the second -alloc / -initWithData: but it can be smart enough to do it already - if it doesn’t work, most likely, this is a pool of abstracts.

+10
source

There is no delay with @autoreleasepool {...} ; objects in the pool immediately call release . If the object survives, it is because there is a strong link elsewhere or because the object was autorelease d in the next pool.

If you do:

  a = [[Foo alloc] initBigThing]; a = nil; a = [[Foo alloc] initBigThing]; 

The first instance of Foo will be released before the second

With one big warning; if any of the code paths that is called by a occurs after retain/autorelease , then it will stick until the pool is exhausted. Surround him at @autoreleasepool{ ... }; gotta do the trick.

Note that the compiler sometimes highlights retain/autorelease in non-optimized assemblies, which are eliminated in optimized assemblies.

+3
source

A slightly more general answer, I found how you can force an object to be freed:

 #import <objc/message.h> // --- while ([[object valueForKey:@"retainCount"] integerValue] > 1) { objc_msgSend(object, NSSelectorFromString(@"release")); } objc_msgSend(object, NSSelectorFromString(@"release")); 

But you should not do this, because ARC will most likely release the object later, and this will crash. This method should only be used for debugging!

+1
source

Source: https://habr.com/ru/post/952891/


All Articles