Нужна помощь с проблемой управления памятью в моей игровой модели - PullRequest
1 голос
/ 05 апреля 2010

Я программист начального уровня, пытающийся сделать игровое приложение для iphone, и я до сих пор сталкивался с возможной проблемой управления памятью (exc_bad_access) моей программы. Я искал и читал десятки статей, касающихся управления памятью (включая документы Apple), но я до сих пор не могу понять, что именно не так с моими кодами. Поэтому я был бы очень признателен, если бы кто-нибудь помог мне разобраться в том беспорядке, который я устроил для себя.

//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 не будет вызван во 2-й раз после вызова endOpenState. Так что это как tap_screen -> tap_screen -> press_button_to_call_endOpenState -> нажмите экран -> tap_screen -> crash

Я думаю о том, что fencePoleArray испортился, когда я использовал [fencePoleArray removeAllObjects], потому что он не падает, когда я закомментирую его. Было бы здорово, если бы кто-нибудь объяснил мне, что пошло не так. И заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 05 апреля 2010

Сначала пара предложений:

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];

Это короче и безопаснее, поскольку ограничение xMax в вашем коде вычисляется из fencePoleArray и используется для итерации по fencePoleImageArray. (Может быть правильно, может быть неправильно.)

Теперь на звонок objectAtIndex:. Если массив все еще находится в памяти и вы пытались получить доступ к объекту за пределами массива, вы получите исключение. Так что я предполагаю, что либо массив, либо некоторые объекты в нем были освобождены без вашего ведома. Вы можете попробовать NSLog массив и объект по заданному индексу и попытаться записать их retainCount. Если происходит сбой строки журнала, вы нашли освобожденный объект и можете начать поиск причины.

(И еще одна вещь: вы должны разделить игровую логику на отдельный класс модели. Это упрощает код и облегчает рассуждения.)

0 голосов
/ 05 апреля 2010

Если вы хотите использовать свойства, вы должны использовать self.propertyName = ... вместо propertyName = ....

Надеюсь, это поможет.

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