UITableViewController:取消分配对象属性

时间:2014-06-07 15:59:32

标签: ios objective-c uitableview

我使用UITableViewController显示来自网络服务的文章列表。检索数据后,将调用此委托方法:

-(void)itemsDownloaded:(NSArray *)items
{
    // Set the items to the array
    _feedItems = items;

    // Reload the table view
    [self.tableView reloadData];
}

我还使用自定义单元格,以便标签的高度发生变化,因此使用以下代码显示整篇文章的标题(遵循本教程Table View Cells With Varying Row Heights) :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = @"BasicCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
    [self configureCell:cell forRowAtIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([cell isKindOfClass:[CustomTableViewCell class]])
    {
        CustomTableViewCell *textCell = (CustomTableViewCell *)cell;

            Article *article_item = _feedItems[indexPath.row];

            NSString *fulltitle = article_item.Title;

//                fulltitle = article_item.Cat_Name; // testing category name

            if (article_item.Subtitle != nil && article_item.Subtitle.length != 0) {
                fulltitle = [fulltitle stringByAppendingString:@": "];
                fulltitle = [fulltitle stringByAppendingString:article_item.Subtitle];
            }
            textCell.lineLabel.text = fulltitle;

        textCell.lineLabel.numberOfLines = 0;
        textCell.lineLabel.font = [UIFont fontWithName:@"Novecento wide" size:12.0f];
    }
}



- (CustomTableViewCell *)prototypeCell
{
    NSString *cellIdentifier = @"BasicCell";

    if (!_prototypeCell)
    {
        _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    }
    return _prototypeCell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];

    self.prototypeCell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));

    [self.prototypeCell layoutIfNeeded];

    CGSize size = [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
    return size.height+1;
}

第一个问题是方法forRowAtIndexPath被调用两次而不是一次。因此,如果_feeditems有10个对象,则该方法被调用20次。第二次调用该方法时,我从获取释放后获得ID对象Cat_Name的两个属性(Articlenull):

*** -[CFString retain]: message sent to deallocated instance 0x9c8eea0
*** -[CFNumber respondsToSelector:]: message sent to deallocated instance 0x9c8e370

尝试显示类别名称时会触发EXC_BAD_ACCESS

我不确定究竟是什么问题,我已经尝试删除代码以改变标签的高度,看看是否通过使用此代码导致了这个问题:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Retrieve cell
    NSString *cellIdentifier = @"BasicCell";
    UITableViewCell *myCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    // Get article
    Article *item = _feedItems[indexPath.row];

    myCell.textLabel.text = item.Title;

    return myCell;
}

唯一的区别是,如果_feeditems有10个对象,则该方法被调用一次,意味着10次。但文章的属性IDCat_Name仍然被取消分配。

在获取数据时,所有对象都是' _feeditems中的属性完好无损,没有解除分配。我想它发生在cellForRowAtIndexPathforRowAtIndexPath

更新

正如@Ilya K所建议的那样,没有从configureCell:forRowAtIndexPath:调用tableView:heightForRowAtIndexPath就停止了两次调用它的问题。我还试过拥有feedItems的财产。到目前为止,这是在@interface(TableViewController.m)的Controller中设置的:

@interface TableViewController () {
    HomeModel *_homeModel;
    NSArray *_feedItems;
    Article *_selectedArticle;
}

我已将其从界面中删除,并将其添加为property(TableViewController.h):

@interface TableViewController : UITableViewController <HomeModelProtocol>
@property (weak, nonatomic) IBOutlet UIBarButtonItem *sidebarButton;
@property (nonatomic, strong) CustomTableViewCell *prototypeCell;
@property(nonatomic) NSString *type;
@property(nonatomic) NSString *data;
@property(copy) NSArray *_feedItems;
@end

尽管如此,它仍然会发出解除分配的消息。

更新2

我已使用Instruments Zombie模板查看了代码(感谢此问题ViewController respondsToSelector: message sent to deallocated instance (CRASH)的答案)。这是我从Instruments获得的错误:

Zombie Messaged

An Objective-C message was sent to a deallocated 'CFString (immutable)' object (zombie) at address: 0x10c64def0

enter image description here

所有Release/Retain Event Types指向以下方法connectionDidFinishLoading,该方法在从Web服务检索JSON数据时使用,并为检索到的每篇文章创建Article个对象:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // Create an array to store the articles
    NSMutableArray *_articles = [[NSMutableArray alloc] init];

    // Parse the JSON that came in
    NSError *error;

    // Highlighted in blue
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:_downloadedData options:kNilOptions error:&error];
    NSArray *fetchedArr = [json objectForKey:@"result"];

    // Loop through Json objects, create question objects and add them to our questions array
    for (int i = 0; i < fetchedArr.count; i++)
    {
        NSDictionary *jsonElement = fetchedArr[i];

        // Create a new location object and set its props to JsonElement properties
        Article *newArticle = [[Article alloc] init];
        newArticle.ID = jsonElement[@"ID"];
        newArticle.Title = jsonElement[@"Title"];
        newArticle.Subtitle = jsonElement[@"Subtitle"];
        newArticle.Content = jsonElement[@"Content"];
        newArticle.ImageUrl = jsonElement[@"ImageUrl"];
        newArticle.Author = jsonElement[@"Author"];
        newArticle.PostId = jsonElement[@"PostId"];
        newArticle.ArticleOrder = jsonElement[@"ArticleOrder"];
        newArticle.Cat_Id = jsonElement[@"CategoryId"];
        // Highlighted in yellow
        newArticle.Cat_Name = jsonElement[@"CategoryName"];

        // Add this article object to the articles array
        // Highlighted in yellow
        [_articles addObject:newArticle];
    }

    // Ready to notify delegate that data is ready and pass back items
    if (self.delegate)
    {
        [self.delegate itemsDownloaded:_articles];
    }
}

我仍然无法弄清楚出了什么问题。

更新3

connectionDidFinishLoading进行更多测试我已删除了要释放的两个属性,并且未显示已释放的消息。我不知道导致这两个属性(IDCat_Name)被解除分配的原因,此时无法从任何地方访问这些属性。

2 个答案:

答案 0 :(得分:1)

您不需要从tableView:heightForRowAtIndexPath调用configureCell:forRowAtIndexPath:您应该使用带有sizeWithAttributes的Article对象来确定单元格高度:

您的prototypeCell函数只是创建了类型为CustomTableViewCell的无关空单元格,并且没有必要尝试重新调整它的大小。

tableView:cellForRowAtIndexPath:每当你的tableview需要重绘时调用,例如滚动时。这意味着你的_feeditems数组应该被分配并保持一致,以便在实例生命周期的任何时候使用UITableView。

还要确保为_feeditems声明属性并使用此属性分配数据。

实施例: @property(强)NSArray * feeditems;或@property(复制)NSArray * feeditems;

in itemsDownloaded: self.feeditems = items;

答案 1 :(得分:0)

最后解决了deallocated messages问题。将InstrumentsZombie模板一起使用时(使用InstrumentsZombie模板:ViewController respondsToSelector: message sent to deallocated instance (CRASH)),我发现这一行:

NSDictionary *json = [NSJSONSerialization JSONObjectWithData:_downloadedData options:kNilOptions error:&error];
connectionDidFinishLoading方法中的

导致了这个问题。我搜索了NSJSONSerialization causing deallocation messages,我从这个问题Loading properties from JSON, getting "message sent to deallocated instance"得到了答案。 Article类的一些属性设置为assign而不是strong

@interface Article : NSObject

@property (nonatomic, assign) NSNumber *ID; // changed assign to strong
@property (nonatomic, strong) NSString *Title;
@property (nonatomic, strong) NSString *Subtitle;
@property (nonatomic, strong) NSString *Content;
@property (nonatomic, strong) NSString *ImageUrl;
@property (nonatomic, assign) NSNumber *PostId; // changed assign to strong
@property (nonatomic, strong) NSString *Author;
@property (nonatomic, assign) NSNumber *ArticleOrder; // changed assign to strong
@property (nonatomic, assign) NSNumber *Cat_Id; // changed assign to strong
@property (nonatomic, assign) NSString *Cat_Name; // changed assign to strong

@end

将属性更改为strong后,所有deallocated messages都已停止。

我知道这个错误似乎对每个项目非常具体,其原因可能会有所不同,但如果有人有类似的东西,这就是我解决它的方法。