Cocos2D v3.1 - Memory management issues with ARC - objective-c

I've began using Cocos2D v3.1 some days ago (was using v2 for my older projects), and there seems to be memory management issues when using ARC. When I profile my game, I see that my custom classes (subclassed from CCSprite) are released correctly; I have the exact same number of #persistent as the number of objects running in my scene. However, I saw that CCSpriteFrames number is constantly going up and never released. I looked around in the class CCSprite, and I found that there's no dealloc function (which can be logical because we are using ARC) but the CCSpriteFrame allocated for every sprite is never released, which means that I can stack up a good bunch of these objects, so my Memory usage keeps going up. Same thing for CCTexture when I am using particles.
So I was wondering if there's something I must do to dealloc those frames or if I am just not understanding something about ARC.
Thanks in advance for your answers!
P.S When my scene is replaced, dealloc is called in which I release all my objects by putting their references to nil, thus cascading through all my objects' dealloc method and releasing everything except CCSpriteFrame apparently.

Related

ARC reference counting dealloc, and release

I am getting a bit confused. I am creating an app with storyboard, and running it on iPad 1. the application uses a lot of memory, so reached the 120mb, crashes. accordingly to what I have understood to remove this problem you need to release, dealloc... the point is that with ARC this should be automatic. In fact if I add for e.g.: [label1 release]; it gives me an error. But this ARC automatic release and dealloc does not seem to work! Is this because there are different ways to release with ARC??
You don't need to manually retain/release/autorelease with ARC. However if you have active references to a lot of unused objects they will still remain in memory. Profile your app with Instruments and it will show you how many objects you're creating of each class and how much memory they're consuming.
With ARC you still need to think about memory usage you just don't need to worry as much about memory leaks.
NSObject *bigMemObj = [[BigMemClass alloc] init];
//This creates the object in memory. In both arc and manual counting the retain count is 1
//Do stuff
//Prior to ARC you would have had to call [bigMemObj release]; before setting the variable to nil
bigMemObj = nil
//With ARC you don't have to do anything. The compiler inserts the release at compile time
Also read the documentation on declaring iVars __strong vs __weak.
Without looking at your code it's hard to identify what is consuming all the memory but hopefully that should help you determine where to start looking.
You should implement #autoreleasePool{} inside each method. In essence, each method will look like the following:
-(void)methodName{
#autoreleasePool{
//do method stuff
}
}
This will ensure that, upon exiting the autoreleasePool, memory is properly released.
I can't vote this back up, otherwise I would. I think Alessandro is asking about ARC vs using release and dealloc, not about what he's loading!
So, Alessandro, your understanding is correct that with ARC you don't release or dealloc. Therefore, those won't work if you're using ARC. Also, there is no alternative to release/dealloc, since ARC doesn't use it.
My suggestion would be to look at what you're using in the app that is taking up all this memory. Do you have a large number of pictures, for example, that are very large? Keep any graphics as small as possible, matching the resolution of the iPad. Especially the iPad 1, which doesn't have the "retina display".
You can use Autorelease pools in ARC. Here is some documentation on when to use them:
NSAutoreleasePool Class Reference
Advanced Memory Management Programming Guide: About Memory Management

Obj-C message sent to deallocated object (EXEC_BAD_ACCESS), with iOS5, ARC

I'm developing an iOS5 App using ARC, and I started to get some random EXEC_BAD_ACCESS crashes that I cannot figure out.. By random I mean it is very unpredictable: sometimes it may take a long time to crash, sometimes short. and there is also no one specific button/tablecell/etc. that trigger the crash. Every user interaction can possibly crash the app, but you cannot repeat the crash.
I have tried to turn on NSZombie and also some malloc debugger tools. In Instruments the crash error reads: An Objective-C message was sent to a deallocated object (zombie) at address: 0x10bd1b40. And the last part of the reference count log looks like this:
475 CoursesFirstViewController Release 2 02:23.253.631 0 UIKit -[UINibDecoder finishDecoding]
476 CoursesFirstViewController Release 1 02:23.253.838 0 Foundation -[NSAutoreleasePool drain]
477 CoursesFirstViewController Zombie -1 02:35.752.420 0 Foundation objectHash
The 2, 1, -1 thing are the reference counts. I have no idea why it skips 0 and drop to -1, crashing the program (all entries but the last have continuous ref counts). Also I have no idea what objectHash is.
My app comprises of multiple functions, accessible as icons on my main screen. CoursesFirstViewController is one of the functions. Always, it is CoursesFirstViewController and objectHash that crash the app, even if I am at somewhere else. (This is the case for the log above: I went out of CoursesFirstViewController (thus returning to the main screen of my app) at 02:23, but after 12 sec, when I was in some other function, the app crashed) I only need to enter CourseFirstViewController, mess around with it for a bit, and then go elsewhere to continue using the app, and after a while it will just crash.
I'm really mad of this problem now. I have searched SO and Google for quite a while but cannot find a solution. Any help will be greatly greatly appreciated. Thanks!!
#robmayoff yea I already checked that but didn't see anything especially useful there. the last few calls are objectHash, hashProbe, [NSConcreteHashTable rehashAround], [NSConcreteHashTable removeItem] etc.
This is actually somewhat useful. This is telling us that a collection, probably an NSDictionary, but possibly an NSSet is being destroyed. From your earlier information it seems to be an autoreleased collection that is created during the nib instantiation process (so probably an ivar of CoursesFirstViewController). That's where I'd be looking anyway given the symptoms, but the crash seems to confirm it.
General recommendations are auditing any __bridge casts or unsafe_unretained properties.
Another hunch is that you've named a method in a way that violates Cocoa memory management. The most likely misnaming would be that you have a property that starts with new or copy. This would definitely be an issue if you have ARC code call misnamed non-ARC code.
I suspect one of your properties is marked "weak" when it should be "strong" and is going out of scope and getting released before you access it. ARC can't save you in that case and you will get the above random crash behavior.
If you don't have any luck finding it I would resort to doing a global search for "weak" properties on your project and double-check each one to make sure it isn't something you expect to hang around. It's tedious but honestly doesn't take that long to do and sometimes turns up more than the one bug.
I gather from your Instruments output that the zombie object is of type CoursesFirstViewController. We often use a view controller as the delegate for some other object, and most objects that have delegates do not retain their delegates.
I am assuming that you aren't using any unsafe_unretained properties or variables, or any retain-type properties. So I assume that none of your objects is keeping a dangling reference to the CoursesFirstViewController zombie.
Since the system libraries have to work with non-ARC code, they do not use ARC's weak references. So if some system object has a CoursesFirstViewController as its delegate, and the CoursesFirstViewController is destroyed but the system object is not, then the system object is left with a dangling reference to the destroyed object.
So, check whether you're using the CoursesFirstViewController as the delegate for any system objects. If so, are you sure those objects don't outlive the CoursesFirstViewController?

XCode/Instruments not showing memory leaks

I'm following the Stanford iOS development lectures and I have a Calculator brain class which has been alloc init in a controller but I haven't released it in the dealloc.
- (CalculatorBrain *)brain
{
if (!brain)
brain = [[CalculatorBrain alloc] init];
return brain;
}
I ran from XCode -> Run with Performance Tool and the app started and no leaks appeared, I then clicked the home button in the iOS Simulator and nothing, I then double clicked the home button and closed the app and still nothing.
I also did Build & Analyse and it didnt spot anything
Could you let me know why its not picking it up?
It appears as if there is no detectable leak. Look at this line:
brain = [[CalculatorBrain alloc] init];
As long as brain points to an object, that object won't be considered a "memory leak". If at some point you do this,
brain = nil;
Then the leak will register. Deallocating the container object will also achieve this, but are you sure it's being deallocated? (It won't be deallocated when your program exits, for example.)
The problem: Leak detectors cannot detect all memory leaks -- this is a mathematically proven fact. Most detectors only detect unreachable objects, and many leak detectors are especially susceptible to false negatives -- in C it is hard to tell the difference at runtime between a pointer and an integer.
Edit: It sounds like your application only ever creates one instance of the controller, which only creates one instance of CalculatorBrain. If you think about what a memory leak really is, you can define it as unused memory that your program does not release back to the operating system.
While the program is running, CalculatorBrain is always in use and therefore it is not a leak.
When the program exits, the operating system automatically reclaims all memory used by your process, therefore there cannot be any memory leaks after a program exits.
If you want to create a leak to see what it looks like, you could create a new CalculatorBrain multiple times while your program is running, but forget to release the unused versions. In this scenario, as your program runs, more and more instances of CalculatorBrain would accumulate. On iOS and other embedded systems, this will generally crash your program. On a modern 64 bit computer it will gradually fill up the available swap space until you run out of swap, address space, or some other resource -- causing the program to crash or making the system very unresponsive.
Standard practice is to not care about deallocating objects which are supposed to exist for the entire program's run.
The analyzer cannot find all memory leaks. As far as it is concerned, storing the instance into the ivar doesn't leak it from that method, and then in dealloc it doesn't realize that the ivar should be released. XCode 4 may have improved in this respect, I don't recall (I still use XCode 3 myself).
As for the performance tool, remember that an object won't be considered leaked until nothing holds a reference to it anymore. So even though your controller doesn't deallocate the brain, the brain won't be considered leaked until the controller is deallocated (or receives a brain transplant). Also, note that on *nix-like systems, memory allocations are automatically cleaned up on process exit. So it isn't really a leak if you allocate memory for objects that should exist for the lifetime of your process (e.g. the app delegate and anything it permanently holds on to) and rely on this behavior to free it on process exit.
Well... it's true that leaks can't detect all memory leaks, but let's say that you are doing this:
myIvarBrain=[self brain];
If you are giving it to an iVar (released in the dealloc of your class, and without accessors), actually there is no leak at all. The returned RC is one and it will be one since the deallocation of your class. If you don't release it in the dealloc, you should wait a dealloc of your class to see a memory leak.
Does make sense?

Does Objective-C have garbage collector on the iPhone?

I'm thinking of writing an app for the iphone. But I'm confused in regards to the objective-c language. Am I correct in saying that I have to do my own memory management? Is Objective-C a managed language such as C# and/or Java?
Thank you!
"Managed" is a marketing term that has no technical meaning. Objective C for the iPhone is not garbage collected, but uses retain counting memory management. So in some sense, you have to do your own memory management, but it's limited to knowing the Cocoa retain counting methodology.
Edit: My comments on "managed" refer to a previous post version. The comment is left here because I'm tired of seeing the word.
There is no garbage collection feature. However, any object you copy, retain, alloc etc. has its retain count bumped up by one, and you are responsible for calling a releaseor an autorelease - you own an instance of that object. If you don't call [<YOUROBJECT> release]; or [<YOUROBJECT> autorelease];, the object remains in the iPhone's/iPod Touch's/iPad's memory, but the pointer no longer remains, and a memory leak occurs, as that bit of memory can no longer be accessed. Autoreleasing adds the object to the autorelease pool, and means that it will become released at some point in the future. Always use a standard release where possible, as autorelease means that it will still stay in the device's memory for a while, you can never be sure when it will be released, and it is a slightly more demanding method to call.
Never call dealloc on an object - releasing or autoreleasing is sufficient, and if required, the object will automatically be dealloced.
Make sure you never release objects you do not own, and if you do release objects, you release them after you no longer have any use of them - otherwise, you may try to access that object, and the device cannot find the object, and the situation results in your app crashing due to an EXEC_BAC_ACCESS error - to find out the root of the problem, open Instruments, add the Object Allocation instrument and enable NSZombie detection in its preferences (or add the Zombies instrument (only available in iPhone SDK 3.2+)). You can then view the entire history of the object which causes the problem, and jump to the exact line of code that caused the problem.
You can read more here.
Hope this helped
As others have pointed out Objective-C has no garbage collector on the iPhone, but it does have one for Mac OSX. Here is an article describing this in more detail: http://vasudevkamath.blogspot.com/2010/01/objective-c-my-opinions.html
You have to do your own memory management with Objective-C on the iPhone.
The answer isn't specific to Objective-C. If you're doing iPhone development, no...there is no garbage collector. If you're developing for Mac OSX, however, there is garbage collection.

Track all Objective-C's alloc/allocWithZone/dealloc

Sorry for long description, however the questions aren't so easy...
My project written without GC. Recently I found a memory leak that I can't find. I did use new Xcode Analyzer without a result. I did read my code line by line and verified all alloc/release/copy/autorelease/mutableCopy/retain and pools... - still nothing.
Preamble: Standard Instruments and Omni Leak Checker don't work for me by some reason (Omin Tool rejects my app, Instruments.app (Leaks) eats too many memory and CPU so I have no chance to use it).
So I wanna write and use my own code to hook & track "all" alloc/allocWithZone:/dealloc messages statistics to write some simple own leaks checking library (the main goal is only to mark objects' class names with possible leaks).
The main hooking technique that I use:
Method originalAllocWithZone = class_getClassMethod([NSObject class],#selector(allocWithZone:));
if (originalAllocWithZone)
{
imp_azo = (t_impAZOriginal)method_getImplementation(originalAllocWithZone);
if (imp_azo)
{
Method hookedAllocWithZone = class_getClassMethod([NSObject class],#selector(hookedAllocWithZone:));
if (hookedAllocWithZone)
{
method_setImplementation(originalAllocWithZone,method_getImplementation(hookedAllocWithZone));
fprintf(stderr,"Leaks Hook: allocWithZone: ; Installed\n");
}
}
}
code like this for hook the alloc method, and dealloc as NSObject category method.
I save IMP for previous methods implementation then register & calculate all alloc/allocWithZone: calls as increment (+1) stat-array NSInteger values, and dealloc calls as decrement (-1).
As end point I call previous implementation and return value.
In concept all works just fine.
If it needs, I can even detect when class are part of class cluster (like NSString, NSPathStore2; NSDate, __NSCFDate)... via some normalize-function (but it doesn't matter for the issues described bellow).
However this technique has some issues:
Not all classes can be caught, for
example, [NSDate date] doesn't catch
in alloc/allocWithZone: at all, however, I can see alloc call in GDB
Since I'm trying to use auto singleton detection technique (based on retainCount readind) to auto exclude some objects from final statistics, NSLocale creation freezes on pre-init stage when starting of full Cocoa application (actually, even simple Objective-C command line utility with the Foundation framework included has some additional initialization before main()) - by GDB there is allocWithZone: calls one after other,....
Full Concept-Project draft sources uploaded here: http://unclemif.com/external/DILeak.zip (3.5 Kb)
Run make from Terminal.app to compile it, run ./concept to show it in action.
The 1st Question: Why I can't catch all object allocations by hooking alloc & allocWithZone: methods?
The 2nd Question: Why hooked allocWithZone: freezes in CFGetRetainCount (or [inst retainCount]) for some classes...
Holy re-inventing the wheel, batman!
You are making this way harder than it needs to be. There is absolutely no need whatsoever to roll your own object tracking tools (though it is an interesting mental exercise).
Because you are using GC, the tools for tracking allocations and identifying leaks are all very mature.
Under GC, a leak will take one of two forms; either there will be a strong reference to the object that should long ago been destroyed or the object has been CFRetain'd without a balancing CFRelease.
The collector is quite adept at figuring out why any given object is remaining beyond its welcome.
Thus, you need to find some set of objects that are sticking around too long. Any object will do. Once you have the address of said object, you can use the Object Graph instrument in Instruments to figure out why it is sticking around; figure out what is still referring to it or where it was retained.
Or, from gdb, use info gc-roots 0xaddr to find all of the various things that are rooting the object. If you turn on malloc history (see the malloc man page), you can get the allocation histories of the objects that are holding the reference.
Oh, without GC, huh...
You are still left with a plethora of tools and no need to re-invent the wheel.
The leaks command line tool will often give you some good clues. Turn on MallocStackLoggingNoCompact to be able to use malloc_history (another command line tool).
Or use the ObjectAlloc instrument.
In any case, you need to identify an object or two that is being leaked. With that, you can figure out what is hanging on to it. In non-GC, that is entirely a case of figuring out why it there is a retain not balanced by a release.
Even without the Leaks instrument, Instruments can still help you.
Start with the Leaks template, then delete the Leaks instrument from it (since you say it uses too much memory). ObjectAlloc alone will tell you all of your objects' allocations and deallocations, and (with an option turned on, which it is by default in the Leaks template) all of their retentions and releases as well.
You can set the ObjectAlloc instrument to only show you objects that still exist; if you bring the application to the point where no objects (or no objects of a certain class) should exist, and such objects do still exist, then you have a leak. You can then drill down to find the cause of the leak.
This video may help.
Start from the Xcode templates. Don't try to roll your own main() routine for a cocoa app until you know what you're doing.