Цель C ARC NSMutableArray - возвращено из функции - не выпущено? - PullRequest
0 голосов
/ 19 ноября 2011

Я читал много постов и не понимаю, почему это проблема - разве ARC не справится с этим?

Используя приведенные ниже ответы, я смог изолировать проблему как "Moves" как виновника - она, кажется, никогда не освобождается и продолжает потреблять память, даже если для нее установлено значение nil. Moves устанавливается в двух местах внизу Grid.m - методы FindRandomMove, и это вызывается в подпрограмме recursiveProcess.

Общая структура представляет собой сетку из столбцов, составленную из ячеек. Значение Cells генерируется случайным образом для составления загадки. Это делается много раз для тестирования решений головоломки.

Программа запускается при запуске generateTest в Grid.m

(Я ценю все комментарии к любой части кода, если я делаю что-то, что не соответствует передовым методам или есть лучший / правильный способ сделать это).

Полный код проверки:

Cell.h
@interface Cell : NSObject {

CFMutableBitVectorRef _validNumbersBitVector;
NSUInteger _answerNumber; 
UIColor * _color;
NSUInteger _cellRow;
NSUInteger _cellCol;
}   

@property CFMutableBitVectorRef validNumbersBitVector;
@property NSUInteger answerNumber; 
@property (nonatomic, strong) UIColor * color;
@property NSUInteger cellRow;
@property NSUInteger cellCol;

// набор методов

- (void) setValid:         (BOOL)valid   number:(NSUInteger)numberToChange;
- (void) setAllNumbersToValidState:(BOOL) valid;

// get methods

- (BOOL) numberIsValid: (NSUInteger)numberToCheck;
- (NSUInteger) getRandomValidNumber;
- (NSUInteger) validNumberCount;

@end

Cell.m

#import "Cell.h"

#define RANGE CFRangeMake(0,9)

@implementation Cell

@synthesize validNumbersBitVector = _validNumbersBitVector;
@synthesize answerNumber = _answerNumber; 
@synthesize color = _color;
@synthesize cellRow = _cellRow;
@synthesize cellCol = _cellCol;

-(id) init {

    if ((self = [super init])){

        _answerNumber = 0;
        _validNumbersBitVector =CFBitVectorCreateMutable(NULL,10);
    }
    return self;
}

- (void) setValid:(BOOL)valid number:(NSUInteger)numberToChange {

    CFBitVectorSetBitAtIndex(self.validNumbersBitVector, numberToChange - 1, !valid);
}

-(void) setAllNumbersToValidState:(BOOL) valid{

    CFBitVectorSetBits(self.validNumbersBitVector, RANGE, !valid);
}

-(NSUInteger) validNumberCount{

    return CFBitVectorGetCountOfBit(self.validNumbersBitVector, RANGE, 0);
}

- (BOOL) numberIsValid: (NSUInteger)numberToCheck{

     return !CFBitVectorGetBitAtIndex(self.validNumbersBitVector, numberToCheck - 1);
}

- (NSUInteger) getRandomValidNumber{

    NSUInteger pos;
    NSUInteger counter = 0;
    NSUInteger number = 0;
    NSUInteger validCount = 0;

    validCount = CFBitVectorGetCountOfBit(self.validNumbersBitVector, RANGE, 0);

    if (validCount > 0) {
        pos = (rand() %(validCount)) + 1;

        while (counter < pos){
            number ++;
            if (!CFBitVectorGetBitAtIndex(self.validNumbersBitVector, number - 1) ) {
                counter ++;
            }
        }
    }
    return number;
}    
@end

Moves.h

#import "Cell.h"

@interface Moves : NSObject {       
    NSUInteger _row;
    NSUInteger _col;
    NSUInteger _newValue;
    Cell     * _cell;
}   
@property NSUInteger row;
@property NSUInteger col;
@property NSUInteger newValue;
@property (nonatomic,retain) Cell     * cell;

@end

Moves.m

#import "Moves.h"

@implementation Moves

@synthesize row = _row;
@synthesize col = _col;
@synthesize newValue = _newValue;
@synthesize cell =  _cell;

-(id) init {
    if ((self = [super init])) {
        _row = 0;
        _col = 0;
        _newValue = 0;
    }

    return self;
}

@end

Grid.h

#import "Col.h"
#import "Moves.h"

@interface Grid : NSObject {

NSMutableArray* _rowData;
    NSMutableArray* _emptyCells;
    BOOL _puzzleSolved;
BOOL _bruteForceEnd;    
}
@property (nonatomic, retain) NSMutableArray * rowData;
@property (nonatomic, retain) NSMutableArray * emptyCells;
@property BOOL puzzleSolved;
@property BOOL bruteForceEnd;

- (void) generateTest;
- (void) initializePuzzle;
- (Cell *) cellAtRow:(NSUInteger) pRow andCol:(NSUInteger) pCol;
- (void) setAnswerNumber:(NSUInteger)newAnswerNumber cell:(Cell *) pCell;
- (Moves*) FindRandomMove;
- (NSUInteger) recursiveProcess:(NSUInteger)BFCount;

@end

Grid.m

#import "Grid.h"
#import <QuartzCore/QuartzCore.h>

@implementation Grid 

@synthesize rowData = _rowData;
@synthesize emptyCells = _emptyCells;
@synthesize puzzleSolved = _puzzleSolved;
@synthesize bruteForceEnd = _bruteForceEnd;

- (id) init 
{
if ((self = [super init])) {

    _rowData = [[NSMutableArray alloc] initWithCapacity:10];
    _emptyCells = [[NSMutableArray alloc] init];

    NSUInteger i;

    for (i = 0; i<9; i++) {
        Col *tmpCol = [[Col alloc] initWithRow:i + 1];
        [_rowData addObject:tmpCol];
     }
}
return self;
}

- (void) generateTest {

for (int mloop2 = 1; mloop2 < 10000;mloop2 ++) {       
    for (int mloop = 0; mloop < 1000; mloop ++) {
        [self initializePuzzle];        
        [self recursiveProcess:0];
    }
}
}

- (void) initializePuzzle{

self.bruteForceEnd = NO;
[self.emptyCells removeAllObjects];

for (Col * tmpCol in self.rowData){
    for (Cell * answerCell in tmpCol.colData){
        answerCell.answerNumber = 0;
        [self.emptyCells addObject:answerCell];
    }
}
}

- (Cell *) cellAtRow:(NSUInteger) pRow andCol:(NSUInteger) pCol {

Cell * tmpCell;
tmpCell = [[self.rowData objectAtIndex:pRow - 1] cellAtCol:pCol];
return tmpCell;
tmpCell = nil;
}


- (void) setAnswerNumber:(NSUInteger)newAnswerNumber cell:(Cell *) pCell{

if (pCell.answerNumber != newAnswerNumber) {
    if (newAnswerNumber == 0 ) [self.emptyCells addObject: pCell];
    if (pCell.answerNumber == 0) [self.emptyCells removeObject:pCell]; 
    pCell.answerNumber = newAnswerNumber; 
}    
pCell = nil;
}

- (NSUInteger) recursiveProcess:(NSUInteger)BFCount{

Moves * nextMove; 
Cell * answerCell;
NSUInteger counter;
NSUInteger number;
NSUInteger row;
NSUInteger col;

BFCount ++;
nextMove = [self FindRandomMove];
number = nextMove.newValue;
row = nextMove.row;
col = nextMove.col;
nextMove = nil;

answerCell = [self cellAtRow:row andCol:col];
[self setAnswerNumber:number cell:answerCell];
answerCell = nil;

if (BFCount > 48) self.bruteForceEnd=YES; 
if (self.puzzleSolved) self.bruteForceEnd = YES;
if (self.bruteForceEnd) return BFCount;
counter = [self recursiveProcess:BFCount]; // try again
if (counter > 50) NSLog(@"brute force 50");
return BFCount;
}

- (Moves *) FindRandomMove{

Moves * tMove =[[Moves alloc] init]; 
NSUInteger col = 0;
NSUInteger row = 0;
NSUInteger possibleCount = 0;
NSMutableArray * possibleCells = [[NSMutableArray alloc] initWithCapacity:82];
NSUInteger selectedPossible;
NSUInteger numberToUse = 0;
Cell * answerCell ;

NSUInteger validCount = 0;

for ( Cell * tmpCell in self.emptyCells){

    validCount=tmpCell.validNumberCount;       
    [possibleCells addObject:tmpCell];
    possibleCount ++;                   

}

if (possibleCount != 0){
    selectedPossible = 0;       

    if (possibleCount > 1) selectedPossible = rand() % (possibleCount - 1);

    answerCell = [possibleCells objectAtIndex:selectedPossible];

    row = answerCell.cellRow;
    col = answerCell.cellCol;
    numberToUse = answerCell.getRandomValidNumber;
    answerCell = nil;

}

possibleCells = nil;
tMove.row = row;
tMove.col = col;
tMove.newValue = numberToUse;

return tMove;
}


@end

Ответы [ 2 ]

1 голос
/ 19 ноября 2011

НОВЫЙ ОТВЕТ НА ОСНОВЕ КОДА ОБНОВЛЕНИЯ В ПОСТЕ:

Вся эта реализация довольно запутанная. Я собираюсь ограничить свой ответ конкретно проблемами памяти mgmt. В generateTest вы в конечном итоге вызываете initializePuzzle 10 000 000 раз, и в каждой итерации recursiveProcess может вызываться до 49 раз. Таким образом, вы выделяете Moves объект до 490 000 000 раз. Ваша проблема не в том, что объект не освобождается, а в том, что ваш цикл выполнения выделяет огромное количество объектов, которые не освобождаются до завершения цикла выполнения. Быстрое решение для вашего тестового кода - сделать следующее:

- (NSUInteger)recursiveProcess:(NSUInteger)BFCount
{
    @autoreleasepool {
        // your code here
    }
}

Честно говоря, я бы переписал это, чтобы упростить логику и устранить рекурсию. Кроме того, если вам нужно сделать что-то, что повторяет это много раз, вы должны выполнить эту обработку в фоновом режиме и позволить вашему потоку пользовательского интерфейса оставаться разблокированным. Это позволяет пользователям выполнять какие-либо действия (например, отменять игру, перезапускать и т. Д.) Без ожидания.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

Из кода, который вы опубликовали, я не могу рассказать о контексте cellObjects и о том, что происходит с ним после вызова [cell objectsRemoveAllObjects]. Если вы сделали с массивом вместо удаления объектов, просто сделайте cellObjects = nil.

Как сказал @Jesse, вы должны убедиться, что вы не создали цикл сохранения. Если какая-либо ваша обработка происходит в фоновом режиме или отправляется с блоками, которые могут вызвать цикл сохранения в зависимости от того, как вы назначаете / ссылаетесь на объекты. Например, ссылка на себя внутри отправленного блока.

Во время компиляции ARC будет следить за тем, чтобы осиротевшие объекты (например, ни один другой объект не имел на них сильную ссылку) были выпущены правильно.

Если вы не обнаружите проблему, вы можете опубликовать больше кода, показывающего полный жизненный цикл массива, который еще не выпущен. Специфика вашей обработки не важна, кроме любого кода, который создает, присваивает или обнуляет массив или объекты, ссылающиеся на массив.

0 голосов
/ 19 ноября 2011

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

Если это так (или если клетка держится за что-то, что держится за что-то, что ... держится за подмножество ячеек), то вам придется как-то разорвать эту цепочку; возможно, обнуляя то, с чем вы покончили, или сделав что-то слабым указателем.

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

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