2010-04-05 78 views
1

我是一名尝试为iphone制作游戏应用程序的初学者级别程序员,并且迄今为止我遇到了我的程序的内存管理(exc_bad_access)可能存在的问题。我搜索并阅读了许多关于内存管理的文章(包括苹果的文档),但我仍然无法弄清楚我的代码究竟出了什么问题。所以如果有人能帮助我清理我为自己制造的混乱,我会非常感激。在我的游戏模型中需要内存管理问题的帮助

//in the .h file 
@property(nonatomic,retain) NSMutableArray *fencePoleArray; 
@property(nonatomic,retain) NSMutableArray *fencePoleImageArray; 
@property(nonatomic,retain) NSMutableArray *fenceImageArray; 

//in the .m file 
- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.gameState = gameStatePaused; 

    fencePoleArray = [[NSMutableArray alloc] init]; 
    fencePoleImageArray = [[NSMutableArray alloc] init]; 
    fenceImageArray = [[NSMutableArray alloc] init]; 

    mainField = CGRectMake(10, 35, 310, 340); 

    .......... 

    [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(gameLoop) userInfo:nil repeats:YES]; 
} 

所以基本上,玩家触摸屏幕来设置围栏/极

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    if(.......) { 

    ....... 

    } 
    else { 
     UITouch *touch = [[event allTouches] anyObject]; 
     currentTapLoc = [touch locationInView:touch.view]; 

     NSLog(@"%i, %i", (int)currentTapLoc.x, (int)currentTapLoc.y); 

     if(CGRectContainsPoint(mainField, currentTapLoc)) { 
      if([self checkFence]) { 
       onFencePole++; 
       //this 3 set functions adds their respective objects into the 3 NSMutableArrays using addObject: 
       [self setFencePole]; 
       [self setFenceImage]; 
       [self setFencePoleImage]; 

       ....... 

       } 
      } 
      else { 
       ....... 
      } 
     } 
    } 
} 

的setFence功能(setFenceImage和setFencePoleImage与此类似)

-(void)setFencePole { 
    Fence *fencePole; 
    if (!elecFence) { 
     fencePole = [[Fence alloc] initFence:onFencePole fenceType:1 fencePos:currentTapLoc]; 
    } 
    else { 
     fencePole = [[Fence alloc] initFence:onFencePole fenceType:2 fencePos:currentTapLoc]; 
    } 
    [fencePoleArray addObject:fencePole]; 
    [fencePole release]; 

,每当我按下游戏中的按钮,调用endOpenState来清除屏幕上的所有额外图像(栅栏/极点),并删除3个NSMutableArray中的所有现有对象。点是删除NSMutableArrays中的所有对象,但保留数组本身,以便稍后可以重用。

-(void)endOpenState { 

    ........ 

    int xMax = [fencePoleArray count]; 
    int yMax = [fenceImageArray count]; 

    for (int x = 0; x < xMax; x++) { 
     [[fencePoleImageArray objectAtIndex:x] removeFromSuperview]; 
    } 

    for (int y = 0; y < yMax; y++) { 
     [[fenceImageArray objectAtIndex:y] removeFromSuperview]; 
    } 

    [fencePoleArray removeAllObjects]; 
    [fencePoleImageArray removeAllObjects]; 
    [fenceImageArray removeAllObjects]; 

    ........ 
} 

崩溃发生在checkFence函数的这里。

-(BOOL)checkFence { 
    if (onFencePole == 0) { 
     return YES; 
    } 
    else if (onFencePole >= 1 && onFencePole < currentMaxFencePole - 1) { 
     CGPoint tempPoint1 = currentTapLoc; 
     CGPoint tempPoint2 = [[fencePoleArray objectAtIndex:onFencePole-1] returnPos]; // the crash happens at this line 
     if ([self checkDistance:tempPoint1 point2:tempPoint2]) { 
      return YES; 
     } 
     else { 
      return NO; 
     } 
    } 
    else if (onFencePole == currentMaxFencePole - 1) { 

    ...... 

    } 
    else { 
     return NO; 
    } 
} 

所以这里的问题是,一切工作正常,直到checkFence调用endOpenState后第二次调用。因此,它像tap_screen - > tap_screen - > press_button_to_call_endOpenState - >自来水屏幕 - > tap_screen - >崩溃

的我在想什么是fencePoleArray弄乱,当我用[fencePoleArray removeAllObjects],因为它不会崩溃的时候我评论一下。如果有人能向我解释出了什么问题,那真的很棒。并提前致谢。

+0

你在endOpenState复位onFencePole和currentMaxFencePole?如果不是,它看起来像你的代码可以访问(现在是空的)数组末尾的东西。 – user308405 2010-04-05 06:34:44

+0

这会抛出异常,不是吗? – zoul 2010-04-05 06:41:27

+0

是的,我重置onFencePole和currentMaxFencePole。 – Treon 2010-04-05 07:14:58

回答

0

首先,一对夫妇的建议:

if (!elecFence) { 
    fencePole = [[Fence alloc] initFence:onFencePole 
     fenceType:1 fencePos:currentTapLoc]; 
} 
else { 
    fencePole = [[Fence alloc] initFence:onFencePole 
     fenceType:2 fencePos:currentTapLoc]; 
} 

你正在做这个太辛苦了,这个怎么样:

const int fenceType = elecFence ? 2 : 1; 
Fence *fencePole = [[Fence alloc] initFence:onFencePole 
     fenceType:fenceType fencePos:currentTapLoc]; 

这:

int xMax = [fencePoleArray count]; 
int yMax = [fenceImageArray count]; 

for (int x = 0; x < xMax; x++) { 
    [[fencePoleImageArray objectAtIndex:x] removeFromSuperview]; 
} 

for (int y = 0; y < yMax; y++) { 
    [[fenceImageArray objectAtIndex:y] removeFromSuperview]; 
} 

可以缩短使用makeObjectsPerformSelector:

const SEL remove = @selector(removeFromSuperview); 
[fencePoleImageArray makeObjectsPerformSelector:remove]; 
[fenceImageArray makeObjectsPerformSelector:remove]; 

这会更短更安全,因为代码中的xMaxfencePoleArray开始计算并用于迭代fencePoleImageArray。 (可能是对的,可能是错的。)

现在拨打objectAtIndex:。如果数组仍然在内存中,并且您试图访问超出数组边界的对象,则会发生异常。因此,我猜想数组或其中的某些对象在您不知情的情况下被释放。您可以尝试NSLog阵列和给定索引上的对象,并尝试记录它们的retainCount。如果日志记录行崩溃,则已找到已释放的对象并可开始查找原因。

(还有一两件事:你应该在游戏逻辑拆分成一个独立的模型类,简化了代码,并使得它更容易推理。)

+0

感谢您的建议,但它仍然没有帮助我解决内存问题。我没有使用NSZombieEnabled变量的测试,我得到它说一个错误信息: *** - [护栏returnPos]:发送到释放实例0x3933000 我假设我fencePoleArray确实得到了释放的消息,但我看不出过度释放发生在哪里。 – Treon 2010-04-05 06:41:33

+0

太棒了,你已经差不多了。现在你所要做的就是在'Fence'的'release'方法中设置一个断点,并查看你释放实例的位置。发现罪犯应该只需要几分钟的时间(查看堆栈跟踪)。 – zoul 2010-04-05 06:49:07

+0

我该怎么做?我已经检查了我的代码,但我从来没有明确放过任何代码来释放我的fencePoleArray。最接近的匹配是[fencePoleArray removeAllObjects],它只释放数组中的对象,但保留数组本身(这是我的假设,即使当内部的所有对象都被删除时,数组仍然保留,所以如果我错了,请纠正我)。 – Treon 2010-04-05 06:55:27

0

如果你想使用属性,你应该使用self.propertyName = ...而不是propertyName = ...

希望这会有所帮助。

+0

究竟在哪里?在'viewDidLoad',这实际上会导致泄漏。 – zoul 2010-04-05 06:47:39