Cocos2D 2.0 ARC enabled uncontrolled deallocs - objective-c

I recently migrated an existing Cocos2D project from version 0.8 to 2.0 & enabled ARC.
The way I did it is by Apple's empty application template & then adding the code from the Cocos2d 2.x template since it has major changes. After that I added code from the game & made the necessary changes for the deprecated code & for the ARC issues.
Since that the game is working but not as expected, I had no animations & the game was taking the whole CPU power. From the console I saw that everything gets dealloc-ed right after it's creation. My old code is not the reason for that because it even happens before any of my scenes gets pushed.
EDIT
I also repeated again the whole process & made an ARC-enabled version from the Cocos2D template project, but the same there too.. Is that a normal thing maybe?

That's not normal, although common problem when converting to ARC. ARC will release objects out of scope, whereas under MRC an alloc/init object would stay in memory (and leak). Check where you may need to keep a strong reference.
Here's an example that worked before converting to ARC:
-(void) someMethod
{
id object = [[MyObject alloc] init];
}
Under MRC, object stays in memory (leaks) after someMethod returns. Under ARC, ARC cleans up the object when the method returns. The simplest fix is to turn object into an ivar (aka instance variable, member of class).
Also check singletons. Depending on how its implemented, the Singleton class might dealloc right away. For example if the static instance is declared __weak or __unsafe_unretained.
You should also run the Xcode Analyzer (Build -> Analyze) to get pointers for potential issues.

Related

Managing View outlets in ios 5.0 and ios 6.0

I am working on an iPad application which is converted to ARC. Application is for os >= ios 5.0. So my doubt is that, how do I manage viewOutlets? I use -(void)didReceivememoryWarning to set ViewOutlets to nil. But in ios 5.0 and 5.1 application shows lots of memory leaks. As -(void)viewDidUnload is deprecated in ios 6.0 , I am not using the same in the application. Is the correct way to manage outlets? Please give me a quick reply.
ARC (automatic reference counting) features is available in iOS 5 and ahead (iOS 6 and so on..) version.
When you are using ARC unable in your project, you dont need to `manually release any object` that you have allocated for your stuff.
ARC will automatic handle this one.so it will beneficial for `Memory Management`.
Hope ,You understand what i m trying to say.
for more clarification , please refer below link:
To ARC or not to ARC? What are the pros and cons?
ARC, worth it or not?
Good Luck !!!
There is NO DIFFERENCE in how you code for iOS 5 vs. iOS 6.
If you are having problems, it's because you don't understand what viewDidUnload does and when it's called.
viewDidUnload is called when the view of the controller is unloaded. In iOS 6, views are never unloaded. In iOS 5 and before, views were almost never unloaded either. So in both cases, pretty much viewDidUnload is never called.
If your program is leaking stuff, it's because you are not releasing things. You always need to release instance variables in dealloc. I suspect that this is your problem.
When the controller's view is unloaded (which only happens in iOS 5 and before, but this is irrelevant), viewDidUnload allows you to optionally discard some resources to save memory, as long as they are resources that will be re-created when the view is loaded again. This usually includes properties and instance variables that are outlets (since when the view is loaded, it will load the Xib again and re-connect the outlets to new objects), as well as any variables that are set in viewDidLoad.
If you coded your program correctly, I can go in there and remove viewDidUnload, and it would not have any effect on the correctness of the program. (Since viewDidUnload is almost never called anyway.)
You should almost never have to use didReceiveMemoryWarning either.

How does the release work in ARC based projects

I know there are lot of questions on this topic. I have never worked on Non ARC based project.
I have strong & weak property as follows
#property(strong,nonatomic)NSArray *data1;
#property(unsafe_unretained, nonatomic)NSArray *data2;
I have seen in some places people explicitly make the data nil in the viewDidUnload.
i.e
-(void)viewDidUnload{
self.data1=nil;
self.data2=nil;
}
My question is if I dont do this in my case(I mean if I dont make the data1 and data2 nil
in the viewDidUnload), Will ARC release the objects automatically ?
Yes, they will be released when your view controller is deallocated. viewDidUnload (no longer called as of iOS 6) is not the same as dealloc, nor is it an "opposite" of viewDidLoad - it was only called in low memory situations, when a view was off screen.
Any transient, memory-hogging objects should be set to nil in didReceiveMemoryWarning - ARC will not do that automatically for you.
Yes, ARC will automatically release all strong-reference properties/variables just before the destruction of the parent object. As for "weak" references, they are not retained/released (that's the same with or without ARC).
Setting self.data1 = nil in viewDidUnload is usually unnecessary, but sometimes you want to make it obvious to show where your referenced object is released. If you want to be sure that your data1 is released right here at this line of code, use the code you have. If you don't care when and where it's released, you don't have to.

A few questions about ARC for iOS?

I'm fairly new to ARC for iOS (and pretty new to iOS in general) and I have a few quick questions about ARC.
~ In a View Controller, if I do not have statements in my viewDidUnload() method setting my properties to nil, will the properties' memory still be freed when my view controller is released? If so, why do I need to explicitly have this viewDidUnload method?
~ In objects that are not View Controllers, where should I set the properties to nil at? In dealloc? What about primitive properties such as #property BOOL isActive;...do I need to set them equal to nil/zero?
Thanks.
You don't need to set your properties to nil as long as they're weak references. IBOutlets should generally be weak references, since they the view controller contains a strong reference to the view, which in turn contains strong references to all of its subviews. (If you have IBOutlets that aren't part of that view hierarchy, they should be strong.)
You shouldn't need nil or zero anything, objects or scalars. Xcode will insert nilling statements when working with Interface Builder, but this is it still generating code for pre-ARC Objective-C.
You probably don't even need a viewDidUnload; it's only called in special circumstances, when there's low memory stress. Thus, you can't depend on it for cleaning up. Your IBOutlets should be weak, so they'll be cleaned automatically when the view is purged from the viewcontroller (and they'll be restored if the view is reloaded).
I'm assuming here that you're writing a new product, which means you're targeting iOS 5 or later only. If you're targeting iOS 4 in a new product, you really shouldn't be. The world has moved on, with 80% of the market on iOS 5 or later. And that's today. Going forward, it's going to be even harder to avoid iOS 5 features for an even smaller percentage of people.
Memory management for #properties is handled automatically under ARC. For times when you have set yourself as delegate, it is common to set the delegate to nil before going away (in viewWillDisapear for instance) so that future calls to delegate don't reference garbage. Stay tuned for the soon-to-be-posted WWDC videos for the latest guidance.
In viewDidUnload you need to set outlet references to nil, because ARC will release them and you do not want to accidentally use them after that happens.
You don't have to do anything with properties, they will be handled automatically. In fact you really do not normally even have a dealloc method any more with ARC.

ARC: Memory does not get reclaimed?

I am working on an iPad (only) app and I stumbled across a weird problem. The app gets terminated after a memory warning on iPad 1 but works fine on iPad 2.
I am using ARC and targeting iOS 5. I use nibs and most of my assets are displayed using UIImageViews. I also have a few hundred buttons and plenty of gesture recognizers... I re-watched the WWDC11 videos (sessions 323 and 322) on ARC and I don't seem to be doing anything special.
The app is UIImage intensive, I am doing lots of animations using UIImage. I am however using the initWithContentsOfFile constructor rather than the imageNamed call. I'm trying to prevent the images from being cached by the system.
I'm also using GCD to schedule sound effects and to animate views. I'm always doing this on the main thread.
The app uses a UINavigationController that never has more than 1 UIViewController on it's stack. I can confirm that this is true because the didReceiveMemoryWarning only gets called on the current view controller (I'm logging the call).
The thing I don't understand is why Instruments is reporting high numbers (as if the view controllers don't get deallocated) in both the Allocations and VM Tracker instruments. The Allocations instrument shows a small drop when I navigate from one view controller to another (which is what I expect) but the VM Tracker Instrument shows that the Dirty Size is not dropping when I do the same thing. Eventually the app uses too much memory and gets terminated (on iPad 1). When I get memory warnings on the iPad 2 the app does NOT get terminated though...
It feels as if my images, sounds or views don't get destroyed and the memory does not get reclaimed... My object hierarchy is very basic and there should not be any retain cycles of any sort. I don't even have simple delegates...
Do you have any suggestions? I really don't want to release this app only for the iPad 2 or newer... It's an app for kids and it would be a pitty... I'd be so much happier to learn that I'm doing something wrong, as I really want to make sure this app is the best it can be...
Cheers,
Nick
There are ways to say, 'optimise' your objects by setting their properties to nil when certain things aren't needed -- so while you can't write a dealloc method anymore, you can do self.object = nil (when pertinent) which ends up doing something like this in a non-ARC world for an 'retain' (i.e., strong) property:
- (void)setObject:(id)newObject
{
[object release]; // send release message to current object
object = newObject; // set reference to newObject
[object retain]; // send retain message to newObject
}
Now while in ARC you don't/can't write retain/release yourself in your code, the compiler inserts these calls for you, meaning that in practise, setting a property to nil would do this in the above example:
[object release]; // send release message to current object
object = nil; // set reference to nil
[object retain]; // send retain message to nil (no effect)
Moreover, this is just the tip of the iceberg -- you should make sure that there are no retain cycles in your code, which could be resulting in objects leaking without recourse to their destruction. This means, that there may be places where you're using strong references to a property (i.e., an object), when you should be using a weak property. The difference being, that strong references are retained, and weak references are assigned, the former having its retainCount incremented and the latter resulting in a property assignment that looks like this if handwritten:
- (void)setObject:(id)newObject
{
object = newObject;
}
I don't like answering my own question but I figured it could be helpful to future googlers. I implemented my own UIImage based animation and I no longer use the animationImages property. Turns out my memory issues are now gone as I no longer need to store all the images in memory and I load them as they are required using a timer.
I actually think that rolling out a custom animation is beneficial since it allows for callbacks and more powerful customisation options.
Once I'm happy with it and I feel like it's ready to be shared I will post the class(es) on GitHub.

ARC with zombies - Why do objects in instance variables not get released when owning object is deallocated? [duplicate]

I converted my app to ARC and noticed that an object alloc'ed in one of my view controllers was not being dealloc'ed when that view controller was dealloc'ed. It took a while to figure out why. I have Enable Zombie Objects on for my project while debugging and this turned out to be the cause. Consider the following app logic:
1) Users invokes action in RootViewController that causes a SecondaryViewController to be created and presented via presentModalViewController:animated.
2) SecondaryViewController contains an ActionsController that is an NSObject subclass.
3) ActionsController observes a notification via NSNotificationCenter when it is initialized and stops observing when it is dealloc'ed.
4) User dismisses SecondaryViewController to return to RootViewController.
With Enable Zombie Objects turned off, the above works fine, all objects are deallocated. With Enable Zombie Objects on ActionsController is not deallocated even though SecondaryViewController is deallocated.
This caused problems in my app b/c NSNotificationCenter continues to send notifications to ActionsController and the resulting handlers cause the app to crash.
I created a simple app illustrating this at https://github.com/xjones/XJARCTestApp. Look at the console log with Enable Zombie Objects on/off to verify this.
QUESTION(S)
Is this correct behavior of Enable Zombie Objects?
How should I implement this type of logic to eliminate the issue. I would like to continue using Enable Zombie Objects.
EDIT #1: per Kevin's suggestion I've submitted this to Apple and openradar at http://openradar.appspot.com/10537635.
EDIT #2: clarification on a good answer
First, I'm an experienced iOS developer and I fully understand ARC, zombie objects, etc. If I'm missing something, of course, I appreciate any illumination.
Second, it is true that a workaround for this specific crash is to remove actionsController as an observer when secondaryViewController is deallocated. I have also found that if I explicitly set actionsController = nil when secondaryViewController is dealloc'ed it will be dealloc'ed. Both of these are not great workaround b/c they effectively require you to use ARC but code as if you are not using ARC (e.g. nil iVars explicitly in dealloc). A specific solution also doesn't help identify when this would be an issue in other controllers so developers know deterministically when/how to workaround this issue.
A good answer would explain how to deterministically know that you need to do something special wrt an object when using ARC + NSZombieEnabled so it would solve this specific example and also apply generally to a project as a whole w/o leaving the potential for other similar problems.
It is entirely possible that a good answer doesn't exist as this may be a bug in XCode.
thanks all!
Turns out, I've written some serious nonsense
If zombies worked like I originally wrote, turning on zombies would directly lead to innumerable false positives...
There is some isa-swizzling going on, probably in _objc_rootRelease, so any override of dealloc should still be called with zombies enabled. The only thing that won't happen with zombies is the actual call to object_dispose — at least not by default.
What's funny is that, if you do a little logging, you will actually see that even with ARC enabled, your implementation of dealloc will call through to it's superclass's implementation.
I was actually assuming to not see this at all: since ARC generates these funky .cxx_destruct methods to dispose of any __strong ivars of a class, I was expecting to see this method call dealloc — if it's implemented.
Apparently, setting NSZombieEnabled to YES causes .cxx_destruct to not be called at all — at least that's what happened when I've edited your sample project:
zombies off leads to backtrace and both deallocs, while zombies on yields no backtrace and only one dealloc.
If you're interested, the additional logging is contained in a fork of the sample project — works by just running: there are two shared schemes for zombies on/off.
Original (nonsensical) answer:
This is not a bug, but a feature.
And it has nothing to do with ARC.
NSZombieEnabled basically swizzles dealloc for an implementation which, in turn, isa-swizzles that object's type to _NSZombie — a dummy class that blows up, as soon as you send any message to it. This is expected behavior and — if I'm not entirely mistaken — documented.
This is a bug that has been acknowledged by Apple in Technical Q&A QA1758.
You can workaround on iOS 5 and OS X 10.7 by compiling this code into your app:
#import <objc/runtime.h>
#implementation NSObject (ARCZombie)
+ (void) load
{
const char *NSZombieEnabled = getenv("NSZombieEnabled");
if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
{
Method dealloc = class_getInstanceMethod(self, #selector(dealloc));
Method arczombie_dealloc = class_getInstanceMethod(self, #selector(arczombie_dealloc));
method_exchangeImplementations(dealloc, arczombie_dealloc);
}
}
- (void) arczombie_dealloc
{
Class aliveClass = object_getClass(self);
[self arczombie_dealloc];
Class zombieClass = object_getClass(self);
object_setClass(self, aliveClass);
objc_destructInstance(self);
object_setClass(self, zombieClass);
}
#end
You will find more information about this workaround in my blog post Debugging with ARC and Zombies enabled.
Turns out it is an iOS bug. Apple has contacted me and indicated they've fixed this in iOS 6.
to answer the second question you would need to remove the observer from NSNotification - that will keep it from calling the view.
Normally, you would do this in the dealloc but with that zombie issue maybe it's not getting called. Maybe you could put that logic in viewDidUnload?
Because you have open NSZombieEnabled, this let the object not call dealloc, and put the object to a special place. you can close NSZombieEnabled and have try again. And double check if your code have circle retain condition.