Objective-c.Как правильно обрабатывать копию объекта, чтобы она не создавала утечек памяти?

#objective-c #memory-leaks #copy #deep-copy

#objective-c #утечки памяти #Копировать #глубокое копирование

Вопрос:

У меня есть некоторые утечки памяти, и я не совсем уверен, что это вызывает, но после некоторого тестирования кажется, что я облажался при копировании своего объекта, и он не выпущен должным образом. Позвольте мне сначала сказать вам, что я использую ARC, и не так давно я мало что знал о копиях или расширенном управлении памятью, поэтому мне еще многому предстоит научиться. Хорошо, давайте добавим некоторые подробности, у меня есть 2 класса Tree и TreeCell.

Дерево:

 @interface Tree : NSObject<NSCopying>

//Here we have 2D array that stores pointers to TreeCell objects
@property (retain,nonatomic)NSMutableArray *tree;

//pointersToTree stores copied pointers from tree, 
//it's the same in terms of content but it have different order of rows and columns
@property (retain,nonatomic)NSMutableArray *pointersToTree;

//i don't believe delegate is relevant to my problems (since it's not copied)
@property (weak,nonatomic) id<treeDelagate>delagate;
…
  

Итак, в short Tree хранятся 2 одинаковых указателя на каждую create TreeCell, по одному в каждом массиве.

TreeCell:

 @interface TreeCell : NSObject<NSCopying>

//ancestors and children stores arrays made of 2 NSNubers 
@property (retain,nonatomic)NSMutableArray * ancestors;
@property (retain,nonatomic)NSMutableArray * children;

@property (retain,nonatomic)NSNumber* boardY;
@property (retain,nonatomic)NSNumber* boardX;
...// there are some more Integers and Booleans
  

И вот как выглядят функции копирования, они оба работают, но я не уверен, правильно ли они реализованы, поэтому начинаем с дерева:

 -(id) copyWithZone: (NSZone *) zone{
    Tree *copy = [[Tree allocWithZone: zone] init];

    //this array will become copy of self.tree
    NSMutableArray* copyOfTree=[[NSMutableArray alloc]init];

    //loop to go thru all stored TreeCell pointers in self.tree
    for (NSInteger a=0; a<self.tree.count; a  ) {
     //adding row   
     [copyOfTree addObject:[[NSMutableArray alloc]init]];

        for (NSInteger b=0; b<[self.tree[a] count]; b  ) {
            //creating copy of TreeCell and storing pointer to it
            [copyOfTree[a] addObject:[((TreeCell*)self.tree[a][b]) copy]];
        }
    }
    copy.tree=copyOfTree;
    //by now we copied whole self.tree and since i have function to create pointersToTree out of it I call it below
    [copy creatArryOfPointer_Rows:self.pointersToTree.count columns:[self.pointersToTree[0]count]];

    return copy;

}
  

Итак, я пытаюсь получить здесь глубокую копию дерева. Хорошо, вот функция копирования TreeCell:

 -(id) copyWithZone: (NSZone *) zone{

    //we start here by allocating and setting NSNubers boardY and boardX
    TreeCell* copy = [[TreeCell allocWithZone: zone] initWithLayer:self.layer boardX:self.boardX andBoardY:self.boardY];

    //after that some integers and booleans are set
    copy.helper=self.helper;
    copy.inTree=self.inTree;
    copy.stableConnected=self.stableConnected;
    copy.positon=self.positon;
    copy.freeStoreg=self.freeStoreg;

   // here we are adding arrays containg 2 NSNumbers
    for (int a=0; a<self.ancestors.count; a  ) {
        [copy addAncestorWithX:self.ancestors[a][0] andY:self.ancestors[a][1]];
    }
    for (int a=0; a<self.children.count; a  ) {
        [copy addChildWithX:self.children[a][0] andY:self.children[a][1]];

    }

    return copy;

}
  

Чтобы прояснить ситуацию, здесь addAncestor функция addChildren выглядит так же:

 -(void) addAncestorWithX:(NSNumber*)ancestorX andY:(NSNumber*)ancestorY{
    if ([self.ancestors indexOfObject:[NSArray arrayWithObjects:ancestorX,ancestorY, nil]]==NSIntegerMax) {
    [self.ancestors addObject:[NSMutableArray arrayWithObjects:ancestorX,ancestorY, nil]];
    }
  

Наконец, этот фрагмент кода, который создает утечку памяти:

 for (NSInteger a=0; a<8000; a  ) {
         Tree* testTree =[self.startingTree copy];
}
  

В конце каждого цикла этого цикла должно быть выпущено testTree, после тестирования я на 100% уверен, что для него вызывается dealloc, а также вызывается для каждой ячейки дерева, которая была сохранена в дереве. Но использование памяти постоянно увеличивается. Пожалуйста, скажите мне, где я ошибся. Некоторые параболы идут и говорят, попробуйте сериализовать его, но это не сокращает его для моего.

Комментарии:

1. Чувак, я пытаюсь разобрать, но это ломает мне голову, у вас есть класс под названием tree, который также имеет свойство под названием tree? Итак, в какой-то момент вы получаете строку, которая в основном гласит: «копия объекта дерева классов имеет свойство с именем tree, которое представляет собой массив, которому вы присваиваете массив с именем copyOfTree (но это копия дерева свойств, а не объекта). Это похоже на процедуру «кто первый». 🙂

2. Вы, может быть, называете основной класс таким образом, что это было не самым умным, но вы поняли, что это именно то, что происходит в коде. Класс Tree — это все о его дереве свойств, которое является 2D-массивом, поэтому, когда я хочу копию класса, я не буду копировать этот массив, поэтому копируйте каждую ячейку дерева, которая находится в этом массиве (ну, внутри на самом деле есть указатели на них). Я полагаю, что моя проблема заключается где-то в функции copyWithZone для TreeCell, завтра я проведу еще несколько тестов. Вероятно, это NSNumbers, которые всегда являются проклятием моего существования.

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

4. Да, я тоже так считаю. На самом деле тот факт, что для TreeCell вызываются deallocs, будет означать, что больше нет ссылок на этот объект и его родительский элемент, который является деревом, поэтому внутри самой TreeCell должны быть какие-то оставшиеся объекты, а их не так много. Я надеюсь, что это все. Хорошо, поскольку сейчас 2 часа ночи, я назову это на сегодня. Спасибо!

Ответ №1:

Я думаю, что вы делаете какие-то сумасшедшие вещи с копированием, но я не могу точно сказать, какова ваша цель при копировании, поскольку она относится к фактически объектам, хранящимся в массиве, поэтому я могу ошибаться. Без написания множества возможных ответов и комментариев. Сначала посмотрите, решает ли это ваши проблемы:

 NSMutableArray *someArray = [NSMutableArray array];

NSMutableArray *aCopyOfSomeArray = [NSMutableArray arrayWithArray:someArray];
  

Комментарии:

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

Ответ №2:

И вот оно у меня есть! Проблема была внутри моей функции для добавления дочерних элементов и предков.

 [self.ancestors addObject:[NSMutableArray arrayWithObjects:ancestorX,ancestorY, nil]];
  

Это, безусловно, добавило массив, который я хотел, но он не выделил для него место!

Итак, я изменил этот код:

 for (int a=0; a<self.ancestors.count; a  ) {
        [copy addAncestorWithX:self.ancestors[a][0] andY:self.ancestors[a][1]];
    }
    for (int a=0; a<self.children.count; a  ) {
        [copy addChildWithX:self.children[a][0] andY:self.children[a][1]];

    }
  

В это:

 for (int a=0; a<self.ancestors.count; a  ) {

        [copy.ancestors addObject:[[NSMutableArray alloc]init]];
        [copy.ancestors[a] addObject:self.ancestors[a][0]];
        [copy.ancestors[a] addObject:self.ancestors[a][1]];

    }
    for (int a=0; a<self.children.count; a  ) {

        [copy.children addObject:[[NSMutableArray alloc]init]];
        [copy.children[a] addObject:self.children[a][0]];
        [copy.children[a] addObject:self.children[a][1]];



    }
  

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