0

我一直在閱讀很多文章,並且很困惑爲什麼這是一個問題 - ARC不應該處理這個問題嗎?Objective C ARC NSMutableArray - 從函數返回 - 沒有發佈?

利用下面的回覆我能夠將問題隔離爲「移動」作爲罪魁禍首 - 它似乎永遠不會被釋放,並保持消耗內存,即使它被設置爲零。移動在Grid.m底部的兩個地方設置 - 方法FindRandomMove,並在recursiveProcess例程中調用。

一般結構是由列組成的網格,由單元組成。 Cells值是隨機生成的,以製作拼圖。這是多次測試拼圖解決方案。

程序開始在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 

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 

回答

1

基於更新代碼發佈新的答案:

這整個執行是相當令人費解。我將限制我的答案專門針對內存管理問題。在generateTest你最終打電話initializePuzzle 10,000,000次,在每次迭代recursiveProcess可能被調用49次。所以,你正在分配一個最多達490,000,000次的對象Moves。你的問題不是該對象沒有被釋放,而是你的運行循環分配了大量的對象,直到運行循環結束才釋放。要快速解決您的測試代碼將做到以下幾點:

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

坦率地說,我想改寫這個簡化的邏輯和消除遞歸。此外,如果您必須執行迭代很多次的操作,則應該在後臺執行此處理,並允許您的UI線程保持暢通。這允許用戶做事(例如取消遊戲,重新開始等)而不等待。

原來的答案:

從你貼我不能告訴cellObjects的背景下,什麼是發生在它調用[cell objectsRemoveAllObjects]後的代碼。如果您已完成數組而不是刪除對象,只需執行cellObjects = nil即可。

正如@Jesse所說,你應該確保你沒有創建一個保留週期。如果您的任何處理髮生在後臺或根據您分配/引用對象的方式使用可能導致保留週期的塊進行分派。例如,引用調度塊內的self。

在編譯時,ARC會確保孤立的對象(例如沒有其他對象具有強引用)會被正確釋放。

如果您沒有發現問題,則可以發佈更多代碼,以顯示未發佈的陣列的完整生命週期。除了創建,分配或刪除引用數組的數組或對象的任何代碼之外,處理的細節並不重要。

+0

感謝您的回覆。我會嘗試零! – user1054717

+0

關於您的評論以提及自我的問題。 Cell.h我定義了接口屬性_xxxx。然後使用屬性與_。 Ex NSUinteger _cellRow;屬性NSUInteger cellRow;在Cell.m中 - 合成cellRow = _cellRow;然後我在Cell.m中的函數中使用self.cellRow。 (我使用_cellRow的唯一時間是在init中);它是否正確 ? – user1054717

+0

您可以在任何地方使用iVars。如果你定義了一個屬性,你可以使用'self.propertyName'。使用getter/setter(例如self.propertyName)表示調用setter/getter函數。綜合版本是根據您的屬性定義生成的。當你使用iVar時,你直接分配你想要的任何值,如果不是在ARC下,你需要確保釋放舊值(如果需要)並保留新值(如果需要的話)。需要什麼取決於數據類型以及您希望iVar /屬性如何工作。 – XJones

0

我的猜測是,你已經有了一個保留循環。在這種情況下,可能其中一個單元格持有對「cellObjects」子集的引用?如果一個單元格保存到保存在單元格上的子集數組中,則永遠不會釋放任何東西。

如果是這種情況(或者如果一個單元格持有某個持有該單元子集的東西),那麼您必須以某種方式打破該鏈;也許是通過消除你所做的事情,或者做一些__weak指針。

保留循環不會顯示爲泄漏,但是如果您使用堆儀器,它會告訴您可能會保留您的陣列。

+0

感謝堆儀器上的提示。我會做更多的分析並回傳。我懷疑這是一個保留循環。 – user1054717