Подтвердить что ты не робот

Когда использовать свойства в объективе C?

У меня есть вопрос: Сбивать с толку двойное бесплатное сообщение об ошибке/утечку памяти в приложении 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 для моих переменных.

У меня много кода, практически идентичного этому, и у меня нет проблем.

Мои вопросы

  • Когда следует использовать свойство?
  • Должен ли я использовать свойства здесь и почему?

Спасибо

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

Скажем, у вас есть переменная:

NSNumber * myNumber;

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

myNumber = [NSNumber numberWithInt: 5];

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

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

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

NSNumber * myNumber;
...

@property (retain, nonatomic) NSNumber * myNumber;

и

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

@synthesize myNumber;

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

self.myNumber = newNumber;

вызывается следующий метод setter (созданный директивой 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 каждый раз!

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

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

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

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