Heap growth related to use of CCLabelTTF - objective-c

I am using Cocos2d 1.01. I am having undesired heap growth. To identify what is causing the growth I took a baseline snapshot, did a state change and return to zero state and repeated the heapshot. I found the primary cause of the heap growth to be non-object and then looked at the stack trace, noting that the problematic code appeared to be centered around CCLabelTTF.
Here is the code that seems to be problematic:
NSString *desc = [pEffectDescriptions objectAtIndex:i];
CCLabelTTF *descrptionLabel = [CCLabelTTF labelWithString:desc dimensions:CGSizeMake(290, 65) alignment:UITextAlignmentLeft fontName:#"Verdana-Italic" fontSize:10];
descrptionLabel.anchorPoint = ccp(0,0);
descrptionLabel.color = ccc3(192, 192, 192);
descrptionLabel.position = ccp(aSprite.position.x + 8, aSprite.position.y);
[self addChild:descrptionLabel z:10 tag:COMPARTMENT0+9600+i];
I don't understand what the problem is, because before returning to state zero, the following code is executed:
for (int i=0; i<1000; i++) {
if ([self getChildByTag:COMPARTMENT0+9000+i])
[self removeChildByTag:COMPARTMENT0+9000+i cleanup:true];
}
My reasoning is that the CCLabelTTF is owned by the layer and it in turn owns the NSString (the array also retains the NSString). However, when I remove the CCLabelTTF from the layer and its dealloc gets called it should therefore release the CCLabelTTF, which would then dealloc. Could the array reference to the NSString be responsible for preventing the deallocation of CCLabelTTF?
Anyone have a clue?

While adding labels you add 9600 to the tag, while removing you only add 9000 to the tag. Could that be it?
PS: I suggest using bitmap fonts, they use less memory, create, update and render faster.

Related

SKPhysicsBody bodyWithPolygonFromPath memory leaks

I have a strange memory leaks when creating Sprite Kit physics bodies with custom shapes. This is how my implementation looks:
CGFloat offsetX = self.frame.size.width * self.anchorPoint.x;
CGFloat offsetY = self.frame.size.height * self.anchorPoint.y;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 4 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 66 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 35 - offsetX, 57 - offsetY);
CGPathCloseSubpath(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
CGPathRelease(path);
Everything is going on inside SKSpriteNode method. Instruments tells me about several memory leaks after creating such bodies:
Leaked object:
Malloc 32 Bytes
Size:
32 Bytes
Responsible Library:
PhysicsKit
Responsible Frame:
std::__1::__split_buffer<PKPoint, std::__1::allocator<PKPoint>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<PKPoint>&)
The CGPathRelease(path); line is necessary - without it I'm getting more memory leaks about CGPath which is understandable. When I'm using this implementation instead (for testing purposes):
CGFloat radius = MAX(self.frame.size.width, self.frame.size.height) * 0.5f;
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
...everything is working well, without memory leaks. I wonder if this is Sprite Kit bug or I'm doing something wrong.
This is a bug in sprite kit. You will have to wait for a fix.
Does anything change if you do the following?
CGPathRef pathCopy = CGPathCreateCopy(path);
CGPathRelease(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathCopy];
CGPathRelease(pathCopy);
The only restriction on the path parameter is:
A convex polygonal path with counterclockwise winding and no self intersections. The points are specified relative to the owning node’s origin.
I do not know the values of offsetX and offsetY so I do not know whether the path is correct or not, but assuming that they are both 0, it seems to me like this path is clockwise and not counterclockwise. I would create the path using constants and no variables, just to make sure that it is correct and if it is still leaking I would say it's a bug in PhysicsKit.
As commented in several places, this looks like a bug in the implementation of SKPhysicsBody, which persists at least until iOS 7.1. The reason for this is:
SKPhysicsBody holds an instance variable, '_path', which holds a copy of the initial CGPathRef passed when calling 'bodyWithEdgeChainFromPath' or similiar constructors. This instance variable never gets released, so all paths will stay in memory.
However, you may implement a workaround for this by
(1) subclassing the SKShapeNode, that should hold the SKPhysicsBody,
(2) after creating and assigning the SKPhysicsBody for this node, retrieve the instance variable referring to the CGPathRef of the SKPhysicsBody,
(3) when the shape node is deallocated, check the retain count of the path. If it's > 0, release it and the memory leaks are gone.
There's little code overhead (beside subclassing all shape nodes, that use physics bodies relying on CGPath's). Just do this:
Add an instance variable to your subclass:
{
CGPathRef myPath;
}
Implement the method to retrieve the value for this CGPath in any subclassed SKShapeNode implementation. You might also consider to add this as a general category on SKNode:
- (CGPathRef) getPhysicsBodyPath
{
CGPathRef path = nil;
if ( self.physicsBody ){
object_getInstanceVariable(self.physicsBody, "_path", (void**) &path);
}
return(path);
}
This code will return the CGPathRef instance used by the node's physics body. Note, however, that this must be done immediately after assigning the physics body to the node. At a later time (i.e. in dealloc(), this might return a null value. So, after creating the body, store this value in the instance variable 'myPath'. To make this code work even after Apple might fix the bug, we'll add an additonal retain, which will make sure we can access this object once our SKNode is deallocated (see below):
/*
* we need to keep a copy of the final path reference, that has been created for the physics body.
* retrieving this value during deallocation won't work any more...
*/
myPath = CFRetain([self getPhysicsBodyPath]);
Finally, overwrite the 'dealloc' method and release the path, after your SKNode is released:
- (void) dealloc
{
self.physicsBody = nil;
[super dealloc];
if ( myPath != nil ) {
/* this will work, because we've retained the path for this instance */
CFIndex rc = CFGetRetainCount (myPath);
/* this is our own release ... */
CGPathRelease(myPath);
/* in case Apple has fixed the release, this is OK, otherwise we'll need
* to release the path twice to avoid memory leaks
*/
if ( rc > 1 ) {
CGPathRelease(myPath);
}
}
}
This will finally release this path and fix the memory leaks. This code works for all iOS version up to 7.1 and it also should work on further versions, once Apple finally fixes this bug and SKPhysicsBoy actually releases the path (as they are supposed to do).
I've also experienced similar memory leak, but it was fixed after I removed one line from my viewDidLoad function
skView.showsPhysics = YES;

Creating autoreleased object inside GLKViewController run loop causes malloc error

I'm currently using GLKit to do some OpenGL drawing. I created a normal UIViewController and then added a GLKViewController subclass inside a container to do my drawing. While everything runs fine initially, if I let my program run for a period of time (perhaps 15-30 minutes), eventually it crashes and gives me the following error.
malloc: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
So I turned on the malloc_error_break breakpoint, and the stack trace points to the following code.
-(NSArray*)meshes:(NSArray *)meshes sortedFromFrontToBack:(BOOL)sortedFromFrontToBack
{
NSMutableArray *sortedMeshes = meshes.mutableCopy;
[sortedMeshes sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
DSNode *mesh1 = obj1;
DSNode *mesh2 = obj2;
GLKVector3 depth1 = isnan(mesh1.boundingSphere.radius) ? mesh1.transformationState.position : mesh1.boundingSphere.center;
GLKVector3 depth2 = isnan(mesh2.boundingSphere.radius) ? mesh2.transformationState.position : mesh2.boundingSphere.center;
GLKMatrix4 mesh1ToCameraSpace = [mesh1 nodeToOtherNodeTransform:self];
GLKMatrix4 mesh2ToCameraSpace = [mesh2 nodeToOtherNodeTransform:self];
GLKVector3 depth1InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh1ToCameraSpace, depth1);
GLKVector3 depth2InCameraSpace = GLKMatrix4MultiplyVector3WithTranslation(mesh2ToCameraSpace, depth2);
NSNumber *n1 = [NSNumber numberWithFloat:depth1InCameraSpace.z];
NSNumber *n2 = [NSNumber numberWithFloat:depth2InCameraSpace.z]; /* Breakpoint triggered here */
if(sortedFromFrontToBack)
{
return [n2 compare:n1];
}
return [n1 compare:n2];
}];
return sortedMeshes;
}
As I commented, the [NSNumber numberWithFloat:] call throws the malloc error. This method gets called once each frame from my GLKViewController's drawInRect method. Essentially, I have a class which keeps track of my cameras and the meshes which are going to be drawn by OpenGL, and it sorts them in camera space from either front to back for opaque meshes or back to front for transparent before drawing them.
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(self.clearColor.r, self.clearColor.g, self.clearColor.b, self.clearColor.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DSDirector *director = [DSDirector sharedDirector];
for(DSMesh *mesh in director.opaqueMeshes)
{
[mesh draw];
}
/* The director class keeps track of my scene's cameras and meshes and calls the above method to return the scene's transparent meshes properly sorted */
for(DSMesh *mesh in director.transparentMeshes)
{
[mesh draw];
}
}
From what I've read, the autorelease pool should drain at the end of each run loop, so I wouldn't think that creating a bunch of autoreleased objects every frame is an issue, they should all get flushed each frame. I've profiled my program and checked for any leaks and can't find any, and I'm using ARC as well, which should minimize the risk. When I profile it, the live bytes total never budges, although the overall bytes rises quite quickly, and no leaks are found. In addition, didReceiveMemoryWarning never fires. I'm stumped.
Something smells wrong about the NSNumber creation being the cause of your malloc error. Testing with Instruments might help find the real culprit.
However, you're doing two things you don't need to, so there's some chance that eliminating them might help with your problem.
First, you don't need to wrap floats in an NSNumber to compare them. The basic comparison and mathematical operators work just fine, and don't require extra time or memory for object creation:
if (depth1InCameraSpace.z < depth2InCameraSpace.z)
return NSOrderedAscending;
else if (depth1InCameraSpace.z > depth2InCameraSpace.z)
return NSOrderedDescending;
else
return NSOrderedSame;
Second, the Tile-Based Deferred Rendering strategy implemented by the GPU hardware on iOS devices does its own hidden surface removal optimizations -- sorting opaque geometry front to back is redundant, so all it does is waste CPU time. (There's a decent explanation of this is in the OpenGL ES Hardware Platform Guide for iOS.) You should still sort translucent geometry back to front (and draw it after opaque geometry) for proper blending, though.
So, I think I've figured out what was going on. At some prior point I'd enabled NSZombies and then forgot about it, and so all of my objects that I expected to be deallocated were actually still hanging around. When profiling in Instruments, zombies must not be counted as live, which is why I couldn't figure out why I was seemingly running out of memory when the number of live bytes wasn't climbing. However, when I upgraded to XCode 5, the new memory gauge showed that I was quickly racking up memory, and then when I dropped into Instruments, Leaks was covered up by a warning that said that it would not work properly since NSZombies were enabled. So, I disabled zombies, and now my memory usage is staying constant, just like I expected it to.

iOS Application Crash while UIImage loading (virtual memory not cleaning up)

I have a weird crash in my application without any trace. this is probably a memory related problem but with very little information & I'm not sure how to proceed or fix it. If it wasnt for instruments would have been left with no clue what so ever.
I have an image array (in this example an array of size 2) where I load an image, create an image context & draw and save it into the array. Everytime the method is called image array objects are replaced with the new content. In instruments I see a very huge Virtual Memory usage during this method call & apparently after each call memory is not cleared & hence crashes. The project is ARC. I'll list down code below. This is all we need to recreate this issue. (the image I'm using is little big in size about 7MB, so its easier to recreate crash). Also i'm using iPad2 device.
+ (UIImage *)imageCopy:(UIImage *)src
{
UIGraphicsBeginImageContext(src.size);
[src drawAtPoint:CGPointZero];
UIImage *r = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return r;
}
- (IBAction)buttonTouch:(id)sender
{
for (int i=0; i<2; i++)
{
if (i==0)
{
self.mImage = [UIImage imageNamed:#"LARGE_elevation.jpg"];
}
else
{
self.mImage = [UIImage imageNamed:#"LARGE_elevation2.jpg"];
}
// imageArray is a NSMutableArray with capacity of 2
[imageArray setObject:[ViewController imageCopy:self.mImage] atIndexedSubscript:i];
}
((UIImageView *)[self.view viewWithTag:100]).image = self.mImage;
}
Here is a screen from instruments where it crash on 2nd time after memory warnings are issued.
I dont see any big issue with the "imageCopy" method I'm using here.
Any help on this is really appreciated.
Thanks & Cheers,
I found out that it was a cyclic reference issue. So when new content replaces the old content in the array, the past objects were still remaining. It was quite an interesting finding because in the memory leaks analyser it showed up as few KB data leak which you wont suspect as the non freed virtual memory was few hundred Megabytes (MB).
As a very abstract example.
ClassA
#property (strong) ClassB *obj
----------
ClassB
#property (strong) ClassA *obj
- (id)initWithA:(ClassA *)objA;
----------
So when you remove A neither object will be deallocated properly. In my case leak traced by the leak analyser was few KB for both of the objects even though the CoreGraphics calculations were hanging onto about 200MB data in virtual memory.
Fix was to mark the A reference in ClassB as weak.
ClassB
#property (weak) ClassA *obj
- (id)initWithA:(ClassA *)objA;
Verdict
Never under estimate a memory leak, no matter how big or small & arc or mrc
The problem is probably that the method imageNamed: caches the images loaded, and there is apparently no way to clear the cache after a memory warning programmatically.
Instead of imageNamed:, you could use other methods like initWithData: that do not cache the images. You will find a detailed discussion here.

Do loops and convenience methods cause memory peaks with ARC?

I'm working with ARC and seeing some strange behavior when modifying strings in a loop.
In my situation, I'm looping using NSXMLParser delegate callbacks, but I see the same exact behavior and symptoms using a demo project and sample code which simply modifies some NSString objects.
You can download the demo project from GitHub, just uncomment one of the four method calls in the main view controller's viewDidLoad method to test the different behaviors.
For simplicity's sake, here's a simple loop which I've stuck into an empty single-view application. I pasted this code directly into the viewDidLoad method. It runs before the view appears, so the screen is black until the loop finishes.
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
NSString *newText = [text stringByAppendingString:#" Hello"];
if (text) {
text = newText;
}else{
text = #"";
}
}
The following code also keeps eating memory until the loop completes:
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = #"";
}
}
Here's what these two loops loop like in Instruments, with the Allocations tool running:
See? Gradual and steady memory usage, until a whole bunch of memory warnings and then the app dies, naturally.
Next, I've tried something a little different. I used an instance of NSMutableString, like so:
NSMutableString *text;
for (NSInteger i = 0; i < 600000000; i++) {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
This code seems to perform a lot better, but still crashes. Here's what that looks like:
Next, I tried this on a smaller dataset, to see if either loop can survive the build up long enough to finish. Here's the NSString version:
NSString *text;
for (NSInteger i = 0; i < 1000000; i++) {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = #"";
}
}
It crashes as well, and the resultant memory graph looks similar to the first one generated using this code:
Using NSMutableString, the same million-iteration loop not only succeeds, but it does in a lot less time. Here's the code:
NSMutableString *text;
for (NSInteger i = 0; i < 1000000; i++) {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
And have a look at the memory usage graph:
The short spike in the beginning is the memory usage incurred by the loop. Remember when I noted that seemingly irrelevant fact that the screen is black during the processing of the loop, because I run it in viewDidLoad? Immediately after that spike, the view appears. So it appears that not only do NSMutableStrings handle memory more efficiently in this scenario, but they're also much faster. Fascinating.
Now, back to my actual scenario... I'm using NSXMLParser to parse out the results of an API call. I've created Objective-C objects to match my XML response structure. So, consider for example, an XML response looking something like this:
<person>
<firstname>John</firstname>
<lastname>Doe</lastname>
</person>
My object would look like this:
#interface Person : NSObject
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#end
Now, in my NSXMLParser delegate, I'd go ahead and loop through my XML, and I'd keep track of the current element (I don't need a full hierarchy representation since my data is rather flat, it's a dump of a MSSQL database as XML) and then in the foundCharacters method, I'd run something like this:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if((currentProperty is EqualToString:#"firstname"]){
self.workingPerson.firstname = [self.workingPerson.firstname stringByAppendingString:string];
}
}
This code is much like the first code. I'm effectively looping through the XML using NSXMLParser, so if I were to log all of my method calls, I'd see something like this:
parserDidStartDocument:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didStartElement:namespaceURI:qualifiedName:
parserDidEndDocument:
See the pattern? It's a loop. Note that it's possible to have multiple consecutive calls to parser:foundCharacters: as well, which is why we append the property to previous values.
To wrap it up, there are two problems here. First of all, memory build up in any sort of loop seems to crash the app. Second, using NSMutableString with properties is not so elegant, and I'm not even sure that it's working as intended.
In general, is there a way to overcome this memory buildup while looping through strings using ARC? Is there something specific to NSXMLParser that I can do?
Edit:
Initial tests indicate that even using a second #autoreleasepool{...} doesn't seem to fix the issue.
The objects have to go somewhere in memory while thwy exist, and they're still there until the end of the runloop, when the autorelease pools can drain.
This doesn't fix anything in the strings situation as far as NSXMLParser goes, it might, because the loop is spread across method calls - need to test further.
(Note that I call this a memory peak, because in theory, ARC will clean up memory at some point, just not until after it peaks out. Nothing is actually leaking, but it's having the same effect.)
Edit 2:
Sticking the autorelease pool inside of the loop has some interesting effects. It seems to almost mitigate the buildup when appending to an NSString object:
NSString *text;
for (NSInteger i = 0; i < 600000000; i++) {
#autoreleasepool {
if (text) {
text = [text stringByAppendingString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
}
The Allocations trace looks like so:
I do notice a gradual buildup of memory over time, but it's to the tune of about a 150 kilobytes, not the 350 megabytes seen earlier. However, this code, using NSMutableString behaves the same as it did without the autorelease pool:
NSMutableString *text;
for (NSInteger i = 0; i < 600000000; i++) {
#autoreleasepool {
if (text) {
[text appendString:#" Hello"];
}else{
text = [#"" mutableCopy];
}
}
}
And the Allocations trace:
It would appear that NSMutableString is apparently immune to the autorelease pool. I'm not sure why, but at first guess, I'd tie this in with what we saw earlier, that NSMutableString can handle about a million iterations on its own, whereas NSString cannot.
So, what's the correct way of resolving this?
You are polluting the autorelease pool with tons and tons of autoreleased objects.
Surround the internal part of the loop with an autorelease pool:
for (...) {
#autoreleasepool {
... your test code here ....
}
}
While you're hunting memory-related bugs, you should note that #"" and #" Hello" will be immortal objects. You can think about it as a const, but for objects. There will be one, and only one, instance of this object in memory the entire time.
As #bbum pointed out, and you verified, the #autoreleasepool is the correct way to deal with this in a loop.
In your example with the #autoreleasepool and NSMutableString, the pool doesn't really do much. The only mortal object inside the loop is your mutableCopy of #"", but that will only be used once. The other case is just an objc_msgSend to a persisting object (the NSMutableString), which only references an immortal object and a selector.
I can only assume the memory build up is inside Apple's implementation of NSMutableString, although I can wonder why you'd see it inside the #autoreleasepool and not when it's absent.

objc leak behavior I can't explain

The following code loop does not leak memory (as verified by watching it loop infinitely under "top");
NSBitmapImageRep *this_bmap = 0;
while (1) {
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
[this_bmap release];
CGImageRelease(windowImage);
}
and I would not expect it to. However, when I copy a pointer to the bitmap data, like this:
NSBitmapImageRep *this_bmap = 0;
while (1) {
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
void *pixels1 = [this_bmap bitmapData];
[this_bmap release];
CGImageRelease(windowImage);
}
this now leaks like crazy. I can see this happening rapidly under "top" and the program eventually grinds to a halt.
I am new to Objective-C, but I am not new to programming and I can't understand this behavior. The documentation for the method bitmapData claims it simply returns a pointer (as opposed to allocating something), so I am stumped. I found a similar question from some time ago, but the only answer was to "look into pools" and I don't see how that could help here.
Any ideas what's going on here?
Accessing the pixel data causes the object to be retained and autoreleased so that the bitmap data doesn't suddenly go away unexpectedly. To see your expected results (i.e., loop not consuming memory with each iteration), rewrite as:
NSBitmapImageRep *this_bmap = 0;
while (1) {
NSAutoreleasePool* loopPool = [NSAutoreleasePool new];
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
void *pixels1 = [this_bmap bitmapData];
[this_bmap release];
CGImageRelease(windowImage);
[loopPool drain];
}
hmmm, I'm no guru so I'm just guessing here. But it occurs to me that every time through to loop you are effectively declaring a new variable called pixels1. Therefore each time through you allocate some new space rather than reuse it. Try moving the declaration of pixels1 outside the loop and see what happens.
Note, this is just a guess and may be wrong. A more knowledgable person may be able to give you a definitive answer.
The "initWithCGImage:" call has this in its documentation:
Discussion
If you use this method, you
should treat the resulting bitmap
NSBitmapImageRep object as read only.
Because it only retains the value in
the cgImage parameter, rather than
unpacking the data, accessing the
pixel data requires the creation of a
copy of that data in memory. Changes
to that data are not saved back to the
Core Graphics image.
That seems to indicate that the memory returned from bitmapData needs to be CFRelease'ed.