【问题标题】:When to use properties in objective C?何时在目标 C 中使用属性?
【发布时间】:2012-01-01 22:00:41
【问题描述】:

我在这里有一个问题:Confusing double free error message/memory leak in iPhone app 我认为需要一个新问题来回答它。

我感兴趣的代码是那个问题,但我会在这里重新发布

#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

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

我有很多几乎与此相同的代码,我没有问题。

我的问题是

  1. 什么时候应该使用属性?
  2. 我应该在这里使用属性吗?为什么?

谢谢

【问题讨论】:

标签: iphone objective-c ios properties


【解决方案1】:

从技术上讲,您只需要对可以从其他类访问的值使用属性,但许多人发现对所有指针类型的实例变量使用(保留)属性更容易,这样保留就更自动化了。 (然后在dealloc中使用self.propertyName = xxx;符号进行设置,self.propertyName = nil;释放。)

是的,您可以“手动”进行保留和发布,但这样做会很乏味,而且在进行“快速编辑”时往往会搞砸事情。但是,您必须注意的一件事是将保留(不仅仅是自动保留)值(例如您的 alloc/init 值)分配给 self.xxx 属性。如果您不以某种方式减轻它,这将导致双重保留。

如果你不使用属性,另一件要做的事情是在你release 之后总是nil 一个指针值。这可以防止您意外使用已释放的值,并防止您执行双重 release

(请注意,使用我上面描述的“懒惰”技术绝不是“糟糕的编程”,而不是“完美”地弄清楚所有事情。大约 98% 的编程是调试,你可以做任何事情来防止错误或让他们更容易找到是好事。)

(我还要注意,您在上述代码中的问题似乎主要是您在释放它们后没有niltdest 等指针。您的if 测试应该可能会检查是否指针在使用前已被清零。)

添加:请注意,以上内容适用于 ARC 之前的程序。使用 ARC,“规则”发生了重大变化。

【讨论】:

    【解决方案2】:

    属性可以做很多事情。在最肤浅的层面上,它们允许您以点的形式访问您的成员变量。充其量,它们可以成为出色的内存管理工具(甚至更多)。

    假设你有一个变量:

    NSNumber * myNumber;
    

    稍后在代码中,您可以通过以下方式访问它:

    myNumber = [NSNumber numberWithInt: 5];
    

    问题是您可能会丢失对 myNumber 中先前存储的值的引用。可能的内存泄漏!!此时,您的 myNumber 上没有保留,它可能会在您完成使用之前被释放。

    属性如何提供帮助?假设您围绕它定义了一个属性并使用了 synthesize:

    在接口定义中:

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

    在实现文件中:

    @synthesize myNumber;
    

    这将创建一个 getter 和 setter。意思是...每次您将 myNumber 分配给以下内容时:

    self.myNumber = newNumber;
    

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

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

    在这里,myNumber 会自动获得保留。每次手动操作都非常乏味……正如您所见,使用属性要容易得多。

    不过,这仍然不是一个完美的解决方案!为什么?如果您在实现中使用以下语句会怎样:

    myNumber = newNumber;
    

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

    那么,最好的方法是什么?这是我的建议(无数其他人也是如此):

    在接口类中:

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

    在实现文件中:

    @synthesize myNumber = _myNumber;
    

    现在,您可以通过以下方式访问您的号码:

    self.myNumber = whateverNewNumber;
    

    但是,如果你这样做了:

    myNumber = whateverNewNumber;
    

    您会收到一个错误...因为 myNumber 变量不存在...迫使您每次都使用 self.myNumber

    另外,如果你选择走这条路,别忘了dealloc:

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

    或更简洁:

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

    【讨论】:

    • 尽管这个答案看起来很全面,但它包含大量错误和歧义。例如。 myNumber = [NSNumber numberWithInt: 5]; 不会因为便利构造函数自动保留而泄漏。 setter 方法也错过了保留。
    • @Till,你的意思是自动发布吗?如果不是,请澄清什么是“自动保留”。
    • 谢谢直到。我已经编辑了答案。希望它现在没有错误。
    猜你喜欢
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 2012-03-04
    • 2023-03-03
    • 2016-06-04
    • 1970-01-01
    相关资源
    最近更新 更多