Objective c 目标C ARC NSMutableArray-从函数返回-未发布?

Objective c 目标C ARC NSMutableArray-从函数返回-未发布?,objective-c,cocoa-touch,nsmutablearray,automatic-ref-counting,Objective C,Cocoa Touch,Nsmutablearray,Automatic Ref Counting,我已经阅读了很多帖子,对于为什么这是一个问题感到困惑——ARC不应该处理这个问题吗 利用下面的回复,我能够将问题隔离到“移动”作为罪魁祸首-它似乎永远不会被释放,并且即使设置为零也会持续消耗内存。移动设置在Grid.m-methods FindAndomMove底部的两个位置,在recursiveProcess例程中调用 一般结构是由柱组成的网格,由单元组成。随机生成单元格值以制作拼图。这是做了很多次来测试谜题的解决方案 程序首先在Grid.m中运行generateTest (如果我所做的事情不

我已经阅读了很多帖子,对于为什么这是一个问题感到困惑——ARC不应该处理这个问题吗

利用下面的回复,我能够将问题隔离到“移动”作为罪魁祸首-它似乎永远不会被释放,并且即使设置为零也会持续消耗内存。移动设置在Grid.m-methods FindAndomMove底部的两个位置,在recursiveProcess例程中调用

一般结构是由柱组成的网格,由单元组成。随机生成单元格值以制作拼图。这是做了很多次来测试谜题的解决方案

程序首先在Grid.m中运行generateTest

(如果我所做的事情不符合最佳实践或有更好/正确的方法来做,我非常感谢对代码任何部分的所有评论)

要遵循的完整测试代码:

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
移动

#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
网格.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
网格.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
#导入“Grid.h”
#进口
@实现网格
@综合行数据=_行数据;
@合成空细胞=_空细胞;
@综合拼图已解决=_拼图已解决;
@合成bruteforcend=\u bruteforcend;
-(id)init
{
if((self=[super init])){
_rowData=[[NSMutableArray alloc]initWithCapacity:10];
_emptyCells=[[NSMutableArray alloc]init];
NSUI;
对于(i=0;i 48)self.bruteforcend=YES;
如果(self.puzzsolved)self.bruteforcend=YES;
if(self.bruteforcend)返回BFCount;
计数器=[自递归进程:BFCount];//重试
如果(计数器>50)NSLog(@“暴力50”);
返回计数;
}
-(移动*)FindRandomMove{
移动*tMove=[[Moves alloc]init];
n整数col=0;
整数行=0;
NSU整数可能计数=0;
NSMutableArray*possibleCells=[[NSMutableArray alloc]initWithCapacity:82];
选择一个可能的整数;
NSUTEGER numberToUse=0;
单元*应答单元;
NSU整数validCount=0;
用于(Cell*tmpCell在self.emptyCells中){
validCount=tmpCell.validNumberCount;
[possibleCells addObject:tmpCell];
可能计数++;
}
如果(可能计数!=0){
selectedmable=0;
如果(可能计数>1)选择了可能=兰德()%(可能计数-1);
answerCell=[possibleCells objectAtIndex:selectedPossible];
行=answerCell.cellRow;
col=answerCell.cellCol;
numberToUse=answerCell.getRandomValidNumber;
answerCell=nil;
}
可能数=零;
tMove.row=行;
tMove.col=col;
tMove.newValue=numberToUse;
返回tMove;
}
@结束

我猜您有一个retain循环。在这种情况下,可能有一个单元格保存了对“cellObjects”子集的引用?如果一个单元格保存了子集数组,而该数组保存了该单元格,则不会释放任何内容

如果是这样的话(或者如果一个单元格抓住了某个东西,而这个东西……抓住了单元格子集),那么你就必须以某种方式打破这个链条;也许是通过把你已经做过的某件事归零,或者把某件事变成一个弱指针


Retain循环不会显示为泄漏,但是如果您使用heap工具,它将显示什么可能会使您的阵列保持不变。

基于POST中的更新代码的新答案:

整个实现相当复杂。我将把我的答案专门限制在内存管理问题上。在
generateTest
中,您最终调用
initializePuzzle
10000000次,并且在每次迭代中
recursiveProcess
最多可以调用49次。因此,您分配了一个
Moves
对象多达49000000次。您的问题不是对象没有被释放,而是您的运行循环正在分配大量的对象,这些对象在运行循环结束之前不会被释放。测试代码的快速修复方法是执行以下操作:

- (NSUInteger)recursiveProcess:(NSUInteger)BFCount
{
    @autoreleasepool {
        // your code here
    }
}
坦白地说,我会重写它以简化逻辑并消除递归。此外,如果您必须执行多次迭代的操作,您应该在后台执行此处理,并允许您的UI线程保持畅通。这允许用户在不等待的情况下执行操作(例如取消游戏、重新启动等)

原始答案:

从您发布的代码中,我无法分辨cellObjects的上下文以及调用
[cell objects RemoveAllObjects]
后cellObjects的情况。如果您使用数组而不是删除对象,只需执行
cellObjects=nil

正如@Jesse所说,你应该确保你没有创建一个保留周期。如果你的任何处理是在后台进行的,或者被调度的块可能导致一个保留周期,这取决于你如何分配/引用对象。例如,在调度的块中引用self

在编译时,ARC将确保正确释放孤立对象(例如,没有其他对象具有对它们的强引用)


如果您没有发现问题,您可以发布更多显示未发布的阵列的完整生命周期的代码。处理的细节与创建、分配或取消阵列或引用阵列的对象的任何代码无关。

感谢堆工具上的提示。我将做更多的分析d发回。我怀疑这是一个retain循环。感谢您的回复。我将尝试nil!关于您对引用self.Cell.h的评论的问题我定义了接口属性_xxxx。然后在Cell.m-synthesis cellRow=_cellRow中使用属性而不使用uuuuu integer cellRow;属性nsuiger cellRow;,然后使用self.cellRow with在Cell.m中的函数中(我唯一一次使用_cellRow是在init中);这是否正确?您可以在任何地方使用IVAR。如果您定义了属性,则可以使用
self.propertyName
。使用getter/setter(例如self.propertyName)意味着调用setter/getter函数。合成版本
- (NSUInteger)recursiveProcess:(NSUInteger)BFCount
{
    @autoreleasepool {
        // your code here
    }
}