Проблемы с пониманием системы выделения cocos2d для iphone - PullRequest
0 голосов
/ 03 января 2012

Я пытаюсь узнать cocos2d для iphone, следуя следующей книге http://www.apress.com/9781430233039. Я изменил пример ShootEmUp3, доступный в папке CH_08 исходного кода (http://www.apress.com/downloadable/download/sample/sample_id/640/).

Что я хочу сделатьДля этого нужно иметь сцену меню и игровую сцену, и иметь возможность вернуться к сцене меню после завершения игровой сцены (например, все жизни потеряны). Из сцены меню я могу получить доступ к определенной GameScene, нажав назначки, которые являются экземплярами класса с именем «LevelIcon». Другими словами, сцена меню, которую я назвал «Navigator» (подкласс CCLayer), содержит экземпляры класса «LevelIcon» (подкласс NSObject), появляющегося в сцене меню иотвечая на событие касания, вызывая метод replaceScene в CCDirector, следующим образом:.

//From LevelIcon.m    
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    CCLOG(@"Touch");
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
    GameScene * game = [GameScene scene];
    [[CCDirector sharedDirector] replaceScene:game];
    return TRUE;
}

Я пытался освободить объекты в методе навигатора dealloc, но, добавив сообщение CCLOG в методе освобождения LevelIcon Iобнаружил, что метод выпуска никогда не вызывается.

//From Navigator.m

-(void)dealloc
{
    CCLOG(@"Navigator dealloc");
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);

    level1 = nil;
    [level1 dealloc];

    [super dealloc];
}

Затем я добавил журнал в UМетод pdate и заметил, что он все еще вызывается, даже если сцена была GameScene, а не сцена Navigator.Я не могу понять, что LevelIcon был объектом, созданным в Navigator, и в методе dealloc в Navigator я пытался освободить istance класса, как показано выше (, но не делает этого ).

В подтверждение этого я попытался вернуться назад и переместиться из GameScene в Navigator, чтобы вызвать следующий фрагмент кода в GameScene:

        Navigator * navigator = [Navigator scene];
    [[CCDirector sharedDirector] replaceScene:navigator];

И создание нового класса Navigator работаетправильно, но при возвращении в GameScene новый экземпляр класса LevelIcon не исчезает, а остается (так что он накапливается).Мы должны отметить, что метод replaceScene вызывается из класса LevelIcon, отвечая на событие касания следующим образом:

GameScene * game = [GameScene scene];
[[CCDirector sharedDirector] replaceScene:game];

Другими словами, после этого события вызывается метод выпуска Navigator, а не LevelIcon иесли несколько раз класс LevelIcon остается активным в памяти.

Я не уверен, что делаю неправильно, но был бы признателен за подсказку или помощь.

Я также попробовал другой подход, добавив istiance LevelIcon как дочерний элемент Navigator (используя метод addChild), но он не работает, потому что является производным от класса NSObject, а не от класса Node cocos2d.Мне было интересно, имеет ли смысл это каким-то образом подправлять или это не так, как для двух разных иерархий классов (cocos2d и NS / Objective-C).

Я вставляю полный код, чтобы помочь понять:

--NAVIGATOR.H-----

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "LevelIcon.h"

@interface Navigator : CCLayer {
    LevelIcon *level1;
}

+ (id) scene;

@end


-----NAVIGATOR.M------
#import "Navigator.h"
#import "LevelIcon.h"

@implementation Navigator

+(id) scene {
    CCScene *scene = [CCScene node];
    CCLayer *layer = [Navigator node];//??
    [scene addChild:layer];
    return scene;     
}

-(id)init 
{
    CCLOG(@"init");
    if((self=[super init])){        
        CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
        self.isAccelerometerEnabled=YES;
        self.isTouchEnabled = YES;

        [self scheduleUpdate];  
        level1 = [LevelIcon levelIconWithParentNode:self];
    }
    return self;
}

-(void)dealloc
{
    CCLOG(@"Navigator dealloc");
    CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);

    level1 = nil;
    [level1 dealloc];

    [super dealloc];
}
[..]
@end


------LEVELICON.H------------
#import <Foundation/Foundation.h>
#import "cocos2d.h"

@interface LevelIcon : NSObject<CCTargetedTouchDelegate> {
    CCSprite* levelIconSprite;
 }

+(id) levelIconWithParentNode:(CCNode*)parentNode;
-(id) initWithParentNode:(CCNode*)parentNode;

@end



-----LEVEL ICON.M----------------
#import "LevelIcon.h"
#import "GameScene.h"

@implementation LevelIcon

// Static autorelease initializer, mimics cocos2d's memory allocation scheme.
+(id) levelIconWithParentNode:(CCNode*)parentNode
{
    CCLOG(@"levelIconWithParentNode");  
    return [[[self alloc] initWithParentNode:parentNode] autorelease];
}

-(id) initWithParentNode:(CCNode*)parentNode 
{
    CCLOG(@"initWithParentNode");
    if ((self = [super init]))
    {
        CCLOG(@"initWithParentNode: inside if");
        CGSize screenSize = [[CCDirector sharedDirector] winSize];

        levelIconSprite = [CCSprite spriteWithFile:@"Icon.png"];
        levelIconSprite.position = CGPointMake(CCRANDOM_0_1() * screenSize.width, CCRANDOM_0_1() * screenSize.height);
        [parentNode addChild:levelIconSprite];

        // Manually schedule update via the undocumented CCScheduler class used internally by CCNode.
        [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO];

        // Manually add this class as receiver of targeted touch events.
        [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES];
    }

    return self;
}

-(void) dealloc
{
    CCLOG(@"Level icon dealloc");
    // Must manually unschedule, it is not done automatically for us.
    [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];

    // Must manually remove this class as touch input receiver!
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
    [super dealloc];
}

-(void) update:(ccTime)delta
{
    CCLOG(@"Icon Update!");
}


-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    CCLOG(@"Touch");
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
    GameScene * game = [GameScene scene];
    [[CCDirector sharedDirector] replaceScene:game];
    return TRUE;
}

@end

Спасибо за чтение!:)

Ответы [ 2 ]

2 голосов
/ 03 января 2012

Это довольно странно:

level1 = nil;
[level1 dealloc];

1) Вы не хотите напрямую вызывать dealloc, используйте [release1 level1], и когда счетчик ссылок достигнет 0, он будет автоматически удален

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

0 голосов
/ 04 января 2012

В конце дня я обнаружил, что ошибка не была напрямую вызвана вызовом объекта релиза.

Приведенный выше код показывает инициализацию автоматического выпуска, и, следовательно, не нужно вызыватьрелиз.Я согласен, с моей стороны произошла ошибка при попытке снова вызвать релиз в Navigator.Этому нет никаких оправданий (единственное, что я не знаю об этом, и попытка сделать это без успеха, безуспешно привела меня к попытке ошибочно поиграть с релизом, не доверяя малоизвестной «теории» и пытаясьнайти проблему более критично).

Проблема заключалась в том, что объект "как-то" сохранялся дважды, и, следовательно, dealloc никогда не вызывался.

Чтобы решить эту проблему, я добавил вызов

[[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];

в метод ccTouchBegan , чтобы возможные потери были потеряны.

Вв определении метода removeDelegate явного вызова к выпуску нет, но в sharedDispatcher addTargetedDelegate есть вызов ccArrayAppendObject , который сохраняет объект, переданный в качестве аргументасм. код ниже).

//From CCTouchDispatcher.m line 121
-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches 
{
    CCTouchHandler *handler = [CCTargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches];
    if( ! locked ) {
        [self forceAddHandler:handler array:targetedHandlers];
    } else {
        [handlersToAdd addObject:handler];
        toAdd = YES;
    }
}

//From ccCArray.h line 115 (after having opened the definition of several function starting from **addObject** being called by addTargetedDelegate I got here)
/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */
static inline void ccArrayAppendObject(ccArray *arr, id object)
{
    arr->arr[arr->num] = [object retain];
    arr->num++;
}

Затем, глядя на определение функции removeDelegate (см. код ниже), которая теперь вызывается в ccTouchBegan Я заметил, что sharedDispatcher] removeDelegate добавляет объект в список handlersToRemove , которые затем освобождаются.Это должно быть то, что заставляет объект быть освобожденным.

//From LevelIcon.m 
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    CCLOG(@"Touch");
    [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];

    GameScene * game = [GameScene scene];
    [[CCDirector sharedDirector] replaceScene:game];



    return TRUE;
}

//From CCTouchDispatcher.m line 153
-(void) removeDelegate:(id) delegate
{
    if( delegate == nil )
        return;

    if( ! locked ) {
        [self forceRemoveDelegate:delegate];
    } else {
        [handlersToRemove addObject:delegate];
        toRemove = YES;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...