views:

976

answers:

3

I've made a test iPhone app to try and understand why I may be leaking memory in some other projects. I'm also a newbie when it comes to Obj-C/iPhone.

Basically the view controller creates 5 randomly colored boxes which are themselves sub-classed UIViews.

double-Clicking on any of these boxes fires off a notification to the view controller. The controller then goes through and deletes the current 5 boxes, and creates 5 more random one in there place. double-Clicking any again continues on the same.

Here's the console output of my NSLogs when i double-click the 1st box, then the 4th box, then the 1st box again:

Deleting boxes in Box View
------------------------------------
Deleting boxes in Box View
Box view retain count before Remove: 10
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 2
------------------------------------
Deleting boxes in Box View
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 13
Box view retain count before Remove: 2
------------------------------------
Deleting boxes in Box View
Box view retain count before Remove: 10
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 2
Box view retain count before Remove: 2
------------------------------------

What i'm trying to understand is why the retain counts before I remove the boxes is 2. And also why the box I click on is incrementally higher. I figure the the increase for the one i click is because of the notification stuff, by why are they incrementally different?

Will a retain count of 2 before the remove mean that i'm leaking these views.

including my code for both the viewcontroller and the UIView bluebox class.

Any help greatly appreciated. I've tried running the Leaks instrument on it but it just crashes with EXC_BAD_ACCESS, so i'm stuck trying to figure out with just regular debug.

Controller

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>;


@interface BoxViewController : UIViewController 
{
    UIView *boxview;
}
@property (nonatomic, retain) UIView *boxview;

-(void)cleanUp;
-(void)doTestLayout;
@end

#import "BoxViewController.h"
#import "BlueBox.h"
#import <Foundation/NSNotification.h>

@implementation BoxViewController

@synthesize boxview;


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {

    UIView* newview = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,480)];
    newview.backgroundColor = [UIColor clearColor];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeupEventReceived:) name:@"nodeClicked" object:nil];

    boxview = newview;

    [self.view addSubview:boxview];
    [self doTestLayout];

    [super viewDidLoad];

    [newview release];

}


-(void)doTestLayout
{

    [self cleanUp];

    int x_offset = 10;

    BlueBox *bb;
    for(int i=0; i < 5; i++)
    {
     bb = [[BlueBox alloc] init];
     CGRect cFrame = CGRectMake(x_offset,100, 30, 30);
     bb.frame = cFrame;
     //NSLog(@"Placing Box#%i",i);
     //NSLog (@"Box retain count: %d", [bb retainCount]);


     [boxview addSubview:bb];
     //NSLog (@"Box view retain count after adding to view: %d", [bb retainCount]);

     x_offset += 40;

     [bb release];

     //NSLog (@"Boxview retain count after release: %d", [bb retainCount]);
    }

}

-(void) cleanUp
{
    NSLog(@"Deleting boxes in Box View");
    if(boxview)
    {
     for (UIView *view in boxview.subviews) {

      NSLog (@"Box view retain count before Remove: %d", [view retainCount]);
      [view removeFromSuperview];
     }

    }
    NSLog(@"------------------------------------");


}



-(void) nodeupEventReceived:(id)sender
{
    //NSLog(@"Event received - Relayout");
    [self doTestLayout];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [boxview release];
    [super dealloc];
}

@end

UIView class

#import <UIKit/UIKit.h>


@interface BlueBox : UIView {

}

@end
#import "BlueBox.h"


@implementation BlueBox

- (void)drawRect:(CGRect)rect {
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetRGBStrokeColor(context, .48, .53, .67, 1.0);

    CGFloat red = (CGFloat)random()/(CGFloat)RAND_MAX;
    CGFloat green = (CGFloat)random()/(CGFloat)RAND_MAX;
    CGFloat blue = (CGFloat)random()/(CGFloat)RAND_MAX;
    CGContextSetRGBFillColor(context, red, green,blue, 1.0);

    CGContextSetLineWidth(context, 2.0);


    CGRect rrect = CGRectMake(0,0, 30, 30);
    CGFloat radius = 5.0;

    CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
    CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);

    CGContextMoveToPoint(context, minx, midy);
    CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
    CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
    CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
    CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
    CGContextClosePath(context);
    // Fill & stroke the path
    CGContextDrawPath(context, kCGPathFillStroke);


}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    //NSLog(@"Touched");

    //Number of touches on the screen
    NSSet *allTouches = [event allTouches];
    switch ([allTouches count])
    {
     case 1:
     {
      //Get the first touch.
      UITouch *touch = [[allTouches allObjects] objectAtIndex:0];

      switch([touch tapCount])
      {
       case 1://Single tap
        //NSLog(@"Single touch");
        break;
       case 2://Double tap.
       {
        //NSLog(@"Double touch.");
        [[NSNotificationCenter defaultCenter] postNotificationName:@"nodeClicked" object:self];
        break;
       }
      }
     } 
      break;
    }
}


- (void)dealloc {
    [super dealloc];
}


@end
A: 

The 2 retain count is most likely from the newView and boxView in your first function. Since both variables are accessing that same view, it gets retained twice (on creation and on copy). I would just use boxView from the start and see if that affects anything.

I'm not sure what the 10, 13, and 10 counts are because of, there might be some quick retains done by default when you click something.

I would suggest leaks to help with your problem, but I guess you can't get that to run. I would suggest building your test project from scratch again, just so you can have that resource.

TahoeWolverine
removed the newview and changed alloc to:boxview = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,480)];retain counts are still 2
Really, darn. I thought I was just starting to get the hang of how the retain/release system works. I'm afraid I can't be of much more help, but I'm sure hitting the apple docs might clear things up a bit...
TahoeWolverine
i was able to get leaks instrument working by recreating project from scratch using same code. no leaks are being detected and Net Bytes in ObjectAlloc seems to be hold steady at 273600 for All Allocations. Does that mean that's how much memory the app is currently using, therefore no memory leak?
Well, I think that should be the total that you've used. You should be able to click through the pointer to a chunk of memory and see where it has been retained. This should show you why you get 2s, 10s, and 13s...
TahoeWolverine
A: 

The 2 retain count is most likely from the newView and boxView in your first function. Since both variables are accessing that same view, it gets retained twice (on creation and on copy). I would just use boxView from the start and see if that affects anything.

I'm not sure what the 10, 13, and 10 counts are because of, there might be some quick retains done by default when you click something.

I would suggest leaks to help with your problem, but I guess you can't get that to run. I would suggest building your test project from scratch again, just so you can have that resource.

TahoeWolverine
Somehow my post was duplicated...what's the appropriate course of action here?
TahoeWolverine
A: 

Change

boxview = newview;

to

self.boxview = newview;

If you don't access boxview as a property, it won't retain the object. So what happens is you release newview making its retainCount equal 0 and thus deallocate it so boxview now points to a bad memory location. This will kill your app with an EXC_BAD_ACCESS exception.

Something that helped me with EXC_BAD_ACCESS exceptions is the ability to track an object's malloc history.

So you know, EXC_BAD_ACCESS means you tried to access an instance of an object that no longer exists. In your case, when newview was released and deallocated, the memory was probably overwritten by another object that was dynamically allocated.

Brenden