Oppure

Loading
08/05/12 12:57
_mikele_
Ciao a tutti, sono nuovo nella programmazione di dispositivi Apple e sto programmando un gioco per iPad come progetto scolastico con le librerie Cocos2d.

Ultimamente purtroppo mi sono bloccato perché l'applicazione va in crash quando cerco di accedere ad un array di tipo NSMutableArray (inizializzato nel metodo init()) in un altro metodo.

L'array dovrebbe contenere oggetti di tipo TeddyBear (classe creata da me).

[CODE]

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

@interface TeddyBear : NSObject
{
int life;

CCSprite *_teddyBearSprite;
CCAction *_walkAction;
CCAction *_moveAction;
CCAnimation *_walkAnim;
}

@property (nonatomic, retain) CCSprite *teddyBearSprite;
@property (nonatomic, retain) CCAction *walkAction;
@property (nonatomic, retain) CCAction *moveAction;
@property (nonatomic, retain) CCAnimation *walkAnim;

- (void) initTeddyBear: (int) l withAnimFrames:(NSMutableArray*) animFrames withSpriteFrameName:(NSString*) spriteFrameName withDelay:(float) d;

@end



// TeddyBear.m
#import "TeddyBear.h"

@implementation TeddyBear

@synthesize teddyBearSprite = _teddyBearSprite;
@synthesize walkAction = _walkAction;
@synthesize moveAction = _moveAction;
@synthesize walkAnim = _walkAnim;

- (void) initTeddyBear:(int) l withAnimFrames:(NSMutableArray*) animFrames withSpriteFrameName:(NSString*) spriteFrameName withDelay:(float) d
{
CGSize winSize = [CCDirector sharedDirector].winSize;
int rndY;

rndY = arc4random() % ((int) winSize.height - 200) + 110;
NSLog(@"winSize.height = %f\n", winSize.height);

life = l;

_teddyBearSprite = [CCSprite spriteWithSpriteFrameName:spriteFrameName];

_teddyBearSprite.position = ccp(arc4random() % (int) winSize.width, rndY);

_walkAnim = [CCAnimation animationWithFrames:animFrames delay:d];
self.walkAction = [CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:_walkAnim restoreOriginalFrame:NO]];
[_teddyBearSprite runAction:_walkAction];
}

@end



// HelloWorldLayer.h
#import "cocos2d.h"

// HelloWorldLayer.h
@interface HelloWorldLayer : CCLayer
{
/*CCSprite *_teddyBearSprite;
CCAction *_walkAction;
CCAction *_moveAction;

CCArray *_teddyBears;
int _nextTeddyBear;
double _nextTeddyBearSpawn;*/

NSMutableArray *_teddyBears;
}

//@property (nonatomic, retain) CCSprite *teddyBearSprite;
//@property (nonatomic, retain) CCAction *walkAction;
//@property (nonatomic, retain) CCAction *moveAction;
//@property (nonatomic, assign) NSMutableArray *teddyBears;

// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;

@end



// HelloWorldLayer.m
#import "HelloWorldLayer.h"
#import "TeddyBear.h"

#define kNumTeddyBears 15

[…]

- (id) init
{    
if((self = [super init]))
{
self.isTouchEnabled = YES;

/* Inizio animazione teddyBear. */
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"AnimTeddyBear.plist"];

CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"AnimTeddyBear.png"];
[self addChild:spriteSheet];

NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 4; ++i)
{
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"teddybear%d.png", i]]];

_teddyBears = [NSMutableArray array];

TeddyBear *tmpTeddyBear = [TeddyBear new];

//[tmpTeddyBear initTeddyBear:1 withAnimFrames:walkAnimFrames withSpriteFrameName:@"teddybear1.png" withDelay:0.09f];

for(int i = 0; i < kNumTeddyBears; i++)
{
[tmpTeddyBear initTeddyBear:1 withAnimFrames:walkAnimFrames withSpriteFrameName:@"teddybear1.png" withDelay:0.09f];
[spriteSheet addChild:[tmpTeddyBear teddyBearSprite]];
[_teddyBears addObject:tmpTeddyBear];
}

NSLog(@"Grandezza _teddyBears: %d in init()\n", [_teddyBears count]);
int i = 0;
for(TeddyBear *tb in _teddyBears)
{
NSLog(@"i = %d\n", i);
    }
}

return self;
}

[…]

-(void) ccTouchEnded:(UITouch *) touch withEvent:(UIEvent *) event {
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
NSLog(@"Grandezza _teddyBears: %d in ccTouchEnded\n", [_teddyBears count]);
}

[…]
[/CODE]

L'applicazione va in crash nell'ultima riga del metodo -(void) ccTouchEnded… durante la stampa quando tenta di accedere a [_teddyBears count], è come se l'array non esistesse più dopo essere uscito dal metodo init().
aaa
08/05/12 16:04
_mikele_
Come non detto, ho risolto usando un CCArray al posto di un NSMutableArray :D
aaa
08/05/12 17:20
pierotofy
Ti consiglio di studiare un po' piu' a fondo il meccanismo di gestione della memoria dell'Objective-C. E' un po' contorto, ma e' fondamentale capire come funziona.

Quando inizializzi il tuo array in init:

 _teddyBears = [NSMutableArray array];


Il reference count per l'oggetto e' a uno. Quando esci da init, il reference count viene decrementato (quindi 1-1 = 0), l'oggetto viene rilasciato e il tuo puntatore fa ora riferimento ad uno spazio di memoria deallocato.

Per risolvere:

 _teddyBears = [[NSMutableArray array] retain];


Retain aumentera' il reference count a 2. Quando init esce, il reference count sara' ad 1 (2 - 1 = 1). Poi quando hai finito di usare l'array (nel distruttore della scena?) ricordati di chiamare:

[_teddyBears release];


Che decrementera' il reference count di 1, raggiungendo 0 e quindi deallocando lo spazio.
Il mio blog: piero.dev
10/05/12 17:04
_mikele_
Grazie, come al solito le tue risposte sono brillanti :D
Sì in effetti non l'ho studiata per niente la gestione della memoria, vedrò di rimediare ;)
aaa
12/05/12 13:54
_mikele_
Ok ho capito come funzionano retain e release ma se io avessi una variabile primitiva come dovrei fare? Ho per esempio un float che viene inizializzato nel metodo init(), poi vorrei modificarlo in update() ma non viene modificato e rimane sempre dello stesso valore, ho provato in vari modi e ho cercato su internet ma non ho trovato niente :(
aaa
12/05/12 15:35
pierotofy
?

I primitivi non hanno bisogno di essere allocati/deallocati. Release, retain & co sono solo per gli oggetti.
Il mio blog: piero.dev
13/05/12 13:16
_mikele_
Sì lo so infatti ho sempre fatto così anche negli altri linguaggi però adesso ho una variabile float (float time) dichiarata nell'interfaccia HelloWorldLayer, in init() faccio time = CFAbsoluteTimeGetCurrent(); e successivamente in update() che viene chiamata ogni non so quanti ms rifaccio time = CFAbsoluteTimeGetCurrent(); ma il valore di time non cambia mai e rimane sempre lo stesso di quando l'ho inizializzata in init(). In poche parole è come se in update non venisse assegnato nessun valore a time pur dopo aver fatto l'assegnazione, è strana come cosa non riesco a spiegarmela...
aaa
15/05/12 10:25
_mikele_
Risolto, ho usato CACurrentMediaTime() al posto di CFAbsoluteTimeGetCurrent() :)
aaa