Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use properties in objective C?

I have a question here: Confusing double free error message/memory leak in iPhone app which I think needs a new question to answer it.

The code I am interested in is in that question but I will re post it here

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

@interface ListOfCarShares : UITableViewController <NSXMLParserDelegate>
{
    NSURLConnection *connection;
    NSMutableData *carsharexml;
    NSMutableArray *ldestination;
    NSMutableArray *ldeparts_from;
    NSMutableArray *lcs_id;
    NSMutableArray *ltime;
    NSMutableString *currentElement;

    NSMutableString *tdest;
    NSMutableString *tfrom;
    NSMutableString *ttime;
    NSMutableString *tid;
}

-(void)fetchcarshares;
@property (nonatomic, assign) IBOutlet UITableViewCell *maincell;

@end

#import "ListOfCarShares.h"

@implementation ListOfCarShares
@synthesize maincell;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qualifiedName
    attributes:(NSDictionary *)attributeDict
{
    currentElement = [[elementName copy] autorelease];
    if ([elementName isEqualToString:@"destination"]) 
    {

        //NSLog(@"found current conditions tag it reads %@",currentElement);
        tdest = [[NSMutableString alloc] init];
    }
    if ([elementName isEqualToString:@"departs_from"])
    {
        tfrom = [[NSMutableString alloc] init]; 
    }
    if ([elementName isEqualToString:@"time"])
    {
        ttime = [[NSMutableString alloc] init]; 
    }
    if ([elementName isEqualToString:@"cs_id"])
    {
        tid = [[NSMutableString alloc] init]; 
    }
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if ([currentElement isEqualToString:@"destination"])
    {
        [tdest appendString:string];
    }
    if ([currentElement isEqualToString:@"departs_from"])
    {
        [tfrom appendString:string];
    }
    if ([currentElement isEqualToString:@"time"])
    {
        [ttime appendString:string];
    }
    if ([currentElement isEqualToString:@"cs_id"])
    {
        [tid appendString:string];
    }
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([currentElement isEqualToString:@"destination"])
    {
        [ldestination addObject:tdest];
        [tdest release];
    }
    if ([currentElement isEqualToString:@"departs_from"])
    {
        [ldeparts_from addObject:tfrom];
        [tfrom release];
    }
    if ([currentElement isEqualToString:@"time"])
    {
        [ltime addObject:ttime];
        [ttime release];
    }
    if ([currentElement isEqualToString:@"cs_id"])
    {
        [lcs_id addObject:tid];
        [tid release];
    }
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    ldestination = [[NSMutableArray alloc] init];
    ldeparts_from = [[NSMutableArray alloc] init];
    ltime = [[NSMutableArray alloc] init];
    lcs_id = [[NSMutableArray alloc] init];
    carsharexml = [[NSMutableData alloc] init];

    [self fetchcarshares];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [connection release];

    [ldestination release];
    [ldeparts_from release];
    [ltime release];
    [lcs_id release]; ///
    [carsharexml release];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [ltime count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
    if (cell == nil) 
    {
        [[NSBundle mainBundle] loadNibNamed:@"carsharecell" owner:self options:nil];
    }

    // Configure the cell...
    cell=maincell;

    UILabel *from;
    UILabel *dest;
    UILabel *time;

    from = (UILabel *)[cell viewWithTag:4];
    dest = (UILabel *)[cell viewWithTag:5];
    time = (UILabel *)[cell viewWithTag:6];
    from.text=[ldeparts_from objectAtIndex:indexPath.row];
    dest.text=[ldestination objectAtIndex:indexPath.row];
    time.text=[ltime objectAtIndex:indexPath.row];
    return cell;
}

/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }   
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}
*/

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

-(void)fetchcarshares
{

    MyManager *sharedManager = [MyManager sharedManager];
    NSString *urlString = [NSString stringWithFormat:@"http://url/get.php?username=%@&password=%@",sharedManager.user,sharedManager.passw];

    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];

    connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];

}

-(void) connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
    [carsharexml appendData:data];
}

-(void) connectionDidFinishLoading:(NSURLConnection *)conn
{
    NSString *xmlcheck = [[NSString alloc] initWithData:carsharexml encoding:NSUTF8StringEncoding];

    NSLog(@"%@",xmlcheck);

    [xmlcheck release];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData: carsharexml];
    [parser setDelegate:self];
    [parser parse];
    [parser release];

    [[self tableView] reloadData];
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 102;
}
#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}

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

@end

I only have one property defined in the .h file. The people who answered that question seem to think that the reason I am having double free error is due to the fact I don't have @property for my variables.

I have lots of code practically identical to this and I don't have a problem.

My questions are

  1. When should I use a property?
  2. Should I be using properties here and why?

Thanks

like image 282
RNs_Ghost Avatar asked Nov 19 '11 13:11

RNs_Ghost


2 Answers

You technically only need to use properties for values that are intended to be accessible from other classes, but many find it easier to use (retained) properties for all pointer-type instance variables so that the retaining is a bit more automatic. (And then use self.propertyName = xxx; notation for setting and self.propertyName = nil; for releasing in dealloc.)

Yes, you can do the retains and releases "manually", but it's a hair tedious to do so, and you tend to muck things up when you make "quick edits". The one thing you have to watch out for, though, is assigning a retained (not simply autoretained) value (such as your alloc/init values) to a self.xxx property. This will result in double retain, if you don't mitigate it somehow.

Another thing to do, if you don't use properties, is to always nil a pointer value after you release it. This prevents you from accidentally using the released value and and it prevents you from doing a double release.

(Note that it's in no way "bad programming" to use "lazy" techniques like I described above, vs "perfectly" figuring out everything. About 98% of programming is debugging, and anything you can do to prevent bugs or make them easier to find is goodness.)

(I'll also note that your problem in the above code appears to be mainly that you do not nil thetdest et al pointers after releasing them. And your if tests should likely check to see if the pointer has been nilled before using it.)

Added: Note that the above applies to pre-ARC programs. With ARC the "rules" change substantially.

like image 138
Hot Licks Avatar answered Sep 26 '22 18:09

Hot Licks


Properties do a lot of things. At the most superficial level, they let you access your member variables in dotted form. At best, they can be excellent memory management tools (and more).

Let's say you have a variable:

NSNumber * myNumber;

Later in the code, you access it as:

myNumber = [NSNumber numberWithInt: 5];

The problem is that you might lose reference to the previously stored value in myNumber. Possible Memory Leak!! At this point, you don't have a retain on myNumber and it may get dealloc'd before you're done using it.

How can properties help? Let's say you defined a property around it and used synthesize:

In the interface definition:

NSNumber * myNumber;
...

@property (retain, nonatomic) NSNumber * myNumber;

and

In the implementation file:

@synthesize myNumber;

This will create a getter and setter. Meaning... everytime you assign myNumber to something as in:

self.myNumber = newNumber;

the following setter method (created by synthesize directive) gets invoked:

- (NSNumber *) setMyNumber: (NSNumber *) newNumber {
    [myNumber release];
    myNumber = newNumber;
    [myNumber retain];

    return newNumber;
}

Here, myNumber gets a retain automatically. This is very tedious to do by hand everytime... as you can see, it's much easier to use properties.

This is still not a perfect solution, though! Why? What if you use the following statement in your implementation:

myNumber = newNumber;

Remember, properties' getter and setter get invoked only if you're using the dotted notation (self.myNumber). So here, using properties has done nothing for us, 'cause we forgot to use them! This is very common and likely lapse and understandingly frustrating.

So, what's the best way? This is what I recommend (as do countless others):

In the interface class:

NSNumber * _myNumber;
...
@property (retain, nonatomic) NSNumber * myNumber;

In the implementation file:

@synthesize myNumber = _myNumber;

Now, you can access your-number as:

self.myNumber = whateverNewNumber;

But, if you did:

myNumber = whateverNewNumber;

You'll get an error... because myNumber variable just doesn't exist... forcing you to use self.myNumber everytime!

Also, if you do choose to go this route, don't forget the dealloc:

- (void) dealloc {
    [_myNumber release];
    _myNumber = nil;
}

or more succinct:

- (void) dealloc {
    self.myNumber = nil;
}
like image 26
Sanjay Chaudhry Avatar answered Sep 25 '22 18:09

Sanjay Chaudhry