简体   繁体   中英

Using accessor methods to set iVars?

Initially, I was looking at the way "pickerData" is set and thinking I wonder why you can't just assign it directly (as in METHOD_002), but then I stated thinking that I should really be using the accessor methods I defined and not setting the instance variables directly. Am I understand this correctly that METHOD_001 is a better way of doing this?

@property(nonatomic, retain) IBOutlet NSArray *pickerData;

METHOD_001

-(void)viewDidLoad {
    NSLog(@"VIEW: Single ... Loaded");
    NSArray *dataArray = [[NSArray alloc] initWithObjects:@"A", @"B", @"C",nil];
    [self setPickerData:dataArray];
    [dataArray release];
    [super viewDidLoad];
}

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

OR METHOD_002

-(void)viewDidLoad {
    NSLog(@"VIEW: Single ... Loaded");
    if(pickerData != nil) [pickerData release];
    pickerData = [[[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil] retain];
    [super viewDidLoad];
}

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

EDIT_001:

First off I have added the "nil" values to terminate the NSArrays, coming from CI always forget this, my bad. Also you're right, I did not account for the fact in METHOD_002 that pickerData might already be set and as a result leak the old object. Once you start noticing these problems and fixing the code it starts to look like METHOD_001 is the best idea. Or to just use the property directly as Vladimir and eJames noted.

self.pickerData = [NSArray arrayWithObjects: @"A", @"B", @"C", nil];

EDIT_002:

Thank you for all the pointers and comments, for now I am going to stick with METHOD_001, I could just as easily use NSArrayWithObjects: but I am trying to keep memory usage low by releasing things myself as soon as I can (not that it matters here, but for future projects) Also I do like the feel of self.pickerData, but I am still unsure how I feel about dot-notation and have for now been trying to stick with old style objects and messages where possible. Again many thanks for the help.

gary

There are a lot of topics to cover here, but I'll start with the recommended approach:

METHOD_003

-(void)viewDidLoad { 
    NSLog(@"VIEW: Single ... Loaded"); 
    self.pickerData = [NSArray arrayWithObjects:@"A", @"B", @"C", nil]; 
    [super viewDidLoad];
}

Note: thanks to Vladimir for the reminder to nil-terminate .

Now, for the details:

METHOD_001 does the exact same thing as METHOD_003, but it takes a few more lines of code.

METHOD_002 has a memory leak because you alloc the array, and then also retain it. This results a total retain count of 2. When you release the array in your dealloc method, the count will be reduced to 1, so the array will not be released from memory.

Even if you remove the extra retain from METHOD_002, it will not do two very important things:

  1. It will not send the proper KVC/KVO notifications . Cocoa does a lot of very convenient things behind-the-scenes when you use a property accessor, so unless you have a good reason not to, using the accessors is highly recommended.

  2. It will not automatically release any old data that was stored in pickerData . This is not a concern in viewDidLoad , but if you were in a different method, it would make a difference, so it is best to just get in the habit of using the accessors unless you have a specific reason not to.

You should always use accessors of properties (which in Objective-C 2.0 means using the self.property notation.)

Why? Because it provides automatic access control and object life-cycle management. The generated accessors can provide a lot protection, such as read/write, copy, retain etc that take a lot of manual code otherwise. If you write your own accessors, you can add in all the validation and side effects you want.

(Back before Objective-C 2.0 writing accessors was considered a high art. It still can be if you fully exploit the potential.)

The only time you should access properties directly is when you are writing an accessor. For example take this common pattern:

@property(nonatomic, retain)  NSMutableArray *myObjects;
@synthesize myObjects;

-(NSMutableArray *) myObjects{
    if (myObect!=nil) {
        return myObect;
    }
    NSMutableArray *anArray=[[NSMutableArray alloc] initWithCapacity:1];
    self.myObject=anArray;
    [anArray release]
    return myObject;
}
  1. This accessor ensures that myObjects is never nil which removes a lot of boilerplate nil testing in the rest of your code.
  2. You obviously can't call self.myObjects (which is really [self myObjects] )inside the accessor without creating an infinite recursion so you must access the raw variable here but...
  3. ...You can call (the autogenerated) self.myObjects= (which is really [self setMyObjects:anArray] ) because it's an entirely different method. If you look at the internals of setMyObjects: you would see that it access the raw variable as well.
  4. If you use the generated accessors, self.myObjects= handles retaining, copying, nilling etc for you, every time you call it. The only time you have to call release is in the dealloc. This alone wipes out probably half the errors people make in Objective-C.

Conversely, outside of an accessor method, you gain absolutely nothing by directly accessing the properties inside the classes own methods. All it does is save some key strokes while exposing you to the risk of hard to find bugs.

As the previous answers demonstrated, you made several memory errors by trying to manage the property directly. Had you used the accessor every time, you would not have made them. For example:

pickerData = [[[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil] retain];

... has to be managed exactly right every time whereas ...

self.pickerData = [[NSArray alloc] initWithObjects:@"A", @"B", @"C", nil];

... is automatically correct.

Remember that the ultimate design goal for any Objective-C class is that is should be perfectly modular and reusable. That means it should manage all its own memory, its own data validation and its own side effects. Accessors are absolutely vital to that management. By wrapping logic around every access of a variable, you ensure that (1) it is the type, range, etc you expect and (2) that it is always around when you need it be (3) that you can control all the side effects of writing or reading the variable and (4) that it doesn't leak.

I cannot extol the virtues of accessors enough. In fact, I might write a little song. ;-)

The biggest problems with your METHOD_002 are: a) you're retaining an array you already own, which you don't need to do (and will leak the array), and b) you're not accounting for the possibility that pickerData may already have a non- nil value ( viewDidLoad may be called many times over the life of a controller).

As a matter of style, the first method is probably better. It would have avoided (b), and generally handles a lot of niggling details so you don't have to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM