views:

77

answers:

2

If I have a custom class called Tires:

#import <Foundation/Foundation.h>

@interface Tires : NSObject {
@private
     NSString *brand;
     int size;
}

@property (nonatomic,copy) NSString *brand;
@property int size;

- (id)init;
- (void)dealloc;

@end
=============================================

#import "Tires.h"

@implementation Tires

@synthesize brand, size;

- (id)init {
     if (self = [super init]) {
          [self setBrand:[[NSString alloc] initWithString:@""]];
          [self setSize:0];
     }
     return self;
}

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

@end

And I synthesize a setter and getter in my View Controller:

#import <UIKit/UIKit.h>
#import "Tires.h"

@interface testViewController : UIViewController {
     Tires *frontLeft, *frontRight, *backleft, *backRight;
}

@property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;

@end

====================================

#import "testViewController.h"

@implementation testViewController

@synthesize frontLeft, frontRight, backleft, backRight;

- (void)viewDidLoad {
     [super viewDidLoad];
     [self setFrontLeft:[[Tires alloc] init]];
}
- (void)dealloc {
    [super dealloc];
}

@end

It dies after [self setFrontLeft:[[Tires alloc] init]] comes back. It compiles just fine and when I run the debugger it actually gets all the way through the init method on Tires, but once it comes back it just dies and the view never appears. However if I change the viewDidLoad method to:

- (void)viewDidLoad {
     [super viewDidLoad];
     frontLeft = [[Tires alloc] init];
}

It works just fine. I could just ditch the setter and access the frontLeft variable directly, but I was under the impression I should use setters and getters as much as possible and logically it seems like the setFrontLeft method should work.

This brings up an additional question that my coworkers keep asking in these regards (we are all new to Objective-C); why use a setter and getter at all if you are in the same class as those setters and getters.

+1  A: 

One problem that i see is here:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setFrontLeft:[[Tires alloc] init]];
}

When you call [Tires alloc] you get back an object with a retain count of 1. You then use a set method which you have synthesized, which bumps the retain count to 2. When your object is done with the Tire object, it will reduce the retain count back to 1, but the tire will never get deallocated. I think you should use:

[self setFrontLeft:[[[Tires alloc] init] autorelease]];
darren
Tried **[self setFrontLeft:[[[Tires alloc] init] autorelease]];** and it still does the same thing. In the debugger it says **TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION** and if I run the app it just crashes as soon as it is launched.
Rob
+7  A: 

You have declared frontLeft as a 'copy' property:

@property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;

When you assign to this property, a copy is made by invoking the object's copy method. This only works for objects which support the NSCopying protocol (i.e., which implement a copyWithZone: method). Since your Tires class does not implement this method, you get an exception.

You probably want to change this to be a 'retain' property:

@property (nonatomic,retain) Tires *frontLeft, *frontRight, *backleft, *backRight;

See the Objective C documentation on declared properties for more on property declarations.

Damien Neil
That worked perfectly! I forgot that setters invoke the **copy** method. Marking this as the answer.Is it common practice to use setters and getters for instance variables even if you are in the same class as them? This discussion keeps coming up between me and my co-developers.
Rob
I think whether you use getters/setters is mainly a matter of preference. In this case, I'd use the setter, since it will automatically retain the variable for you. I wouldn't use the getter from within the class.
Damien Neil
Using setters gives you memory management for free (assuming you've setup your properties properly), which is a big win in most situations. I usually use getters if I'm using setters (within a class) to make the code more consistent (and hence more readable).
Nick Forge
Often times in books and examples I see them using **alloc** on a separate line then releasing it after. So instead of **[self setFrontLeft:[[Tires alloc] init]]** they would do **Tires *myTires = [[Tires alloc] init]** first, then do **[self setFrontLeft:myTires]** and then release myTires after that. Is the way I am trying to do it causing a memory leak?
Rob