Проблемы управления памятью в Objective-C - PullRequest
1 голос
/ 16 марта 2011

Я новичок в программировании для iPhone и Objective-C. Я создаю приложение на основе View.

Проблема в том, что ни одна из функций dealloc в UIViewController никогда не вызывается. Я решил выгрузить все мои сохраненные объекты программно, прямо перед представлением следующего класса UIViewController. Я устранил все утечки, обнаруженные XCode, протестировал приложение с помощью инструментов утечки и распределения, и все шло нормально, но используемая память накапливается примерно до 180 МБ, и приложение вылетает.

self.retainCount перед представлением следующего UIViewController равно 1, принадлежащие объекты освобождаются, а указатели равны нулю, но память все равно накапливается.

Можете ли вы дать мне подсказку? При необходимости я выложу несколько примеров кода. Или вы можете прислать мне несколько ссылок на что-то вроде управления памятью Objective C 101?

Это мой интерфейс:

@interface GameScreen : UIViewController
{
    IBOutlet UILabel     *timeLabel;
    IBOutlet UIView      *gameView;
    IBOutlet UIImageView *gameViewFrame;
    IBOutlet UIButton    *showOutlineButton;    
    UIImage *puzzleImage;
    UIView *optionsView;
    UIView *blockView;  
    NSTimer *timer;
    UIImageView *viewOriginalPicture;
    SHKActivityIndicator *activityIndicator;
    BOOL originalPictureShown;
    BOOL outLineShown;
}

@property (nonatomic, retain) IBOutlet UILabel     *timeLabel;
@property (nonatomic, retain) IBOutlet UIView      *gameView;
@property (nonatomic, retain) IBOutlet UIImageView *gameViewFrame;
@property (nonatomic, retain) IBOutlet UIButton    *showOutlineButton;
@property (nonatomic, retain) UIImage *puzzleImage;

А вот и реализация:

- (id) initWithPuzzleImage: (UIImage *) img
{
if((self = [super init]))
{
    puzzleImage = [[UIImage alloc] init];
    puzzleImage = img;
}
return self;
}

Эта функция вызывается, когда пользователь нажимает кнопку выхода:

- (void) onExit
{   
[timer invalidate];
CurrentMinuts = 0;
CurrentSeconds = 0;

    //remove piece configurations
    [pieceConfigMatrix removeAllObjects];
    [pieceFramesMatrix removeAllObjects];

PuzzleViewController *modalView = [[PuzzleViewController alloc] init];
    [self unloadObjects];
[self presentModalViewController:modalView animated:YES];
[modalView release];

}

И функция unloadObjects:

- (void) unloadObjects
{
    [self resignFirstResponder];         
    [viewOriginalPicture release];
    viewOriginalPicture = nil;  
    [timeLabel release];
    timeLabel = nil;
    [gameView release];
    gameView = nil;
    [originalImage release];
    originalImage = nil;
    [gameViewFrame release];
    gameViewFrame = nil;
    [timer release];
    [showOutlineButton release];
    showOutlineButton = nil;    
}

Я знаю, что я делаю неправильно, но я не уверен. Позволь мне объяснить. Я добавляю кусочки головоломки в свойство gameView. Для этого у меня есть объект «SplitImage». Следующая функция вызывается в - (void) viewDidLoad:

- (void) generatePuzzleWithImage:(UIImage *) image
{   
    SplitImage *splitSystem = [[SplitImage alloc] initWithImage:image andPuzzleSize:gPuzzleSize];
    [splitSystem splitImageAndAddToView:self.gameView];
    [splitSystem release];
}

Далее, функция инициализации для класса SplitImage и функции splitImageAndAddToView:

- (id) initWithImage: (UIImage *) image andPuzzleSize: (int) pSize
{
    if((self = [super init]))
    {
        UIImage *aux = [[[UIImage alloc] init] autorelease];
        pieceCenterSize = [SplitImage puzzlePieceSizeForNumberOfPieces:pSize];

        UIImage *outSideBallSample = [UIImage imageNamed:@"sampleHorizontal.jpg"]; //convexity size for puzzle size
        outSideBallSample = [outSideBallSample resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(pieceCenterSize, pieceCenterSize) interpolationQuality:kCGInterpolationHigh];      
    outSideBallSize = roundf(outSideBallSample.size.height);

        puzzleSize = pieceCenterSize * pSize;

        pieceNumber = pSize;

        if(image.size.height < puzzleSize || image.size.height > puzzleSize || image.size.width < puzzleSize || image.size.width > puzzleSize)
    {
        aux = [SplitImage resizeImageForPuzzle:image withSize:puzzleSize];
        aux = [SplitImage cropImageForPuzzle:aux withSize:puzzleSize];
    }

    aux = [aux imageWithAlpha];
    originalImage = [[UIImage imageWithCGImage:aux.CGImage] retain];

    mainImage = aux.CGImage;
    imageSize = CGSizeMake(aux.size.width, aux.size.height);
    NSLog(@"%@", NSStringFromCGSize(imageSize));

    splitImageSize = CGSizeMake(pieceCenterSize + 2*outSideBallSize, pieceCenterSize+2*outSideBallSize);
    }   
return self;
}



- (void) splitImageAndAddToView: (UIView *) view
{   
    for (int i = 0; i < pieceNumber; i++)
    for(int j = 0; j < pieceNumber; j++)

    //some code

    UIImage *mask;
    mask = [self randomlyRetriveMaskWithPrefix:1 forPieceAtI:i andJ:j];
    CGImageRef split = CGImageCreateWithImageInRect(mainImage, cuttingRect);

    PuzzlePiece *splitView = [[PuzzlePiece alloc] initWithImage:[UIImage imageWithCGImage:split] andMask:mask centerSize:pieceCenterSize objectMatrixPosition:i*pieceNumber+j outSideBallSize:outSideBallSize pieceType:pieceType pieceFrame:cuttingRect];

    [pieceFramesMatrix addObject:[NSValue valueWithCGRect:cuttingRect]];
    [splitView setTag:(i+1)*100+j];
    [view addSubview:splitView];
    CGImageRelease(split);
    [splitView release];
    }

Спасибо, Андрей

Ответы [ 4 ]

2 голосов
/ 16 марта 2011

Управление памятью Objective-C 101 находится здесь:

https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

Игнорировать данные о сборке мусора, они недоступны для iOS.

1 голос
/ 16 марта 2011

Во-первых, retainCount бесполезен.

Монитор активности почти так же бесполезен. Кроме того, поведение в симуляторе может сильно отличаться на фронте использования памяти. Лучше сфокусировать отладку такой проблемы на устройстве.

Далее, это:

Проблема в том, что ни один из UILiewController-функции для освобождения когда-либо называются. Я решил выгрузить все мои оставшиеся объекты программно, прямо перед представляя следующий UIViewController класс.

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

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

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

Вам также следует «Построить и проанализировать», а затем исправить все обнаруженные проблемы.

Далее, если вы видите увеличение памяти повторяющихся операций со стороны пользователя, то Анализ кучи чрезвычайно эффективен для точного определения того, что пошло не так.

Некоторые конкретные комментарии:

puzzleImage = [[UIImage alloc] init];
puzzleImage = img;

2 ошибки; Вы пропускаете UIImage и не удерживаете img. То, что ваше приложение не дает сбоя в свете вышеприведенного кода, указывает на то, что в других местах, вероятно, существует избыточное удержание.

1 голос
/ 16 марта 2011

Нормально освобождать объекты перед представлением следующего UIViewController.

Если у вас есть NIB, его содержимое будет загружено в первый раз, когда вы (или система) обращаетесь к члену представления. Ваш контроллер представления получит вызов loadView, а затем viewDidLoad. Если у вас есть какие-либо представления, которые вы хотите добавить программно, вы можете сделать это в loadView или viewDidLoad, если вы хотите опросить объекты, загруженные из NIB, вы можете сделать это в viewDidLoad.

После загрузки вид останется в памяти до тех пор, пока не появится предупреждение о нехватке памяти. В этот момент он будет выпущен. Вы получите viewDidUnload. Вы должны освободить все представления, связанные с тем, что у вас все еще есть ссылка на них, и установить в ноль любые слабые ссылки, которые у вас могут быть.

Любая попытка доступа к свойству view впоследствии приведет к перезагрузке NIB и т. Д.

Итак, когда вы представляете новый UIViewController, просто представьте его. Если вы создаете объекты, которые используются только для отображения этого контроллера, тогда делайте это на viewDidLoad и освобождайте их на viewDidUnload.

Несмотря на это, инструмент "Утечки" в инструментах должен иметь возможность сообщать вам, какие типы объектов являются утечками и где вы впервые их распределили, так что это действительно облегчает поиск утечек. Единственное, на что следует обратить внимание, это то, что если один объект обрабатывает свои свойства / элементы полностью правильно, но сам по себе утечка, то все, что он создает, обычно также будет утечкой. Поэтому, когда что-то протекает, проверьте, что созданное, оно не протекает, прежде чем рвать на себе волосы, почему вы не можете найти проблему.

1 голос
/ 16 марта 2011

retainCount не является надежным инструментом отладки.

Вы никогда не должны обращать внимание на retainCount или полагаться на него.То, что вы выпустили, не означает, что какая-то часть программы до сих пор не имеет ссылки на нее.retainCount не имеет значения.

Обычно, как правило.Если вы используете 'alloc', вы должны 'release' в какой-то момент.

Если, конечно, вы не поместили его в пул автоматического выпуска.

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

Публикуйте примеры своего кода о том, как вы создаете экземпляры объектов и где вы их выпускаете, возможно, вы делаете что-то не так на ранних этапах.

edit: извинения, я поставил 'init', а не 'alloc' ранее, спасибо, мечтаю, ошибка ранним утром.

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