Как очистить кэшированный UITableViewCell - PullRequest
7 голосов
/ 18 февраля 2010

У кого-нибудь есть предложения по очистке кэшированного UITableViewCell?

Я бы хотел кэшировать эти ячейки с помощью reuseIdentifier.Однако бывают случаи, когда мне нужно удалить или изменить некоторые строки таблицы.Я ожидаю вызова reloadData после изменения строки.

Прямо сейчас, dequeueReusableCellWithIdentifier всегда возвращает кешированную (устаревшую) запись из ранее.Как я могу указать, что кеш устарел и должен быть очищен?

Ответы [ 8 ]

7 голосов
/ 05 января 2011

Я не уверен, почему вы пытаетесь очистить клетки в первую очередь. Каждый раз, когда вы удаляете ячейку из очереди, вам необходимо переустанавливать любые данные, которые должны отображаться. Кэширование просто не позволяет вам каждый раз устанавливать неизменяемые свойства. Но фактические данные, которые отображаются, должны быть установлены, даже если ячейка была кэширована ранее.

Обратите внимание, что ваш идентификатор повторного использования должен быть одинаковым для всех ячеек одного типа. Если вы делаете что-то глупое, например вычисление идентификатора на основе рассматриваемой строки, то вы делаете это неправильно.

Ваш код должен выглядеть примерно так:

- (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;
}

В этом примере обратите внимание, как я всегда устанавливаю данные для ячейки каждый раз, и все ячейки имеют один и тот же идентификатор. Если вы следуете этому шаблону, у вас не должно быть никаких причин пытаться «очистить» ваши ячейки, так как все старые данные будут в любом случае перезаписаны. Если у вас есть несколько типов ячеек, вы можете использовать несколько идентификаторов в этом случае, но это по-прежнему 1 идентификатор на тип ячейки.

3 голосов
/ 12 сентября 2013
while ([tableView dequeueReusableCellWithIdentifier:@"reuseid"]) {}
3 голосов
/ 05 апреля 2013

Я не знаю, как очистить кэш, однако я использую обходной путь, как справиться с ситуацией, когда необходимо изменить строки.

Прежде всего, я не использую статический идентификатор ячейки. Я прошу объект данных сгенерировать его (обратите внимание на [myObject signature], он предоставляет уникальную строку, описывающую все необходимые свойства):

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyObject *myObject = _dataSource[indexPath.row];
    NSString *cellId = [myObject signature];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
        [myObject configureCell:cell];
    }
    [myObject updateCellWithData:cell];
    return cell;
}

[myObject signature] предоставляет разные подписи для другого списка свойств. Итак, если мой объект изменился, я просто вызываю [self.tableView reloadData], myObject предоставит новую подпись, и таблица загрузит для нее новую ячейку.

[myObject configureCell:cell] помещает все необходимые подпредставления в ячейку.

[myObject updateCellWithData:cell] обновляет подпредставления ячейки текущими данными.

2 голосов
/ 08 августа 2012

Я нашел действительно хороший метод.

В .ч

//add this
int reloadCells;

дюйм. М

- (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;
}
1 голос
/ 17 марта 2017

Я кодирую ObjC (ручной подсчет ссылок) и обнаружил какое-то странное поведение, препятствующее освобождению UITableView и UITableViewCells, хотя мое управление памятью правильное.UITableView и UITableViewCell, кажется, сохраняют друг друга.И да, я нашел способ заставить UITableView и его ячейки освобождать друг друга (удалять кэшированные ячейки).

Для этого в основном вы запрашиваете табличное представление для многоразовой ячейки.Представление таблицы удалит его из кэша многократно используемых ячеек.Ведь вы говорите, что ячейку уберите из своего суперпредставления.Готово.

Теперь как самостоятельный класс ... Во-первых, вам нужен массив всех идентификаторов ячеек, которые вы использовали.Затем вы реализуете фиктивный источник данных и позволяете этому источнику данных очистить UITableView, перезагрузив себя с источником данных «ClearAllCachedCells»:

#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

И волшебство происходит в файле .m:

#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
1 голос
/ 26 октября 2012

Мне пришлось что-то подобное сегодня. У меня есть галерея изображений, и когда пользователь удаляет их все, галерее необходимо избавиться от последней ячейки, поставленной в очередь, и заменить ее изображением «галерея пуста». Когда процедура удаления завершается, она устанавливает флаг 'purge' в YES, затем выполняет [tableView reloadData]. Код в cellForRowAtIndexPath выглядит следующим образом:

- (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
}
0 голосов
/ 15 февраля 2013

Нет, я думаю ... Я использовал совершенно другой подход. Вместо того чтобы полагаться на кеш UITableView, я создаю свой собственный. Таким образом, я отлично контролирую, когда и что нужно чистить. Как то так ...

Дано:

NSMutableDictionary *_reusableCells;
_reusableCells = [NSMutableDictionary dictionary];

Я могу сделать:

- (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;
}

и

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

Надеюсь, это поможет.

0 голосов
/ 18 февраля 2010

Старые данные из предыдущего использования ячейки должны быть очищены с сообщением prepareForReuse. Когда ячейка снята с очереди, это сообщение отправляется в ячейку, прежде чем оно возвращается с dequeueReusableCellWithIdentifier:.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...