How to purge a cached UITableViewCell

2019-02-13 17:29发布

Does anyone have suggestions on how to purge a cached UITableViewCell?

I'd like to cache these cells with reuseIdentifier. However, there are times when I need to delete or modify some of the table rows. I expect to call reloadData after the row changes.

Right now, dequeueReusableCellWithIdentifier always returns the cached(obsolete) entry from before. How do I indicate that the cache is stale and needs to be purged?

8条回答
做自己的国王
2楼-- · 2019-02-13 17:37

I have found a really good method.

In .h

//add this
int reloadCells;

In .m

- (void)dumpCache {
    reloadCells = 0;
    [tableView reloadData];
}

-(void)tableView:(UITableView *)tableView cellForRowAtUndexPath:(NSIndexPath *)indexPath {
    static NSString *CellID = @"Cell"
    UITableViewCell *cell = [tableView dequeCellWithReuseIdentifier:CellID];
    if (cell == nil || reloadCells < 12) {
        cell = [[UITableViewCell alloc] initWithFormat:UITableViewCellStyleDefault reuseIdentifier:CellID];
        reloadCells ++;
    }
    cell.textLabel.text = @"My Cell";

return cell;
}
查看更多
Juvenile、少年°
3楼-- · 2019-02-13 17:40

I'm not sure why you're trying to purge cells in the first place. Every time you dequeue a cell, you need to re-set any data that needs to be displayed. The caching just prevents you from having to set up any non-changing properties every time. But the actual data that's being displayed must be set, even if the cell was cached before.

Note that your reuse identifier is supposed to be the same for all the cells of the same type. If you're doing something silly like calculating the identifier based on the row in question, then you're doing it wrong.

Your code should look something like

- (UITableViewCell *)tableView:(UITableView *)view cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *identifier = @"CellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        // no cached cell, create a new one here
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
        // set any properties that all such cells should share, such as accessory, or text color
    }
    // set data for this particular cell
    cell.textLabel.text = @"foo";
    return cell;
}

In that example, note how I always set the data for the cell every single time, and all cells share the same identifier. If you follow this pattern, you should have no reason at all to try and "purge" your cells, as any old data will be overwritten anyway. If you have multiple types of cells, you may want to use multiple identifiers in that case, but it's still 1 identifier per cell type.

查看更多
时光不老,我们不散
4楼-- · 2019-02-13 17:43

No way, I think... I used a completely different approach. Instead of relying on UITableView cache, I build my own. In this way I have perfect control on when and what to purge. Something like this...

given:

NSMutableDictionary *_reusableCells;
_reusableCells = [NSMutableDictionary dictionary];

I can do:

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

    UITableViewCell *cell = [_reusableCells objectForKey:cellIdentifier];

    if (cell == nil)
    {
        // create a new cell WITHOUT reuse-identifier !!
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
        // store it in my own cache
        [_reusableCells setObject:cell forKey:cellIdentifier];
        /* ...configure the cell... */
    }

    return cell;
}

and:

-(void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    [_reusableCells removeAllObjects];
}

Hope this may help.

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-02-13 17:45

I had to something similar today. I have a gallery of images, and when the user deletes them all, the gallery needs to get rid of the last queued cell and replace it with a "gallery empty" image. When the delete routine completes, it sets a 'purge' flag to YES, then does a [tableView reloadData]. The code in cellForRowAtIndexPath looks like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    NSString *cellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
    if (purge) {
        cell = nil;
        purge = NO;
    }
    if (cell == nil) {
        //*** Do usual cell stuff
    }
    return cell
}
查看更多
闹够了就滚
6楼-- · 2019-02-13 17:47

I am coding ObjC (manual reference counting) and found some strange behavior that prevents UITableView and UITableViewCells from getting released, although my memory management is correct. UITableView and UITableViewCell seem to retain each other. And yes, I found a way to force the UITableView and its cells to release each other (remove cached cells).

To do this, basically you ask a table view for a reusable cell. The table view will remove it from its reusable cells cache. After all you tell that cell to remove from its superview. Done.

Now as a stand-alone class... First, you need an array of all cell identifiers you used. Next, you implement a dummy data source and let that data source clear the UITableView by reloading itself with the "ClearAllCachedCells" Data source:

#import <UIKit/UIKit.h>
@interface ClearAllCachedUITableViewCellsDataSource : NSObject <UITableViewDataSource, UITableViewDelegate>

+(void)clearTableView:(UITableView*)tv
     reuseIdentifiers:(NSArray<NSString*>*)cellIdentifiers
  delegateAfterFinish:(id<UITableViewDelegate>)dg
dataSourceAfterFinish:(id<UITableViewDataSource>)ds;

@end

And the magic happens in the .m file:

#import "ClearAllCachedUITableViewCellsDataSource.h"


@interface ClearAllCachedUITableViewCellsDataSource () {
    BOOL clearing;
}
@property (nonatomic, readonly) UITableView *tv;
@property (nonatomic, readonly) NSArray<NSString*> *identifiers;
@property (nonatomic, readonly) id ds;
@property (nonatomic, readonly) id dg;
@end

@implementation ClearAllCachedUITableViewCellsDataSource

-(void)dealloc {
    NSLog(@"ClearAllCachedUITableViewCellsDataSource (%i) finished", (int)_tv);
    [_tv release];
    [_ds release];
    [_dg release];
    [_identifiers release];
    [super dealloc];
}

-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
    [self performSelectorOnMainThread:@selector(clear) withObject:nil waitUntilDone:NO];
    NSLog(@"TV (%i): reloading with zero cells", (int)_tv);
    return 0;
}

-(void)clear {
    if (!clearing) {
        clearing = YES;
        for (NSString *ident in self.identifiers) {
            UITableViewCell *cell = [_tv dequeueReusableCellWithIdentifier:ident];
            while (cell) {
                NSLog(@"TV (%i): removing cached cell %@/%i", (int)_tv, ident, (int)cell);
                [cell removeFromSuperview];
                cell = [_tv dequeueReusableCellWithIdentifier:ident];
            }
        }
        self.tv.delegate = self.tv.delegate == self ? self.dg : self.tv.delegate;
        self.tv.dataSource = self.tv.dataSource == self ? self.ds : self.tv.dataSource;
        [self release];
    }
}

+(void)clearTableView:(UITableView*)tv
     reuseIdentifiers:(NSArray<NSString*>*)cellIdentifiers
  delegateAfterFinish:(id<UITableViewDelegate>)dg
dataSourceAfterFinish:(id<UITableViewDataSource>)ds {
    if (tv && cellIdentifiers) {
        NSLog(@"TV (%i): adding request to clear table view", (int)tv);
        ClearAllCachedUITableViewCellsDataSource *cds = [ClearAllCachedUITableViewCellsDataSource new];
        cds->_identifiers = [cellIdentifiers retain];
        cds->_dg = [dg retain];
        cds->_ds = [ds retain];
        cds->_tv = [tv retain];
        cds->clearing = NO;
        tv.dataSource = cds;
        tv.delegate = cds;
        [tv performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
    }
}

@end
查看更多
放我归山
7楼-- · 2019-02-13 17:54
while ([tableView dequeueReusableCellWithIdentifier:@"reuseid"]) {}
查看更多
登录 后发表回答