何时使用目标C中的属性?

时间:2021-12-12 12:47:01

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.

我在这里有一个问题:在iPhone应用程序中混淆双重免费错误消息/内存泄漏,我认为需要一个新问题来回答它。

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.

我只在.h文件中定义了一个属性。回答这个问题的人似乎认为我有双重免费错误的原因是因为我的变量没有@property。

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. 我什么时候应该使用房产?
  3. Should I be using properties here and why?
  4. 我应该在这里使用房产吗?为什么?

Thanks

谢谢

2 个解决方案

#1


21  

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.)

从技术上讲,您只需要为可以从其他类访问的值使用属性,但许多人发现所有指针类型实例变量的使用(保留)属性更容易,因此保留更自动一些。 (然后使用self.propertyName = xxx;用于设置的表示法和self.propertyName = nil;用于在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.

是的,你可以“手动”进行保留和释放,但这样做很麻烦,而且当你进行“快速编辑”时,你往往会搞砸。但是,您需要注意的一件事是将保留(不是简单的自动恢复)值(例如您的alloc / init值)分配给self.xxx属性。如果你不以某种方式缓解它,这将导致双重保留。

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.

另一件事,如果不使用属性,则在释放指针值后始终为nil指定值。这可以防止您意外使用释放的值,并且它可以防止您进行双重释放。

(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.)

(请注意,使用像我上面描述的“懒惰”技术绝不是“糟糕的编程”,而是“完美地”弄清楚所有内容。大约98%的编程是调试,以及你可以采取的任何措施来防止错误或使它们更容易找到善良。)

(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.)

(我还会注意到你在上面的代码中出现的问题似乎主要是你在释放它们之后没有找到最好的指针。而且你的if测试应该在使用之前检查指针是否已经被填满了。)

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

补充:请注意,上述内容适用于ARC之前的程序。随着ARC,“规则”发生了重大变化。

#2


6  

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.

问题是您可能会丢失对myNumber中先前存储的值的引用。可能的内存泄漏!!此时,您没有在myNumber上保留,并且在您使用它之前可能会被取消删除。

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

物业如何帮助?假设你在它周围定义了一个属性并使用了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:

这将创建一个getter和setter。含义...每次将myNumber分配给某个东西时,如:

self.myNumber = newNumber;

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

调用以下setter方法(由synthesize指令创建):

- (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.

在这里,myNumber会自动获得保留。每次手工操作都非常繁琐......正如您所看到的,使用属性要容易得多。

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.

请记住,只有在使用点分表示法(self.myNumber)时才会调用属性的getter和setter。所以在这里,使用属性对我们没有任何作用,因为我们忘了使用它们!这是非常常见的,可能会失效并且理解为令人沮丧。

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!

你会得到一个错误...因为myNumber变量不存在...强迫你每次都使用self.myNumber!

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

此外,如果您确实选择了这条路线,请不要忘记dealloc:

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

or more succinct:

或更简洁:

- (void) dealloc {
    self.myNumber = nil;
}

#1


21  

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.)

从技术上讲,您只需要为可以从其他类访问的值使用属性,但许多人发现所有指针类型实例变量的使用(保留)属性更容易,因此保留更自动一些。 (然后使用self.propertyName = xxx;用于设置的表示法和self.propertyName = nil;用于在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.

是的,你可以“手动”进行保留和释放,但这样做很麻烦,而且当你进行“快速编辑”时,你往往会搞砸。但是,您需要注意的一件事是将保留(不是简单的自动恢复)值(例如您的alloc / init值)分配给self.xxx属性。如果你不以某种方式缓解它,这将导致双重保留。

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.

另一件事,如果不使用属性,则在释放指针值后始终为nil指定值。这可以防止您意外使用释放的值,并且它可以防止您进行双重释放。

(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.)

(请注意,使用像我上面描述的“懒惰”技术绝不是“糟糕的编程”,而是“完美地”弄清楚所有内容。大约98%的编程是调试,以及你可以采取的任何措施来防止错误或使它们更容易找到善良。)

(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.)

(我还会注意到你在上面的代码中出现的问题似乎主要是你在释放它们之后没有找到最好的指针。而且你的if测试应该在使用之前检查指针是否已经被填满了。)

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

补充:请注意,上述内容适用于ARC之前的程序。随着ARC,“规则”发生了重大变化。

#2


6  

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.

问题是您可能会丢失对myNumber中先前存储的值的引用。可能的内存泄漏!!此时,您没有在myNumber上保留,并且在您使用它之前可能会被取消删除。

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

物业如何帮助?假设你在它周围定义了一个属性并使用了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:

这将创建一个getter和setter。含义...每次将myNumber分配给某个东西时,如:

self.myNumber = newNumber;

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

调用以下setter方法(由synthesize指令创建):

- (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.

在这里,myNumber会自动获得保留。每次手工操作都非常繁琐......正如您所看到的,使用属性要容易得多。

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.

请记住,只有在使用点分表示法(self.myNumber)时才会调用属性的getter和setter。所以在这里,使用属性对我们没有任何作用,因为我们忘了使用它们!这是非常常见的,可能会失效并且理解为令人沮丧。

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!

你会得到一个错误...因为myNumber变量不存在...强迫你每次都使用self.myNumber!

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

此外,如果您确实选择了这条路线,请不要忘记dealloc:

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

or more succinct:

或更简洁:

- (void) dealloc {
    self.myNumber = nil;
}