Когда использовать свойства в цели C? - PullRequest
12 голосов
/ 19 ноября 2011

У меня есть вопрос здесь: Запутывающее двойное бесплатное сообщение об ошибке / утечка памяти в приложении для iPhone , на которое, я думаю, нужен новый вопрос, чтобы ответить на него.

Код, который меня интересует, находится в этом вопросе, но я его опубликую здесь

#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. Должен ли я использовать свойства здесь и почему?

Спасибо

Ответы [ 2 ]

21 голосов
/ 19 ноября 2011

Технически вам нужно использовать свойства только для значений, которые должны быть доступны из других классов, но многие считают, что проще использовать (сохранять) свойства для всех переменных экземпляра типа указателя, так что сохранение происходит немного более автоматически. (А затем используйте self.propertyName = xxx; обозначение для настройки и self.propertyName = nil; для освобождения в dealloc.)

Да, вы можете делать удержания и отпускания "вручную", но это утомительно, и вы склонны все портить, когда делаете "быстрые правки". Единственное, на что вам следует обратить внимание, это присвоить сохраненное (а не просто автоматически сохраняемое) значение (например, ваши alloc/init значения) свойству self.xxx. Это приведет к двойному удержанию, если вы не уменьшите его каким-либо образом.

Другая вещь, которую нужно сделать, если вы не используете свойства, это всегда nil значение указателя после того, как вы release его. Это предотвращает случайное использование выпущенного значения и делает двойное release.

(Обратите внимание, что ни в коем случае не «плохое программирование» - использовать «ленивые» методики, как я описал выше, а не «отлично» - выяснять все. Около 98% программирования - это отладка, и все, что вы можете сделать, чтобы предотвратить ошибки или сделать их легче найти это добро.)

(Я также отмечу, что ваша проблема в приведенном выше коде заключается, главным образом, в том, что вы не используете nil tdest и др. Указатели после их освобождения. И ваши if тесты, вероятно, должны проверить, не перед использованием указатель был обнулен.)

Добавлено: Обратите внимание, что вышесказанное относится к программам, предшествующим ARC. С ARC «правила» существенно меняются.

6 голосов
/ 19 ноября 2011

Свойства делают много вещей. На самом поверхностном уровне они позволяют вам получить доступ к переменным-членам в точечной форме. В лучшем случае они могут быть отличными инструментами управления памятью (и не только).

Допустим, у вас есть переменная:

NSNumber * myNumber;

Позже в коде вы получаете доступ к нему как:

myNumber = [NSNumber numberWithInt: 5];

Проблема в том, что вы можете потерять ссылку на ранее сохраненное значение в myNumber. Возможная утечка памяти !! На данный момент у вас нет сохранения myNumber, и он может быть освобожден до того, как вы его используете.

Как могут помочь свойства? Допустим, вы определили свойство вокруг него и использовали синтез:

В определении интерфейса:

NSNumber * myNumber;
...

@property (retain, nonatomic) NSNumber * myNumber;

и

В файле реализации:

@synthesize myNumber;

Это создаст геттер и сеттер. Значение ... каждый раз, когда вы присваиваете myNumber чему-то, как в:

self.myNumber = newNumber;

вызывается следующий метод установки (созданный директивой synthesize):

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

    return newNumber;
}

Здесь myNumber автоматически получает удержание. Это очень утомительно делать каждый раз вручную ... как вы можете видеть, гораздо проще использовать свойства.

Это все еще не идеальное решение! Зачем? Что если вы используете в своей реализации следующее утверждение:

myNumber = newNumber;

Помните, что свойства getter и setter вызываются только в том случае, если вы используете пунктирную запись (self.myNumber). Итак, использование свойств для нас ничего не дало, потому что мы забыли их использовать! Это очень распространенное явление, которое, скорее всего, упущено и вызывает разочарование.

Итак, как лучше? Это то, что я рекомендую (как и многие другие):

В классе интерфейса:

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

В файле реализации:

@synthesize myNumber = _myNumber;

Теперь вы можете получить доступ к своему номеру как:

self.myNumber = whateverNewNumber;

Но если вы сделали:

myNumber = whateverNewNumber;

Вы получите ошибку ... потому что переменная myNumber просто не существует ... вынуждает вас использовать self.myNumber каждый раз!

Кроме того, если вы решите пойти по этому пути, не забудьте о сделке:

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

или более кратко:

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