Копирование многомерного NSMutableArray

В настоящее время я работаю над приложением Судоку, числа хранятся в многомерном NSMutableArray из NSNumbers. Я сохраняю массив в своем SudokuGridView для отображения чисел в сетке. Когда приходит время решить головоломку, я передаю [grid numberGrid] подклассу созданной мной NSOperation, который решает головоломку.

Массив сетки определяется как свойство как таковое:

@property (readonly) NSMutableArray *numberArray; 

При передаче его в решатель судоку я иду:

MESudokuSolver *solvePuzzleOperation  = [[MESudokuSolver alloc] initWithPuzzle: [grid numberArray]];

initWithPuzzle определяется так:

- (id)initWithPuzzle:(NSMutableArray *)puzzleArray  {
    if(self = [super init]) {
        puzzle = [[NSMutableArray alloc] initWithArray: puzzleArray];
    }
    return self;    
}

Когда я затем преобразовываю головоломку в примитивный массив int для ее решения, а затем снова возвращаюсь в головоломку NSMutableArray. Что забавно, так это то, что теперь NSMutableArray сетки теперь имеет решение... Это означает, что каким-то образом внутри MESudokuSolver массив сетки изменяется. Поэтому я провел небольшое исследование, указатель на массив, который передается в экземпляр MESudokuSolver, отличается от головоломки NSMutableArray MESudokuSolver. Странно, правда? Я знаю.

После дальнейшего расследования указатель на номера NSN в массивах с разными указателями фактически одинаков.

Вам Stackru, я спрашиваю, WTF?

1 ответ

Решение

Когда вы инициализируете массив с содержимым другого массива, содержимое обоих массивов будет ссылаться на одни и те же объекты. Что вы хотите сделать, это выполнить глубокую копию. Это гарантирует, что каждый массив ссылается на свою собственную копию объекта, поэтому, если вы измените объект в одном массиве, это не повлияет на объект в другом массиве, потому что они на самом деле являются разными объектами. Это относится даже к массивам массивов. Существует несколько подходов к выполнению глубоких копий. Поскольку вам нужны изменяемые копии ваших изменяемых массивов внутри изменяемого массива, это немного сложнее, но, тем не менее, просто:

// Implemented as a free function here, but this is not required.

NSMutableArray *MECopyGrid(NSMutableArray *outer)
{
    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:[outer count]];

    for (NSMutableArray *inner in outer)
    {
        NSMutableArray *theCopy = [inner mutableCopy];
        [result addObject:theCopy];
        [theCopy release];
    }

    return result;
}

Остерегайтесь также оптимизаций NSNumber. Какао (и я предполагаю, что Cocoa Touch также) кэширует несколько разных экземпляров NSNumber. Поскольку экземпляры NSNumber являются неизменными, если вы попросите [NSNumber numberWithInteger:1]Какао может дать вам ссылку на существующий экземпляр, содержащий то же значение. Если вы заметили, что указатели экземпляра NSNumber совпадают, скорее всего, потому что Какао дал вам старый экземпляр. Это сэкономит память, особенно в таких ситуациях, как ваша (без оптимизации вам потребуется 81 независимый экземпляр NSNumber, но с оптимизацией вам потребуется не более 9).

Другие вопросы по тегам