Проблемы с использованием NSIndexPath как ключа в NSMutableDictionary?

Есть ли какая-либо особая причина, по которой попытка сохранить и получить значение в NSMutableDictionary с помощью NSIndexPath, поскольку key может завершиться с ошибкой?

Я изначально попытался сделать это, чтобы сохранить NSMutableDictionary высот UITableViewCell (self.cellHeights) для UITableView. Каждый раз, когда вы нажимаете UITableViewCell, эта ячейка будет либо расширяться, либо сокращаться между двумя разными высотами на основе значения, хранящегося в NSMutableDictionary для этого конкретного indexPath:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    NSNumber *heightNSNumber = [self.cellHeights objectForKey:indexPath];
    if (!heightNSNumber)
    {
        heightNSNumber = [NSNumber numberWithFloat:100.0];
        [self.cellHeights setObject:heightNSNumber forKey:indexPath];
    }
    return [heightNSNumber floatValue];
}

- (void)tableView:(UITableView *)tableView  
        didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSNumber *heightNSNumber = [self.cellHeights objectForKey:indexPath];
    if (!heightNSNumber)
    {
        heightNSNumber = [NSNumber numberWithFloat:100.0];
        [self.cellHeights setObject:heightNSNumber forKey:indexPath];
    }

    if ([heightNSNumber floatValue] == 100.0)
    {
        [self.cellHeights setObject:[NSNumber numberWithFloat:50.0] 
                             forKey:indexPath];
    } else {
        [self.cellHeights setObject:[NSNumber numberWithFloat:100.0] 
                             forKey:indexPath];
    }
    [tableView beginUpdates];
    [tableView endUpdates];
}

По неизвестным мне причинам, высота ячейки внутри tableView:didSelectRowAtIndexPath: через [self.cellHeights objectForKey:indexPath] работает просто отлично. Тем не менее, попытка получить высоту ячейки внутри tableView:heightForRowAtIndexPath: через [self.cellHeights objectForKey:indexPath] всегда возвращает nil, потому что кажется, что indexPath, используемый для хранения высоты, не соответствует индексу, который используется для извлечения высоты ячейки, даже если они имеют одинаковые значения для indexPath.section и indexPath.row. Из-за этого к self.cellHeights добавляется новый объект для "одинакового" пути указателя (как видно, так как self.cellHeights.count увеличивается после этого).

Это не происходит, когда вы храните высоты ячеек в NSMutableDictionary, используя строку ([NSNumber numberWithInteger:indexPath.row]) в качестве ключа... так, что я делаю это сейчас, но я хотел бы понять, почему indexPath не работает как ключ.

Ответ 1

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

Просто воссоздайте indexPath, добавив следующую строку:

indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];

Вуаля. tableView:heightForRowAtIndexPath: использует внутренние экземпляры NSMutableIndexPath (как вы бы видели с точкой останова). Как-то эти экземпляры кажутся несогласованными с NSIndexPath при вычислении хеш-ключей.

Преобразуя его обратно в NSIndexPath, тогда все работает.

Ответ 2

@Ответ на вопрос представляется приемлемым, но этот вопрос был более подробно рассмотрен здесь. Короче говоря, UITableView иногда использует экземпляры NSMutableIndexPath вместо NSIndexPath, а экземпляры этих двух классов никогда не равны, потому что [NSMutableIndexPath class] != [NSIndexPath class]. Обходной путь состоит в том, чтобы всегда генерировать ключ NSIndexPath для всего, что полагается на isEqual или hash, например, поиск ключей словаря:

- (NSIndexPath *)keyForIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath class] == [NSIndexPath class]) {
        return indexPath;
    }
    return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
}

Ответ 3

Существует несколько вещей, которые должны быть реализованы для надежной работы объекта в качестве ключа для NSDictionary, а именно протокола isEqual:, hash и Copyable.

Я не очень уверен, что NSIndexPath когда-либо намерен работать как ключ к словарям (потому что он был индексом для массивов).

Я предполагаю, что hash не выполняется правильно для разных экземпляров класса. Также обратите внимание, что некоторые из методов делегата таблицы вызываются с помощью NSIndexPath, а некоторые - с NSMutableIndexPath. Возможно, это имеет значение.