【问题标题】:"Swapping" a UIView Instance variable - cannot dealloc "previous" view“交换”一个 UIView 实例变量 - 不能解除分配“上一个”视图
【发布时间】:2009-12-02 00:52:21
【问题描述】:

我想以某种方式组织我的 iPhone 游戏的关卡视图,但我根本做不到(没有扩展对象分配)。我为我的代码做了一个真正的“骨架”(这个游戏有 2 个级别,目标是发布 iPhone 显示器)。我只是无法释放上一个级别,因此仪器显示递增的 BGTangramLevel 实例。

请看一下,我需要一些有用的设计想法(我的第三个问题)。

viewcontroller.h

@interface compactTangramViewController : UIViewController
{
    //The level.
    BGTangramLevel *currentLevel;
    UIColor *levelColor;
}

//It is to be just a reference, therefore I use assign here.
@property (nonatomic, retain) BGTangramLevel *currentLevel;

-(void) notificationHandler: (NSNotification*) notification;
-(void) finishedCurrentLevel;

@end

viewcontroller.m

@implementation compactTangramViewController
@synthesize currentLevel;

//Initializer functions, setting up view hierarchy.
-(void) viewDidLoad
{   

    //Set up levelstepper.
    levelColor = [UIColor greenColor];

    //Set up "state" classes.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationHandler:) name:@"finishedCurrentLevel" object:nil];   

    //Attach level 1.
    currentLevel = [BGTangramLevel levelWithColor: levelColor frame:self.view.frame];
    [self.view addSubview:currentLevel];

    [super viewDidLoad];

}

//Release objects.
-(void) dealloc
{
    [currentLevel release];
    [super dealloc];
}

//Notification handling.
-(void) notificationHandler: (NSNotification*) notification
{
    //Execute level swap.
    if ([notification name] == @"finishedCurrentLevel") [self finishedCurrentLevel];
}

-(void) finishedCurrentLevel
{
    //Remove previous level.
    [currentLevel removeFromSuperview];
    //[currentLevel release];

    //Step level.
    if (levelColor == [UIColor greenColor]) levelColor = [UIColor blueColor]; else levelColor = [UIColor greenColor];

    //Attach level 2.
    currentLevel = [BGTangramLevel levelWithColor: levelColor frame:self.view.frame];
    [self.view addSubview:currentLevel];

}
@end

BGTangramLevel.h

@interface BGTangramLevel : UIView
{
    BOOL puzzleCompleted;   
}

//Initializer.
+(BGTangramLevel*)levelWithColor: (UIColor*) color frame: (CGRect) frame;

//Test if the puzzle is completed.
-(void) isSolved;

@end

BGTangramLevel.m

@implementation BGTangramLevel

//Allocated instance.
+(BGTangramLevel*)levelWithColor: (UIColor*) color frame: (CGRect) frame
{
    BGTangramLevel *allocatedLevel = [[BGTangramLevel alloc] initWithFrame:frame];
    allocatedLevel.backgroundColor = color;
    return allocatedLevel;
}

//Finger released.
-(void) touchesEnded: (NSSet*)touches withEvent: (UIEvent*)event
{
    //The completement condition is a simple released tap for now...
    puzzleCompleted = YES;
    [self isSolved];    
}

//Test if the puzzle is completed.
-(void) isSolved
{
    //"Notify" viewController if puzzle has solved.
    if (puzzleCompleted) [[NSNotificationCenter defaultCenter] postNotificationName:@"finishedCurrentLevel" object:nil]; 
}

-(void) dealloc
{
    NSLog(@"Will ever level dealloc invoked."); //It is not.
    [super dealloc];
}

@end

那我该怎么办?我尝试标记自动释放返回的级别实例,在 removeFromSuperview 之后释放 currentLevel,尝试以(非原子,分配)方式合成的 currentLevel 属性,但对象分配仍然增长。我可以避免通知吗?我被卡住了。

【问题讨论】:

    标签: iphone xcode design-patterns uiview dealloc


    【解决方案1】:

    您需要更严格地遵循保留/释放规则。你绝对不应该为了找到有用的东西而在一些地方实验性地添加保留、释放和自动释放。关于 Cocoa 内存管理的文章已经很多了,这里不再赘述。

    具体来说,BGTangramLevel 的levelWithColor:frame: 方法应该在将allocatedLevel 返回给它的调用者之前调用[allocatedLevel autorelease]。它不拥有该对象,由调用者决定是否保留它。

    您还需要了解访问实例变量和访问属性之间的区别。 Cocoa 的属性只是 getter 和 setter 方法的语法糖。当您在视图控制器中引用 currentLevel 时,您正在直接处理实例变量。当您引用 self.currentLevel 时,您正在处理该属性。

    即使您声明了一个属性,currentLevel = [BGTangram ...] 也只是将引用复制到变量中。在viewDidLoad 中,如果要通过属性的setter 方法,则需要使用self.currentLevel = [BGTangram ...],该方法将保留对象(因为您以这种方式声明了属性)。看出区别了吗?

    我认为您的泄漏发生在finishedCurrentLevel。如果您使用了self.currentLevel = [BGTangram ...],将调用属性的setter 方法,该方法将释放旧对象并保留新对象。因为您直接分配给实例变量,所以您只需覆盖对旧级别的引用而不释放它。

    在视图控制器的dealloc 方法中调用[currentLevel release] 是正确的。

    【讨论】:

    • 听起来很有希望。我期待着测试它。是的,返回 [allocatedLevel autorelease];因意外而跳过。这些分配器/初始化器应该返回自动释放标记的对象,这很清楚。大多数答案都建议使用合成访问器,我相信我确实使用了它们。我试图自己实现访问器以确保释放/保留,但没有用,因为我从未使用简单的 currentLevel=[... 分配来调用它们。希望 self.currentLevel=[... 有帮助。再次感谢。
    • 还有一个问题。假设我通过访问器获取/设置槽,我是否在调用 removeFromSuperview 之前再次手动保留 currentLevel (如文档所述)?嗯...不要想,因为我想在那里释放 BGTangramLevel 实例。
    • 一个视图保留它的子视图,所以addSubview:是一个隐含的retainremoveFromSuperview是一个隐含的release。但是,由于您的控制器负责保留和释放 currentLevel,因此您不必关心其他人是否这样做。文档只是指出,在不首先保留视图的情况下调用 removeFromSuperview 是一个常见错误,在这种情况下,它将立即被释放。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2018-03-17
    • 2013-10-24
    相关资源
    最近更新 更多