beginAnimations with "context"variable in ARC? - ios

The question partially similar to existing ones but I still get error with memory management.
The following non-ARC code work:
[UIView beginAnimations:... context:[[NSNumber numberWithInt:i] retain]];
and somewhere in didStopSelector:
NSNumber * n = (NSNumber *)context;
...
[n release];
I tried to remove retain/release and to add copy (and combined these ways) but with no effect.
Additionally I saw another similar question:
UIView Animation on multiple UIImageViews in ARC
They pass imageName variable as context but they don't describe if it is retained or autoreleased.
Questions:
1)How to convert my code to ARC correctly?
2)Is there any difference in code if you pass retained/autoreleased context (of cousre, if autoreleased will work in general)?

Try __bridge_retained to retain object and cast it to void*
void *context = (__bridge_retained void *)( #1000 );
and then in animationDidStop you have to transfer ownership with __bridge_transfer. At this point ARC should naturally release the object in current autorelease pool.
- (void)animationDidStop:(void *)context {
NSNumber *n = (__bridge_transfer id)context;
}
Alternatively you can switch to block based API and reference views directly.

Related

Possible memory leak with KVO and context

I'm trying to make a little binding system between my UILabel and my object Data using KVO. If my UI change, my data have to change, and if my data change my UI should refresh to display the new value.
The biggest issue I have is that I need to cast a custom object to a void* (context) with __bridge_retained void* - or CFBridgingRetain() - but I don't know where I should call CFBridgingRelease(). If call it in observeValueForKeyPath method I get a bad access error (I guess because my Reference Count to the object pointed by context is 0)
// viewDidLoad
// binding my label text with a custom data object
[self bindObject:_myLabel withPath:#"text" toObject:_user path:#"name"];
-(void) bindObject:(id)uiObj withPath:(NSString *)uiPath toObject:(id)dataObj path:(NSString *)dataPath
{
// custom object storing the object I want to bind and his path
PLSObjectPath* op = [[PLSObjectPath alloc] init];
op.theObj = dataObj;
op.thePath = dataPath;
PLSObjectPath* ob = [[PLSObjectPath alloc] init];
ob.theObj = uiObj;
ob.thePath = uiPath;
/* possible leak because I don't know where to call CFBridgingRelease */
[uiObj addObserver:self forKeyPath:uiPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(op)];
[dataObj addObserver:self forKeyPath:dataPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(ob)];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
PLSObjectPath *obj = (__bridge PLSObjectPath*) context;
PLSObjectPath* pairObj = [[PLSObjectPath alloc] init];
pairObj.theObj = object;
pairObj.thePath = keyPath;
// avoid infinite loop
[obj.theObj removeObserver:self forKeyPath:obj.thePath];
[obj.theObj setValue:change[#"new"] forKeyPath:obj.thePath];
[obj.theObj addObserver:self forKeyPath:obj.thePath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(pairObj)];
}
Traditionally users of this have used a static char * as the context parameter, so as to differentiate the different observeValueForKeyPath messages. That said, it should be possible to do something as you are attempting.
What I would suggest is to switch from a custom object to a Core Foundation one, where you can do you own memory management explicitly. Thus I'd suggest changing PLSObjectPath to CFDictionary. You can first create a NSDictionary, then "transfer" it to the CF domain with the appropriate cast, and pass that CFDictionary object for context (which is now a retained CF object). Recast it in observeValueForKeyPath to a CFDictionary, properly ARC cast it to a NSDictionary, use that, then it should get released if you've done the ARC correctly. This is all a well understood paradyme - moving objects in and out of ARC.
Another way you could do it is us a static NSMutableDictionary, and use the context pointer to go to a int value, which when converted to a NSNumber is the key to the dictionary. If all KVO occurs on the main thread, you don't need to protect the dictionary, but if not then you will need to put all access to the dictionary behind a serial dispatch queue that in forces serial access on one thread.
The memory leak is from [obj.theObj removeObserver:self forKeyPath:obj.thePath]. You are removing the observer but as the system doesn't treat the context as an object, it will not release it. Also, there is no way for you to get the context from the observed object itself.
At some point you will need to stop all observation to allow your view to be deallocated. This should happen from viewDid(Will)Disappear:. To be able to release the PLSObjectPath:s at that point, you will need to store them somewhere, possibly an NSMutableArray. If you will store these anyway for this purpose, you don't need to use __bridge_retained. Also, in this case your PLSObjectPath:s might/should contain a void* pointing to the other context. This way, you save the creation of PLSObject in observeValueForKeyPath:ofObject:change:context:.
Just a comment, you should start the KVO from viewWill(Did)Appear: and not from viewDidLoad. It gives you a better KVO start/stop management and also removes the unnecessary observation while your view is not on the screen.

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

Hooking end of ARC dealloc

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.

Passing objects safely as opaque context params under ARC

Under manual memory management, I use this pattern fairly often:
NSString * myStr = /* some local object */
[UIView beginAnimation:#"foo" context:(void *)[myStr retain]];
And then, later and asynchronously:
- (void)animationDidStop:(NSString *)anim finished:(NSNumber *)num context:(void *)context
{
NSString * contextStr = (NSString *)context;
// ...
[contextStr release];
}
i.e. I manually managed the lifetime of an object used as an opaque context. (This is true for the old UIView animations but also for other kinds of API that I use.)
Under ARC, my instinct is that I want to __bridge_retained going in and __bridge_transfer in the handler, as suggested here. But this treats a Cocoa object as a CFType not because it's really bridged, but just for the purpose of shoving a retain down its throat.
Is this valid, and is this stylistically acceptable? If not, what's the better* solution?
(The accepted answer in this question gives a different answer, saying that __bridge alone is OK, but that seems to me to be wrong, since the original string would be at risk of being deallocated as soon as it goes out of scope in the first function. Right?)
*Please don't say "use block-based animations instead". (That's not what I'm asking about.)
Go with your instinct. __bridge_retained transfers management of an object to you from ARC, while __bridge_transfer does the reverse, don't worry about treating the object as a CFType - you're not really doing that just taking over management.
The other approach you see recommended is to construct your code so that ARC retains management, but this can easily come across as contrived (and get messy). Having the API you're using maintain the value as it is designed to do is clean; just comment the code appropriately where management is handed to the API and returned back to ARC.
Even if using __bridge_retained/__bridge_transfer seems fine to me (transferring ownership to CoreFoundation or to any C code or to yourself is quite the same you just tell ARC that you are responsible for the object ownership at some point and give the ownership back to ARC later), you can instead keep a strong reference on your object you use as your context somewhere if you prefer, so that ARC does not reclaim its memory.
This can be achieved by using a #property(strong) in your class for example, affecting it to the value when you previously did your retain and assigning it to nil when you previously did your release to let the string go.
Note that if you need to keep around multiple contexts in the same class, you may opt for the option to use an NSMutableArray that keeps your context strings around instead of declaring a property for each context.
#interface YourClass ()
#property(strong) NSMutableArray* runningAnimationContexts;
#end
#implementation YourClass
-(id)init {
self = [super init];
if (self) {
self.runningAnimationContexts = [NSMutableArray array];
}
return self;
}
-(void)someMethod
{
// Example with two different parallel animations using old API
NSString * myStr = /* some local object */
[self.runningAnimationContexts addObject:myStr]; // ~ retain
[UIView beginAnimation:#"foo" context:(__bridge)myStr];
...
[UIView commitAnimations];
NSString * myStr2 = /* some other local object */
[self.runningAnimationContexts addObject:myStr2]; // ~ retain
[UIView beginAnimation:#"foo2" context:(__bridge)myStr2];
...
[UIView commitAnimations];
}
- (void)animationDidStop:(NSString *)anim finished:(NSNumber *)num context:(void *)context
{
NSString * contextStr = (__bridge NSString *)context;
// ...
[self.runningAnimationContexts removeObject:contextStr]; // ~ release
}
#end

Need clarification in iPhone memory management

I need few clarification in iPhone memory management.
Here is an example for setters;
1).
-(void)setValue:(NSString*)input{
[value autorelease];
value = [input retain];
}
In this example, why we have to use autorelease?
Can we use like as follows?
if(value)
[value release];
value = [input retain];
In the first example, Why we should not release the memory for input
2).
If I use following statement; what is the retain count for value
NSString *value;
value = #"welcome";
After the above statement, just I am trying to set one more value. Then what will happen?
eg:
value = #"For testing";
3).
What is the different between 2) and 3)?
NSString *value;
value = [input1 retain];
...
...
value = [input2 retain];// Now what is the retain count for value
4).
If I use following statement, why the app is getting crash?
NSString *value = [[[NSString alloc] init] autorelease];
...
...
Thanks in advance..
If "input" is the exact same object as "value" then calling [value release] could dealloc the object. So you must retain the new input value, release the old value, then assign the new value to the ivar:
[input retain];
[value release];
value = input;
After each of 2) and 3), the NSString *value points to a literal NSString object, the retain count will be 1 in each case, and releasing it is probably not a good idea
After this code:
value = [input2 retain];
value is an alias to the input2 object. The thing to realize is that objects have retain counts, variables do not.
As for your last case,
NSString *value = [[[NSString alloc] init] autorelease];
It creates an autoreleased empty string. If you reference that object again once the autorelease actually happens, you may get a crash because you'll be referring to an object that doesn't exist any more.
If you release a value before you retain the new value then you can have problems if the same value is being set twice. This happens if the caller hasn't retained their own copy, such as when they get the value from the same object they try to set it on, like this:
object.value = object.value;
That statement will cause the object to be released before it's retained again which could lead to the memory being deallocated and result in a dangling pointer being retained. By doing the autorelease it ensures that copying the same pointer onto itself will work correctly.
I usually write my setters as
- (void)setValue:(NString *)input {
if (value != input) {
[value release];
value = [input retain];
}
}
which avoids the problem of input and value both being the same object. If you just release it without checking then you might completely free it and the next line will try to retain an object that doesn't exist anymore.
However, it's best practice to copy strings instead of retaining them :)
(assuming you're working in a non-gc env)
1) you can create a temporary variable and release the temp after the retain/assign. otherwise, you'd need to compare the pointers. deferred release may also mask threading errors (whether you consider that good or bad is...)
2) technically, the NSString literals are valid for the life of your program. that is: while (1) [#"why won't i die?" release]; yields an infinite loop.
3) with explicit retain, alloc+init, new, and copy, you must counterbalance the retain count using release or autorelease. since you did not release value1, the static analysis may (correctly) spot that as a leak. since the string constants never die, the two aren't comparable in this regard.
4) there's nothing wrong with that staement. the problem lies elsewhere in your program. either you are assigning it to an ivar without retaining, or releasing it later.
try using static analysis often, and try reducing how much you use autorelease (you can't avoid it entirely).
none of this is magic, but you can at least reduce the location of many problems to the callsites (or very close) by not using autorelease all over the place. just use manual retain/release where possible.
lastly, check for leaks and run with NSZombies enabled.
1)
You will have to do autorelease because of the following:
When input is the same object as value and you will release value it's retain count will reach zero and get deallocated before you can retain it again though the input.
You can do it with retain but you have to change your code:
-(void)setValue:(NSString*)input{
if (value != input) {
[value autorelease];
value = [input retain];
}
}
2)
I believe #"Text" will be treated as a constant. When you want a object which you do not want any memory management with use:
NSString *value = [NSString stringWithString:#"Text"];
This will return an autoreleased object.
3) In this example it is not about the retain count of value, but about the retain count of both objects where value one is referenced to.
When you dont release input1 before you leave that method, you will have a memory management problem.
4) This statement should work. No points to argue. You rather use [NSString string].
Note:
For memory management: when you use alloc new or copy, you also have to use release or autorelease on the same object in the same scope.

Resources