0

我在使用 ARC 的 iPhone 应用程序中遇到了一个相当严重的问题。

我有一个视图控制器(让我们称之为 A)。这个视图控制器打开一个导航控制器作为一个模态,它通过 3 个不同的视图控制器(我们称之为 1、2 和 3)。在查看第 3 号之后,navigationcontroller 关闭,我们又回到了 A。

所以流程是:A打开navigationcontroller并经过1->2->3然后再次关闭。

每次我经历这个流程时,我都会失去记忆。为了解决这个问题,我搜索了所有文件,寻找任何保留 og 强属性、未失效的计时器或类似的东西。

我有一个想法,这可能是问题所在。在 viewcontroller 1 我使用 coreanimation 和 sprite 呈现动画。我正在使用其他人制作的实现。似乎如果我禁用动画,使用的内存似乎相当稳定(因此不会丢失内存)。我已经稍微修改了实现以使用 ARC。这是我用于精灵动画的实现:

MCSpriteLayer.h

//
//  MCSpriteLayer.h
//
//  Created by Miguel Angel Friginal on 8/20/10.
//  Copyright 2010 Mystery Coconut Games. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface MCSpriteLayer : CALayer {
    unsigned int sampleIndex;
}

// SampleIndex needs to be > 0
@property (nonatomic) unsigned int sampleIndex; 

// For use with sample rects set by the delegate
+ (id)layerWithImage:(CGImageRef)img;
- (id)initWithImage:(CGImageRef)img;

// If all samples are the same size 
+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;

// Use this method instead of sprite.sampleIndex to obtain the index currently displayed on screen
- (unsigned int)currentSampleIndex; 


@end

MCSpriteLayer.m

//
//  MCSpriteLayer.m
//
//  Created by Miguel Angel Friginal on 8/20/10.
//  Copyright 2010 Mystery Coconut Games. All rights reserved.
//

#import "MCSpriteLayer.h"


@implementation MCSpriteLayer

@synthesize sampleIndex;

#pragma mark -
#pragma mark Initialization, variable sample size


- (id)initWithImage:(CGImageRef)img;
{
    self = [super init];
    if (self != nil)
    {
        self.contents = (__bridge id)img;
        sampleIndex = 1;
    }

    return self;
}


+ (id)layerWithImage:(CGImageRef)img;
{
    MCSpriteLayer *layer = [(MCSpriteLayer*)[self alloc] initWithImage:img];
    return layer;
}


#pragma mark -
#pragma mark Initialization, fixed sample size


- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;
{
    self = [self initWithImage:img];
    if (self != nil)
    {
        CGSize sampleSizeNormalized = CGSizeMake(size.width/CGImageGetWidth(img), size.height/CGImageGetHeight(img));
        self.bounds = CGRectMake( 0, 0, size.width, size.height );
        self.contentsRect = CGRectMake( 0, 0, sampleSizeNormalized.width, sampleSizeNormalized.height );
    }

    return self;
}


+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
{

    CGSize newSampleSize;
    if(useRetina == 1) {
        // Supporting retina displays
        if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
            ([UIScreen mainScreen].scale == 2.0)) {
            newSampleSize = CGSizeMake(size.width*2, size.height*2);
        } else {
            newSampleSize = size;

        }
    } else
        newSampleSize = size;

    MCSpriteLayer *layer = [[self alloc] initWithImage:img sampleSize:newSampleSize];
    return layer;
}


#pragma mark -
#pragma mark Frame by frame animation


+ (BOOL)needsDisplayForKey:(NSString *)key;
{
    return [key isEqualToString:@"sampleIndex"];
}


// contentsRect or bounds changes are not animated
+ (id < CAAction >)defaultActionForKey:(NSString *)aKey;
{
    if ([aKey isEqualToString:@"contentsRect"] || [aKey isEqualToString:@"bounds"])
        return (id < CAAction >)[NSNull null];

    return [super defaultActionForKey:aKey];
}


- (unsigned int)currentSampleIndex;
{
    return ((MCSpriteLayer*)[self presentationLayer]).sampleIndex;
}


// Implement displayLayer: on the delegate to override how sample rectangles are calculated; remember to use currentSampleIndex, ignore sampleIndex == 0, and set the layer's bounds
- (void)display;
{
    if ([self.delegate respondsToSelector:@selector(displayLayer:)])
    {
        [self.delegate displayLayer:self];
        return;
    }

    unsigned int currentSampleIndex = [self currentSampleIndex];
    if (!currentSampleIndex)
        return;

    CGSize sampleSize = self.contentsRect.size;
    self.contentsRect = CGRectMake(
        ((currentSampleIndex - 1) % (int)(1/sampleSize.width)) * sampleSize.width, 
        ((currentSampleIndex - 1) / (int)(1/sampleSize.width)) * sampleSize.height, 
        sampleSize.width, sampleSize.height
    );
}


@end

这个实现是否以某种方式无法正确实现或保留任何东西?提前致谢。

更新 - 我正在使用仪器来测量内存。我正在使用内存监视器,我在其中密切关注物理内存可用 - 图像是这样创建的:

NSString *path = [[NSBundle mainBundle] pathForResource:@"round_start.png" ofType:nil];
CGSize fixedSize = CGSizeMake(320, 480);
mascot = [MCSpriteLayer layerWithImage:[UIImage imageWithContentsOfFile:path].CGImage sampleSize:fixedSize :0];
mascot.frame = CGRectMake(ANIMATION_X, ANIMATION_Y, ANIMATION_WIDTH, ANIMATION_HEIGHT);
[self.view.layer addSublayer:mascot2];

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"sampleIndex"];
anim.delegate = self;
anim.fromValue = [NSNumber numberWithInt:1];
anim.toValue = [NSNumber numberWithInt:52];
anim.duration = ANIMATION_DURATION;
anim.repeatCount = 1;

[mascot addAnimation:anim forKey:nil];

- 我一直在经历关闭模式

[self dismissModalViewControllerAnimated:YES];

[self.navigationController dismissModalViewControllerAnimated:YES];
4

1 回答 1

1

假设您对它有很强的引用(这需要得到 nil'd),解除导航控制器并不会释放它。在它使用的视图控制器中,在 dealloc 方法中添加一条日志消息,这样您就知道它们正在被释放(您可以对任何子类项执行此操作)。如果需要,您可以创建一个简单的 UINavigation 子类,仅用于在 dealloc 中添加消息)。您会发现这些项目中的一个或多个没有被释放,然后您需要确定它们是否由属性/ivar 保留,或者因为它们仍然具有超级视图。

于 2013-06-03T18:13:27.113 回答