当你用CCDirector replaceScene方法替换场景时, 每个节点都会调用CCNode所带的三个方法。
这三个方法是:onEnter, onEnterTransitionDidFinish和onExit。
取决于是否使用了CCTransitionScene, onEnter和onExit会在场景转换过程中的某个时间点被调用。对于这三个方法, 你必须调用它们的super方法以避免触摸输入问题和内存泄漏的问题。
-(void) onEnter
{
// 节点调用init方法以后将会调用此方法
// 如果使用了CCTransitionScene,将会在过渡效果开始以后调用此方法
[super onEnter];
}
-(void) onEnterTransitionDidFinish
{
// 调用onEnter以后将会调用此方法
// 如果使用了CCTransitionScene,将会在过渡效果结束以后调用此方法
[super onEnterTransitionDidFinish];
}
-(void) onExit
{
// 节点调用dealloc方法之前将会调用此方法
// 如果使用了CCTransitionScene,将会在过渡效果结束以后调用此方法
[super onExit];
}
如果你不在onEnter方法里调用它的super方法的话,你的新场景可能不会对触摸或者加速计的输入有任何反应。
如果你不在onExit方法里调用它的super方法,当前场景可能不会从内存里释放。
你可以在场景转换之前或者之后,通过使用上述方法在节点中完成一些特定的 操作。因为在程序进入onEnter方法的时候,场景中的所有节点都已经设置完成 了;同时,在onExit方法中,所有节点都还存在于内存中。
1. scene: OtherScene
2. init:
3. onEnter:
4. // ......
5. onExit:
6. onEnterTransitionDidFinish:
7. dealloc:
replaceScene -> Other Scene.
===========================================
scene: OtherScene
init: <OtherScene = 0x1835e10 | Tag = -1>
onExit: <FirstScene = 0x436170 | Tag = -1> // 因为切换到OhterScene时,FirstScene还在内存中,过程是先初始化OhterScene,之后从内存中清空FirstScene
dealloc: <FirstScene = 0x436170 | Tag = -1>
onEnter: <OtherScene = 0x1835e10 | Tag = -1>
onEnterTransitionDidFinish: <OtherScene = 0x1835e10 | Tag = -1>
#import <Foundation/Foundation.h>
#import "cocos2d.h"
typedefenum
{
TargetSceneINVALID = 0,
TargetSceneFirstScene,
TargetSceneOtherScene,
TargetSceneMAX,
}
TargetScenes;
@interface LoadingScene : CCScene
{
TargetScenes targetScene_;
}
+(id)sceneWithTargetScene:(TargetScenes)targetScene;
-(id)initWithTargetScene:(TargetScenes)targetScene;
@end
#import "LoadingScene.h"
#import "FirstScene.h"
#import "OtherScene.h"
@implementation LoadingScene
+(id)sceneWithTargetScene:(TargetScenes)targetScene
{
return [[[self alloc] initWithTargetScene:targetScene] autorelease];
}
-(id)initWithTargetScene:(TargetScenes)targetScene
{
if((self = [super init]))
{
targetScene_ = targetScene;
CCLabelTTF *label = [CCLabelTTFlabelWithString:@"Loading..."
fontName:@"Marker Felt"
fontSize:64];
CGSize size = [[CCDirectorsharedDirector] winSize];
label.position = CGPointMake(size.width*0.5, size.height*0.5);
[self addChild:label];
[selfscheduleUpdate];
}
returnself;
}
-(void)update:(ccTime)delta
{
[selfunscheduleAllSelectors];
switch (targetScene_) {
caseTargetSceneFirstScene:
[[CCDirectorsharedDirector] replaceScene:[FirstScenescene]];
break;
caseTargetSceneOtherScene:
[[CCDirectorsharedDirector] replaceScene:[OtherScenescene]];
break;
default:
NSAssert2(nil, @"%@:unsupported TargetScene %i", NSStringFromSelector(_cmd), targetScene_);
break;
}
}
@end
因为 LoadingScene 继承自 CCScene,并且要求传递一个新的参数给它,所以仅 仅调用[CCScene node]是不够的。SceneWithTargetScene 方法首先会完成对 self 的内存分配,然后调用 initWithTargetScene 方法,最后返回一个新的自 动释放对象。
这里的 init 方法把目标场景储存在一个成员变量里面,接着生成一个 “Loading...”标签,最后运行 scheduleUpdate 这个预约方法。
为什么我们不在init方法里直接使用replaceScene方法呢?这里有两条规则需要遵守。规则一:永远不要在一个节点的init方法中调用CCDirector的replaceScene方法。
规则二:请遵守规则一。不遵守规则的后果是程序崩溃。Director无法容忍一个节点在初始化的同时进行场景替换。
在过渡效果中使用LoadingScene可以优化内存的使用:因为你使用了一个简单 的过渡场景用于替换当前场景,然后用最终的目标场景替换这个过渡场景。在 这个替换的过程中,cocos2d将会有足够的时间来释放之前场景所占用的内存。 我们得到的实际效果是:不再会有两个复杂场景同时占用着内存的情况了,因 此在场景转换过程中也就减少了出现内存使用高峰的机会。
1 #import <Foundation/Foundation.h> 2 #import "cocos2d.h" 3 #import "GameLayer.h" 4 #import "UserInterfaceLayer.h" 5 6 typedef enum // 定义二个层的Tag值 7 { 8 LayerTagGameLayer, // 用于标示[游戏层] 9 LayerTagUILayer, // 用于标示[用户设置层] 10 } MultiLayerSceneTags; 11 12 typedef enum // 定义动作Tag 13 { 14 ActionTagGameLayerMovesBack, 15 ActionTagGameLayerRotates, 16 } MultiLayerSceneActionTags; 17 18 @interface MultiLayerScene : CCLayer 19 { 20 bool isToucheForUserInterface; // 判断用户点击的是否为设置界面 21 } 22 @property (readonly) GameLayer *gameLayer; 23 @property (readonly) UserInterfaceLayer *uiLayer; 24 25 +(MultiLayerScene *)sharedLayer; 26 +(CGPoint)locationFromTouch:(UITouch *)touch; 27 +(CGPoint)locationFromTouches:(UITouch *)touches; 28 +(id)scene; 29 30 @end
1 #import "MultiLayerScene.h" 2 3 @implementation MultiLayerScene 4 5 static MultiLayerScene* multiLayerSceneInstance; // 定义静态的本场景变量 6 7 +(MultiLayerScene *)sharedLayer // 返回静态场景变量 8 { 9 NSAssert(multiLayerSceneInstance != nil, @"MultiLayerScene not available!"); 10 return multiLayerSceneInstance; 11 } 12 13 #pragma mark 返回游戏层 14 - (GameLayer *)gameLayer 15 { 16 CCNode *layer = [self getChildByTag:LayerTagGameLayer]; 17 NSAssert([layer isKindOfClass:[GameLayer class]], @"%@: not a GameLayer!",NSStringFromSelector(_cmd)); 18 return (GameLayer *)layer; 19 } 20 21 #pragma mark 返回用户设置层 22 - (UserInterfaceLayer*)uiLayer 23 { 24 CCNode* layer = [[MultiLayerScene sharedLayer] getChildByTag:LayerTagUILayer]; 25 NSAssert([layer isKindOfClass:[UserInterfaceLayer class]], @"%@: not a UserInterfaceLayer!", NSStringFromSelector(_cmd)); 26 return (UserInterfaceLayer *)layer; 27 } 28 29 #pragma mark 得到用户点击的坐标并转换为OpenGL坐标 30 +(CGPoint)locationFromTouch:(UITouch *)touch 31 { 32 // locationInView:函数返回一个CGPoint类型的值,表示触摸在view这个视图上的位置,这里返回的位置是针对view的坐标系的。 33 // 调用时传入的view参数为空的话,返回的时触摸点在整个窗口的位置。 34 CGPoint touchLocation = [touch locationInView:[touch view]]; 35 36 // convertToGL:将ios坐标系转换为OpenGL坐标系,返回类型 CGPoint 37 return [[CCDirector sharedDirector] convertToGL:touchLocation]; 38 } 39 40 #pragma mark 如果用户是多点触摸,那么返回其中任意一个触摸点的OpenGL坐标 41 +(CGPoint) locationFromTouches:(NSSet *)touches 42 { 43 return [self locationFromTouch:[touches anyObject]]; 44 } 45 46 #pragma mark - 静态构造方法Scene 47 +(id) scene 48 { 49 CCScene* scene = [CCScene node]; 50 MultiLayerScene* layer = [MultiLayerScene node]; 51 [scene addChild:layer]; 52 return scene; 53 } 54 55 #pragma mark 初始化方法init 56 -(id) init 57 { 58 if ((self = [super init])) 59 { 60 NSAssert(multiLayerSceneInstance == nil, @"another MultiLayerScene is already in use!"); 61 multiLayerSceneInstance = self; 62 63 GameLayer* gameLayer = [GameLayer node]; 64 [self addChild:gameLayer z:1 tag:LayerTagGameLayer]; // 将游戏层加入场景中,Z轴位置为 1, 65 // z:是指添加的ZOrder值,ZOrder是指该成员的层级(也可以说深度),z值大的成员在z值小的成员的上面; 66 67 UserInterfaceLayer* uiLayer = [UserInterfaceLayer node]; // 将用户设置层加入场景中,Z轴位置为 2 68 [self addChild:uiLayer z:2 tag:LayerTagUILayer]; 69 } 70 return self; 71 } 72 73 -(void) dealloc 74 { 75 CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self); 76 multiLayerSceneInstance = nil; 77 [super dealloc]; 78 } 79 80 @end
1 #import <Foundation/Foundation.h> 2 #import "cocos2d.h" 3 4 #pragma mark 游戏内容层 5 @interface GameLayer : CCLayer 6 { 7 CGPoint gameLayerPosition; 8 CGPoint lastTouchLocation; 9 } 10 11 @end
1 #import "GameLayer.h" 2 #import "cocos2d.h" 3 #import "MultiLayerScene.h" 4 5 @interface GameLayer (PrivateMethod) 6 -(void)addRandomThings; 7 @end 8 9 @implementation GameLayer 10 -(id)init 11 { 12 if((self = [super init])) 13 { 14 gameLayerPosition = self.position; // 返回层的默认坐标(0,0) 15 CGSize screenSize = [[CCDirector sharedDirector] winSize]; 16 CCSprite *background = [CCSprite spriteWithFile:@"grass.png"]; // 载入“小草”精灵 17 background.position = CGPointMake(screenSize.width * 0.5, screenSize.height * 0.5); // 设置小草精灵的位置 18 [self addChild:background]; // 将小草加入游戏层中 19 20 CCLabelTTF *label = [CCLabelTTF labelWithString:@"GameLayer" 21 fontName:@"Marker Felt" 22 fontSize:44]; 23 label.color = ccBLACK; 24 label.position = CGPointMake(screenSize.width*0.5, screenSize.height * 0.5); 25 //label.anchorPoint = CGPointMake(0.5f, 0.5f); 26 [self addChild:label]; // 将文字标签加入游戏层 27 28 [self addRandomThings]; 29 self.isTouchEnabled = YES; // 支持触屏 30 } 31 return self; 32 } 33 34 #pragma mark 在游戏层添加一些随机物品 35 -(void)addRandomThings 36 { 37 CGSize screenSize = [[CCDirector sharedDirector] winSize]; 38 39 for (int i = 0; i<4; i++) // 添加4团随机的火元素 40 { 41 CCSprite *firething = [CCSprite spriteWithFile:@"firething.png"]; 42 firething.position = CGPointMake(CCRANDOM_0_1()*screenSize.width, CCRANDOM_0_1()*screenSize.height); 43 [self addChild:firething]; // 将火元素添加到游戏层中 44 [self runRandomMoveSequence:firething]; // 随机移动此火元素 45 } 46 47 for (int i = 0; i<10; i++) // 添加10个随机的蜘蛛 48 { 49 CCSprite *spider = [CCSprite spriteWithFile:@"spider.png"]; 50 spider.position = CGPointMake(CCRANDOM_0_1()*screenSize.width, CCRANDOM_0_1()*screenSize.height); 51 [self addChild:spider]; 52 [self runRandomMoveSequence:spider]; // 随机移动此火元素 53 } 54 } 55 -(void) runRandomMoveSequence:(CCNode*)node 56 { 57 float duration = CCRANDOM_0_1() * 5 + 1; 58 59 CCMoveBy *move1 = [CCMoveBy actionWithDuration:duration position:CGPointMake(-180, 0)]; // 在现在位置水平左移180个单位 60 CCMoveBy* move2 = [CCMoveBy actionWithDuration:duration position:CGPointMake(0, -180)]; 61 CCMoveBy* move3 = [CCMoveBy actionWithDuration:duration position:CGPointMake(180, 0)]; 62 CCMoveBy* move4 = [CCMoveBy actionWithDuration:duration position:CGPointMake(0, 180)]; 63 CCSequence *sequence = [CCSequence actions:move1,move2,move3,move4, nil]; // 定义一个动作执行序列 64 CCRepeatForever *repeat = [CCRepeatForever actionWithAction:sequence]; // 该类的作用就是无限期执行某个动作或动作序列,直到被停止 65 [node runAction:repeat]; 66 } 67 68 #pragma mark 在CCLayer中经常要注册Touch Dispatcher来让Layer处理Touch事件。 Dispatcher:调度员 69 -(void)registerWithTouchDispatcher 70 { 71 // 设置此层可以接受手触发事件,且priority(优先级)为0,越小优先级越高,越先接受用户触摸事件,swallow(是否可以吞没事件)为YES 72 [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self 73 priority:0 74 swallowsTouches:YES]; 75 } 76 77 #pragma mark 接受用户触摸事件 78 -(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 79 { 80 CCLOG(@"GameLayer: ccTouchBegan"); 81 lastTouchLocation = [MultiLayerScene locationFromTouch:touch]; // 得到用户触摸开始点OpenGL坐标 82 [self stopActionByTag:ActionTagGameLayerMovesBack]; // 停止此时可能存在加速动作 83 return YES; // 如果是NO,那么就结束触摸事件传递,ccTouchMoved、ccTouchEnded将不执行 84 85 } 86 87 #pragma mark 接受用户滑动事件 88 -(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event 89 { 90 CCLOG(@"GameLayer: ccTouchMoved"); 91 CGPoint currentTouchLocaion = [MultiLayerScene locationFromTouch:touch]; // 在移动的过程中得到一系列新的目标点位 92 CGPoint moveTo = ccpSub(lastTouchLocation, currentTouchLocaion); // 得到二个点相减后的位置 93 moveTo = ccpMult(moveTo, -1); // 将moveTo的X,Y 都与-1相乘,得到原点将移动到新点位的值 94 lastTouchLocation = currentTouchLocaion; // 将现在点位更新为已经使用过的点位 95 self.position = ccpAdd(self.position, moveTo); // 将本层原点向moveTo点位移动 96 } 97 98 #pragma mark 用户触摸事件结束后,本层内容恢复到原点 99 -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event 100 { 101 CCLOG(@"GameLayer: ccTouchEnded"); 102 CCMoveTo *move = [CCMoveTo actionWithDuration:1 position:gameLayerPosition]; // 返回原点 103 CCEaseIn *ease = [CCEaseIn actionWithAction:move rate:0.5f]; // 在0.5秒内完成move动作,且开始运行时是加速状态的 104 ease.tag = ActionTagGameLayerMovesBack; // 此加速动作的Tag = ActionTagGameLayerMovesBack 105 [self runAction:ease]; 106 } 107 @end
1 #import <Foundation/Foundation.h> 2 #import "cocos2d.h" 3 #pragma mark 用户设置层 4 5 typedef enum 6 { 7 UIlayerTagFrameSprite, 8 } UserInterfaceLayerTags; 9 10 @interface UserInterfaceLayer : CCLayer { 11 12 } 13 -(BOOL) isTouchMe:(CGPoint)touchLocation; 14 @end
1 #import "UserInterfaceLayer.h" 2 #import "MultiLayerScene.h" 3 4 @implementation UserInterfaceLayer 5 -(id)init 6 { 7 if((self = [super init])) 8 { 9 CGSize screenSize = [[CCDirector sharedDirector] winSize]; 10 CCSprite *uiframe = [CCSprite spriteWithFile:@"ui-frame.png"]; 11 uiframe.position = CGPointMake(0,screenSize.height); 12 uiframe.anchorPoint = CGPointMake(0, 1); 13 [self addChild:uiframe z:0 tag:UIlayerTagFrameSprite]; // 设置图片Tag = UIlayerTagFrameSprite 14 15 CCLabelTTF *label = [CCLabelTTF labelWithString:@"这里可以显示程序积分" fontName:@"Courier" fontSize:22]; 16 label.color = ccBLACK; 17 label.position = CGPointMake(screenSize.width * 0.5,screenSize.height); 18 label.anchorPoint = CGPointMake(0.5f, 1); 19 [self addChild:label]; 20 self.isTouchEnabled = YES; 21 } 22 return self; 23 } 24 25 -(void) registerWithTouchDispatcher 26 { 27 [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES]; 28 } 29 -(BOOL)isTouchMe:(CGPoint)touchLocation 30 { 31 CCNode *node = [self getChildByTag:UIlayerTagFrameSprite]; // 查找到tag = UIlayerTagFrameSprite 的精灵对象 32 return CGRectContainsPoint([node boundingBox], touchLocation); // 判断用户触摸点是否在node之中 33 } 34 35 #pragma mark 当用户触摸标题栏时,gameLayer进行一系列操作 36 -(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 37 { 38 CGPoint location = [MultiLayerScene locationFromTouch:touch]; // 得到用户点击的OpenGL坐标 39 bool isTouchHandled = [self isTouchMe:location]; // 判断用户点击的是否为UserInteface层 40 if(isTouchHandled) // 如果 YES,那么事件将被吞没不向下层传递了
41 { 42 CCNode *node = [self getChildByTag:UIlayerTagFrameSprite]; 43 NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite"); 44 ((CCSprite *)node).color = ccRED; // 将UserInterface层显示的字体改为红色 45 46 CCScaleTo *scaleDown = [CCScaleTo actionWithDuration:2 scale:0]; // 在2秒内缩放为0,消失了 47 CCScaleTo *scaleUp = [CCScaleTo actionWithDuration:2 scale:1]; // 在2秒内还原为原来大小 48 CCSequence *sequence = [CCSequence actions:scaleDown,scaleUp, nil]; // 定义包括二种缩放方式的动作序列 49 sequence.tag = ActionTagGameLayerRotates; // 将此序列的Tag = ActionTagGameLayerRotates 50 51 GameLayer *gameLayer = [MultiLayerScene sharedLayer].gameLayer; // 得到gameLayer层 52 CCRotateBy *rotate = [CCRotateBy actionWithDuration:4 angle:360]; // 在4秒内旋转360度 53 [gameLayer stopActionByTag:ActionTagGameLayerRotates]; // 停止可能存在的缩放序列 54 [gameLayer setRotation:0]; // 初始化gameLayer层旋转角度为0 55 [gameLayer setScale:1]; // 初始化gameLayer层缩放比例为1倍,既原始大小 56 [gameLayer runAction:rotate]; // 执行旋转 57 [gameLayer runAction:sequence]; // 执行缩放 58 } 59 return isTouchHandled; 60 } 61 62 #pragma mark 用户触摸结束后恢复标题字体颜色 63 -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event 64 { 65 CCNode *node = [self getChildByTag:UIlayerTagFrameSprite]; 66 NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite"); 67 ((CCSprite *)node).color = ccWHITE; 68 } 69 70 -(void)dealloc 71 { 72 CCLOG(@"%@: %@",NSStringFromSelector(_cmd),self); 73 [super dealloc]; 74 } 75 @end
1 #import <Foundation/Foundation.h> 2 #import "cocos2d.h" 3 4 // CCTargetedTouchDelegate,CCStandardTouchDelegate 协议将使对象支持触摸事件 5 6 @interface Spider : NSObject <CCTargetedTouchDelegate> 7 { 8 CCSprite *spiderSprite; // 蜘蛛对象中有一个精灵对象 9 int numUpdates; 10 } 11 12 +(id)spiderWithParentNode:(CCNode *)parentNode; 13 -(id)initWithParentNode:(CCNode *)parentNode; 14 @end
1 #import "Spider.h" 2 #import "MultiLayerScene.h" 3 4 @implementation Spider 5 6 #pragma mark 一个像 CCNode类一样的静态自动释放的初始化方法。这是在模仿 cocos2d 的内存管理方式。 7 +(id)spiderWithParentNode:(CCNode *)parentNode 8 { 9 return [[[self alloc] initWithParentNode:parentNode] autorelease]; 10 } 11 12 -(id)initWithParentNode:(CCNode *)parentNode 13 { 14 if((self = [super init])) 15 { 16 CGSize screenSize = [[CCDirector sharedDirector] winSize]; 17 spiderSprite = [CCSprite spriteWithFile:@"spider.png"]; 18 spiderSprite.position = CGPointMake(CCRANDOM_0_1()*screenSize.width,CCRANDOM_0_1()*screenSize.height); 19 [parentNode addChild:spiderSprite]; // 将预定好随机位置的蜘蛛加入到制定的节点对象(CCNode *)中 20 21 // 自定义使用CCSchedule类手动预约更新方法 22 [[[CCDirector sharedDirector] scheduler] scheduleUpdateForTarget:self 23 priority:0 24 paused:NO]; 25 // 让这个类可以接收定向的触摸事件 26 [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self 27 priority:-1 swallowsTouches:YES]; 28 } 29 return self; 30 } 31 32 -(void)update:(ccTime)delta 33 { 34 numUpdates ++; 35 /*if(numUpdates > 50) 36 { 37 numUpdates = 0; 38 [spiderSprite stopAllActions]; 39 CGPoint moveTo = CGPointMake(CCRANDOM_0_1()*200-100,CCRANDOM_0_1()*100 - 50); 40 CCMoveBy *move = [CCMoveBy actionWithDuration:1 position:moveTo]; 41 [spiderSprite runAction:move]; 42 }*/ 43 if(numUpdates > 50) 44 { 45 numUpdates = 0; 46 CGPoint moveTo = CGPointMake(CCRANDOM_0_1()*200-100,CCRANDOM_0_1()*100 - 50); 47 [self moveAway:2 postion:moveTo]; 48 } 49 } 50 51 -(void)moveAway:(float)duration postion:(CGPoint)moveTo 52 { 53 [spiderSprite stopAllActions]; 54 CCMoveBy *move = [CCMoveBy actionWithDuration:duration position:moveTo]; 55 [spiderSprite runAction:move]; 56 } 57 58 - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 59 { 60 // CCLOG(@"Spride ccTouchBegan"); 61 CGPoint touchLocaion = [MultiLayerScene locationFromTouch:touch]; 62 BOOL isTouchHandled = CGRectContainsPoint([spiderSprite boundingBox], touchLocaion); 63 if(isTouchHandled) 64 { 65 numUpdates = 0; 66 CGPoint moveTo; 67 float moveDistance = 60; 68 float rand = CCRANDOM_0_1(); 69 if(rand < 0.25f) 70 { 71 moveTo = CGPointMake(moveDistance, moveDistance); 72 } 73 else if (rand < 0.5f) 74 { 75 moveTo = CGPointMake(-moveDistance, moveDistance); 76 } 77 else if (rand <0.75f) 78 { 79 moveTo = CGPointMake(moveDistance, -moveDistance); 80 } 81 else 82 { 83 moveTo = CGPointMake(-moveDistance, -moveDistance); 84 } 85 [self moveAway:0.1f postion:moveTo]; 86 } 87 return isTouchHandled; 88 } 89 90 -(void)dealloc 91 { 92 // scheduler要求在dealloc方法中手动解除预约的更新方法 93 [[[CCDirector sharedDirector] scheduler] unscheduleUpdateForTarget:self]; 94 95 // CCTouchDispatcher(触摸调度程序) 也需要在dealloc方法中被移除。 96 [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self]; 97 [super dealloc]; 98 }