2014年5月3日 星期六

CCAction資料


轉帖自http://www.apkbus.com/android-797-1.html


CCAction是游戏的一个和重要组成部分,游戏之所以区别于应用就是因为形象鲜明,趣味性交互性强,如果游戏元素没有动作,那就很无聊了;
        之前我们已经介绍过,CCNode节点中有一个(void)runAction:(CCAction *)action 方法,所以,所有继承自CCNode的类,比如精灵,层,甚至菜单都可以执行动作;
        CCAction有很庞大的层次关系,如图所示

        其中主要的为CCAction-->CCFiniteTimeAction
        其中CCFiniteTimeAction下的两个子类CCActionInstant和CCActionInterval分别为瞬时动作和延时动作;
瞬时动作更接近于节点属性的设置,有的时候是为了更方便地组成动作序列,延时动作则是我们经常看到的一些动画画面;
        动作的返回值类型为id,所以,你会经常看到这种写法
        CCMoveTo *move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100,200)];
        [sprite runAction :move];
        瞬时动作我们会在介绍动作序列时再作详细说明,下面归纳一下延时动作,常用的延时动作有
CCMoveTo 
        移动到参数position位置
        示例:CCMoveTo *move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100,200)];//该动作移动到position参数位置;

CCMoveBy
        精灵移动参数个位置
        示例:[sprite runAction:[CCMoveBy actionWithDuration:3 position:pos]];//该动作使节点移动pos个位置;

CCFadeTo
        节点色调透明度变化
        示例: [sprite runAction:[CCFadeTo actionWithDuration:3 opacity:opa]]; //节点透明度渐变为opa(0~255);

CCFadeIn
        节点亮度从0渐变为最高
        示例: [sprite runAction:[CCFadeIn actionWithDuration:3 ]; //节点在3秒内从黑暗变为最亮;

CCFadeOut
        同上相反;

CCBlink
        节点闪烁,需设置闪烁次数;
        示例:[sprite runAction:[CCBlink actionWithDuration:3 blinks:t]]; //节点在3秒内闪烁t次;如果持续时间变长,闪烁频率会自动调低;

CCRotateTo
        节点旋转,需设置旋转角度
        示例:[sprite runAction:[CCRotateTo actionWithDuration:3 angle:180]]; //节点在3秒内旋转180度;

CCRotateBy
        节点旋转,经试验没发现与CCRotateTo有何区别;
        示例:[sprite runAction:[CCRotateBy actionWithDuration:3 angle:180]];      //效果同上一样;

CCScaleTo
        节点放大,有两个方法
        示例:[sprite runAction:[CCScaleTo actionWithDuration:3 scale :2]];//节点在3秒内放大到原来的2倍;
 [sprite runAction:[CCScaleTo actionWithDuration:3 scaleX:2 scaleY:3]];//节点在3秒内横方向放大到原来的2倍,纵方向放大到原来的3倍;

CCScaleBy 
        节点放大,经试验没发现与上述效果有何区别(大家发现后告诉我);

CCTintTo
        节点调色
        示例:[sprite runAction:[CCTintTo actionWithDuration:3 red:110 green:110 blue:110]]; //节点在三秒内着色为RGB颜色如参数所示

CCTintBy
        节点渐变
        示例:[sprite runAction:[CCTintBy actionWithDuration:3 red:110 green:110 blue:110]]; //节点在三秒内以RGB参数逐渐加深;

CCSkewTo
        节点斜率变化
        示例:[sprite runAction:[CCSkewTo actionWithDuration:3 skewX:30 skewY:5]];//将一个节点按横坐标30,纵坐标5的大小倾斜;该参数效果见图


CCSkewBy
        效果同上一样;

CCBezierTo
        使节点按贝塞尔曲线移动,需先生成一个贝塞尔参数,见示例
        示例:
ccBezierConfig bezier;//创建贝塞尔曲线
        bezier.controlPoint_1 =ccp(sprite.position.x, sprite.position.y);//起始点
        bezier.controlPoint_2 =ccp(sprite.position.x+5,sprite.position.y+20);//控制点
        bezier.endPosition =ccp(sprite.position.x+50, sprite.position.y+100);//结束位置
        [sprite runAction:[CCBezierToactionWithDuration:3bezier:bezier]];
关于贝塞尔曲线,主要作用是通过点确定准确的曲线图形,建议大家百度一下点击打开链接

CCBezierBy
        每次移动贝塞尔曲线个距离;用法同上;

        通过上面这些主流的动作行为,我们可以实现一些很有视觉冲击力的动画效果,常用到的常常有放大(远近景拉伸),旋转,跳越等;
        cocos2d中的动作远比上面这些要灵活的多,下回就介绍一下动作的一些修饰,动作序列,和动作序列中怎样加入一些回调方法;

cocos2d 顏色設定



[mysprite setColor:ccc3(R,G,B)] // R,G,B -> 分別為0~255

[mysprite setOpacity:value]; // value 范围0-255,    0完全透明,255完全不透明。

其它可參考基本設定 http://kirenenko-tw.blogspot.tw/2013/11/ccsprite.html

至於CCSprite的饱和度、色相、亮度等属性可參考
http://blog.csdn.net/ldz15838245189/article/details/9365499


另外也可以利用CCAction來做顏色的調控

CCFadeTo
        节点色调透明度变化
        示例: [sprite runAction:[CCFadeTo actionWithDuration:3 opacity:opa]]; //节点透明度渐变为opa(0~255);

CCFadeIn
        节点亮度从0渐变为最高
        示例: [sprite runAction:[CCFadeIn actionWithDuration:3 ]; //节点在3秒内从黑暗变为最亮;

CCFadeOut
        同上相反;

CCBlink
        节点闪烁,需设置闪烁次数;
        示例:[sprite runAction:[CCBlink actionWithDuration:3 blinks:t]]; //节点在3秒内闪烁t次;如果持续时间变长,闪烁频率会自动调低;

CCTintTo
        节点调色
        示例:[sprite runAction:[CCTintTo actionWithDuration:3 red:110 green:110 blue:110]]; //节点在三秒内着色为RGB颜色如参数所示

CCTintBy
        节点渐变
        示例:[sprite runAction:[CCTintBy actionWithDuration:3 red:110 green:110 blue:110]]; //节点在三秒内以RGB参数逐渐加深;






2014年5月1日 星期四

EXC_BAD_ACCESS 问题根源的方法

code=1,是已经释放的对象又进行释放;code=2,是对已经释放完的,即计数为零的对象又进行使用

參考網址
 http://blog.csdn.net/xml_eason/article/details/7951076



要抓取 EXC_BAD_ACCESS
使用
NSZombieEnabled

啟動方式
在
 Menubar > Product > Scheme > Edit Scheme)




 

2014年4月8日 星期二

xcode自我判定錯誤,所產生的自我迴圈。

1. xxx.h設定一個變數

@property (nonatomic) BOOL deleteMP;




2. xxx.m 不小心寫了一個 包含這個變數的呼叫函數,此處xcode會自行判定為設定呼叫
- (void) setDeleteMP:(int)winPoints
{
    if (self.replaceMP!= YES && self.isFrame == NO) {
        self.deleteMP = YES;
        self.winPoints = winPoints;
}




3. 然後在其他某處使用了

xxx.deleteMP = YES;



4. 於是形成了一個自我迴圈,不斷呼叫 setDeleteMP。
很有趣的一個錯誤產生,居然是xcode的自我判定錯誤,而這個錯誤雖然很容易抓,還是寫出來當作以後的警戒。

2014年3月28日 星期五

CCSpriteBatchNode使用之注意事項

1. 改變zorder時必須要用
     
[myBatchNode reorderChild:mySprite z:indexOfAnArray];
 
不可使用  [mySprite setZOrder:indexOfAnArray];
 
其中 indexOfAnArray 就用z-order的數值來設定

2014年3月24日 星期一

cocos2d 中添加显示文字的三种方式(CCLabelTTF 、CCLabelBMFont 和CCLabelAtlas)(轉貼)

cocos2d 中添加显示文字的三种方式(CCLabelTTF 、CCLabelBMFont 和CCLabelAtlas)

官方說明

   
最近在逛论坛的时候看到有的帖子说显示文本时用 CCLabelTTF 不是很好,且推荐游戏中最好别用。于是网上查了查相关资料,整理了下 ,顺便也分享下
     在 cocos2d 中有三个类可以在层或精灵中添加文字:
  • CCLabelTTF
  • CCLabelBMFont
  • CCLabelAtlas

     CCLabelTTF
           CCLabelTTF 每次调用 setString (即改变文字)的时候,一个新的OPENGL 纹理将会被创建.。这意味着setString 和创建一个新的标签一样慢。
           所以,当你需要频繁的更新它们的时候,尽可能的不用去使用标签对象。  而应该使用CCLabelAtlas或者是CCLabelBMFont。
           OK, 看下它的使用方法
           CCLabelTTF *label = [CCLabelTTF labelWithString:@"the string"  fontName:@"Marker Felt"  fontSize:21];
           [layer addChild:label];
           label.position = ccp(100,100);
           [label setString:@“change string”]; //修改文字的方法

           这个类使用的是系统中的字体,不需要额外往项目文件中添加字体文件。但如果你有自定义的字体也可以加到项目中,如果无法使用,可以查看一下ccConfig.h中的CC_FONT_LABEL_SUPPORT是否enable了。


     CCLabelBMFont
        CCLabelBMFont  相当于每次改变只改变了图片坐标,而CCLabelTTF要重新渲染.这个类使用之前,需要添加好字体文件,包括一个图片文件 (**.png) 和一个 字体坐标文件 (**.fnt)。
         在 cocos2d的示例项目中有现成的,可以先拿过来练习一下,找的时候注意两个文件的 名称是相同的,只是扩展名不同。
         CCLabelBMFont *label =[CCLabelBMFont labelWithString:@"the string"   fntFile:@"konqa32-hd.fnt"];
           [layer addChild:label];
           label.position = ccp(100,100);
           [label setString:@“change string”];
        fntFile是文件名,写扩展名是 .fnt的那个。下面是 .fnt文件的截图
       
        可以看到 .fnt 文件定义了 图像文件的名称,以及每个字符对应的位置信息。
        这个没办法指定字体的字号,但可以用  scale  属性进行缩放来调整大小。就当它是sprite。



   CCLabelAtlas        
        如果你用cocos2d项目模板创建过项目,那么你已经看过它的效果了,就是左下角显示帧率的数字。
        因为帧率一直在变,使用CCLabelTTF的话效率太低,因为只是数字所以也犯不上使用 CCLabelBMFont 加载那么大的文字图像,所以使用这个比较合适。
        CCLabelAtlas *label = [CCLabelAtlas labelWithString:@"12" charMapFile:@"fps_images.png" itemWidth:12 itemHeight:18 startCharMap:'.'];
          [layer addChild:label];
          label.position = ccp(100,100);
          [label setString:@“34”];

       在项目文件中,在resourse group里你可以找一下一个叫 fps_images.png的这个图像文件,文件如下:
       
       所以,这个只能显示上面这个12个字符,abcd什么的就不行了。
       解释一下参数,
       labelWithString就是字符,
       charMapFile 就是字符的图像文件(这个只要图像文件就可以了),
       itemWidth是每个字符的宽度,
       itemHeight是每个字符的高度,这个不能设错,否则显示的时候可能就不对了。
       最后一个是起始字符,它是使用这个其实字符来对应字符和图像关系的。
 

2014年2月14日 星期五

cocos2d 2.0 single touch的基本用法(轉貼)

http://zackworkshopios.blogspot.tw/2012/06/cocos2d-20-single-touch-detection-for.html

需要時方便參考用

In order to detect single touch for CCSprite in Cocos2d 2.0, you must implement CCTargetedTouchDelegate.

Implement CCTargetedTouchDelegate

@interface mySprite : CCSprite <CCTargetedTouchDelegate>

Add Target Delegation

[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];

此處 prriority:0 對應此CCLayer的優先權,在所有的layer中此layer的優先權,越高的越優先,也可以是負值,例如CCMenu就是-128,因此不會與其他layer搶先,所以不易出錯。自己所定的layer就可以高一點,如設定1就會比0高,而搶到觸控權。

swallowsTouches是說是否要獨佔此觸控權,一般來說,只有第一層設成YES,其他層最好設成NO,因為其他層CCLayer在釋放記憶體時,不會將觸控權放出,會引致程式掛掉,因此設定時要小心。要安全是釋放可參考下方

Remove Delegation

[[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];

-(void)onExit {  // 離開前要呼叫此function,來做正確的釋放touch控制權
    [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
    [super onExit];
}

Detect Touched or Not

CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
CGRect rect = [self boundingBox];
if (CGRectContainsPoint(rect, touchPoint)) {
    return YES;
} 
return NO;

sample code for mySprite.h

#import "cocos2d.h"


@interface mySprite : CCSprite <CCTargetedTouchDelegate>
@end

sample code for mySprite.c


#import "mySprite.h"

@implementation mySprite
-(void)onEnter {
    [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
    [super onEnter];
}

-(void)onExit {
    [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
    [super onExit];
}

-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    BOOL isTouched = [self touched:touch];
    if (isTouched) {
        [self stopAllActions];
        id enlarge = [CCScaleTo actionWithDuration:0.5f scale:1.1f];
        id resize = [CCScaleTo actionWithDuration:0.5f scale:1];
        [self runAction:[CCSequence actions:enlarge, resize, nil]];
    }
    return isTouched;
}

-(BOOL)touched:(UITouch *)touch {
    CGPoint touchPoint = [touch locationInView:[touch view]];
    touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
    CGRect rect = [self boundingBox];
    if (CGRectContainsPoint(rect, touchPoint)) {
        return YES;
    } 
    return NO;
}
@end

2014年2月3日 星期一

CCSpriteBatchNode 的基本method

來自http://blog.csdn.net/honghaier/article/details/7931914
只截取部分內容,方便使用時的參考資訊

class CC_DLL CCSpriteBatchNode : public CCNode, public CCTextureProtocol
 {
 public:
  //析构
  ~CCSpriteBatchNode();
 
  // 取得纹理图块顶点缓冲管理器。
  inline CCTextureAtlas* getTextureAtlas(void) { return m_pobTextureAtlas; }
  //设置纹理图块顶点缓冲管理器
  inline void setTextureAtlas(CCTextureAtlas* textureAtlas) 
  { 
   //如果当前使用的纹理图块顶点缓冲管理器与要设置的纹理图块顶点缓冲管理器不同,先释放原顶点缓冲管理器,再将当前使用的顶点缓冲管理器指针指向要设置顶点缓冲管理器。
   if (textureAtlas != m_pobTextureAtlas)
   {
    CC_SAFE_RETAIN(textureAtlas);
    CC_SAFE_RELEASE(m_pobTextureAtlas);
    m_pobTextureAtlas = textureAtlas;
   }
  }
  //取得存放所有使用此图块集的CCSprite指针数组
  inline CCArray* getDescendants(void) { return m_pobDescendants; }

  //静态函数:通过纹理指针按照默认的图块数量29创建一个CCSpriteBatch实例对象。注意:如果游戏运行中图块数量超过这个数值,则数量递增33%以满足需要

  static CCSpriteBatchNode* batchNodeWithTexture(CCTexture2D *tex);

  //静态函数:通过纹理指针按照设定的图块数量创建一个CCSpriteBatch实例对象。 
  static CCSpriteBatchNode* batchNodeWithTexture(CCTexture2D* tex, unsigned int capacity);

  //静态函数:通过图片名称按照默认的图块数量29创建一个CCSpriteBatch实例对象。
  static CCSpriteBatchNode* batchNodeWithFile(const char* fileImage);

  //静态函数:通过图片名称按照设定的图块数量创建一个CCSpriteBatch实例对象。 
  static CCSpriteBatchNode* batchNodeWithFile(const char* fileImage, unsigned int capacity);

  //通过纹理指针和图块数量进行初始化。
  bool initWithTexture(CCTexture2D *tex, unsigned int capacity);
  //通过图片名称和图块数量进行初始化。
  bool initWithFile(const char* fileImage, unsigned int capacity);
  
  //扩容,递增1/3的图块数量
  void increaseAtlasCapacity();

  //通过索引移除一个子结点,参数doCleanUp决定移除的同时是否对其进行释放。
  void removeChildAtIndex(unsigned int index, bool doCleanup);
  //将一个指定的CCSprite指针插入到子节点指针数组的指定位置
  void insertChild(CCSprite *child, unsigned int index);
  //移除一个指定的CCSprite指针
  void removeSpriteFromAtlas(CCSprite *sprite);
  //重新构建指定的CCSprite指针下的所有子结点的索引
  unsigned int rebuildIndexInOrder(CCSprite *parent, unsigned int index);
  //取得指定CCSprite的所有节点的最大图块索引
  unsigned int highestAtlasIndexInChild(CCSprite *sprite);
  //取得指定CCSprite的所有节点的最小图块索引
  unsigned int lowestAtlasIndexInChild(CCSprite *sprite);
  //取得指定CCSprite的Z顺序之上或之下的最近的图块索引
  unsigned int atlasIndexForChild(CCSprite *sprite, int z);
  
  // 实现CCTextureProtocol的接口函数
  // 取得纹理
     virtual CCTexture2D* getTexture(void);
  // 设置纹理
     virtual void setTexture(CCTexture2D *texture);
  //为纹理设置使用的混合状态
     virtual void setBlendFunc(ccBlendFunc blendFunc);
  //取得混合状态
     virtual ccBlendFunc getBlendFunc(void);

  //重载CCNode的相关函数
  //每一帧要被调用的函数
     virtual void visit(void);
  //将一个CCNode指针做为自已的子结点
     virtual void addChild(CCNode * child);
  //将一个CCNode指针以指定的Z顺序做为自已的子结点
     virtual void addChild(CCNode * child, int zOrder);
  //将一个CCNode指针以指定的Z顺序并附带一个参数值做为自已的子结点
     virtual void addChild(CCNode * child, int zOrder, int tag);
  //将一个子结点重新设置Z顺序
     virtual void reorderChild(CCNode * child, int zOrder);
     //将一个子结点删除,并根所参数决定是否释放元素
     virtual void removeChild(CCNode* child, bool cleanup);
  //将所有的子结点移除,并根所参数决定是否释放元素
     virtual void removeAllChildrenWithCleanup(bool cleanup);
  //绘制函数
     virtual void draw(void);

    protected:
        
        //从精灵数据中在图块管理器中插入一个新的图块,注意:并不将精灵放入子结点。  void addQuadFromSprite(CCSprite *sprite, unsigned int index);
  //在子结点数组指定位置插入一个新的精灵。注意:并不新建图块。
        CCSpriteBatchNode * addSpriteWithoutQuad(CCSprite*child, unsigned int z, int aTag);

 private:
  //更新混合状态
  void updateBlendFunc();

 protected:
  //使用的纹理图块顶点缓冲管理器
  CCTextureAtlas *m_pobTextureAtlas;
  //混合状态
  ccBlendFunc m_blendFunc;
  //指向存放所有使用此纹理中的图块的CCSprite指针数组的指针
  CCArray* m_pobDescendants;
 };

2014年1月13日 星期一

Cocos2d-音乐播放(轉貼)

原址 http://www.cnblogs.com/moon-7/archive/2012/04/23/2467145.html

//预加载背景音乐 和 预加载音效  在播放背景音乐与音效之前都要提前加载,
为了防止加载消耗时间发生与游戏不同步;
[[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:@"office_background.mp3"]; 
[[SimpleAudioEngine sharedEngine] preloadEffect:@"office_background.mp3"];
        
//播放背景音乐 和 循环播放背景音乐;
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"office_background.mp3"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"school_kissing.mp3" loop:YES];
        
//暂停播放背景音乐;
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
//继续播放背景音乐(不是从头开始播)
[[SimpleAudioEngine sharedEngine] resumeBackgroundMusic];
//停止播放背景音乐
[[SimpleAudioEngine sharedEngine] stopBackgroundMusic];
//设置背景音乐音量大小;
[CDAudioManager sharedManager].backgroundMusic.volume=1.0;  //(volume值范围为0.0-1.0);
//背景音乐播放几次:
[CDAudioManager sharedManager].backgroundMusic.numberOfLoops= int值;
//背景音乐播放完后:
[[CDAudioManager sharedManager] 
setBackgroundMusicCompletionListener:self selector:@selector(触发的方法)];
 
//播放音效;
[[SimpleAudioEngine sharedEngine] playEffect:@"school_kissing.mp3"];
        
//pitch(值范围:>0){值最大,音调越高,播放时间越短} 
pan:(值范围:-1到1)貌似是左右声道 
gain:(值范围:>0)音量大小;
[[SimpleAudioEngine sharedEngine] playEffect:@"office_background.mp3" 
pitch:3.0 pan:-1 gain:0.1];
 

有时我们需要循环播放一个音效;
可以做如下操作:
在 #import "SimpleAudioEngine.h" 源码中:
-(ALuint) playEffect:(NSString*) filePath loop:(BOOL)loop;

-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) 
pitch pan:(Float32) pan gain:(Float32) gain loop:(BOOL)loop;

在 #import "SimpleAudioEngine.m"源码中 实现方法:
-(ALuint) playEffect:(NSString*) filePath loop:(BOOL)loop
{
    return [self playEffect:filePath pitch:1.0f pan:0.0f gain:1.0f loop:loop];
}

-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) 
pitch pan:(Float32) pan gain:(Float32) gain loop:(BOOL)loop
{
    int soundId = [bufferManager bufferForFile:filePath create:YES];
    if (soundId != kCDNoBuffer) {
        return [soundEngine playSound:soundId sourceGroupId:0 
pitch:pitch pan:pan gain:gain loop:loop];
    } else {
        return CD_MUTE;
    }    
}
这样我们就可以用如下方法实现音效循环播放:
//loop循环播放音效;
[[SimpleAudioEngine sharedEngine] playEffect:@"school_kissing.mp3" loop:YES];
[[SimpleAudioEngine sharedEngine] playEffect:@"school_kissing.mp3" 
pitch:1.0 pan:0.0 gain:1.0 loop:YES];

停止播放音效:设一个全局的静态变量:
static int i=1;(随便赋个值)
在播放音效时候  i=[[SimpleAudioEngine sharedEngine] 
playEffect:@"school_kissing.mp3" loop:YES]; 
 
停止播放音效:   [[SimpleAudioEngine sharedEngine] stopEffect:i];

需要注意的是:如有多个音效播放,索引值不能是同一个, 因为每次播放都会返回一个int值如:
i=[[SimpleAudioEngine sharedEngine] playEffect:@"abc.mp3" loop:YES];
i=[[SimpleAudioEngine sharedEngine] playEffect:@"def.mp3" loop:YES]; 
[[SimpleAudioEnginesharedEngine] stopEffect:i]; 
//播放abc可能返回值为100,播放def返回为101,
这样通过同一个索引“i”就不能停止abc的播放了;
你会发现,还在循环播放;必须使用不同的“索引”才行;
 
 

2014年1月1日 星期三

NSMutableArray 不可作為Class來使用

官方文件上的說明

Subclassing Notes
There is typically little reason to subclass NSMutableArray. The class does well what it is designed to do—maintain a mutable, ordered collection of objects. But there are situations where a custom NSArray object might come in handy. Here are a few possibilities:

Changing how NSMutableArray stores the elements of its collection. You might do this for performance reasons or for better compatibility with legacy code.
Acquiring more information about what is happening to the collection (for example, statistics gathering).
Methods to Override

NSMutableArray defines five primitive methods:

insertObject:atIndex:
removeObjectAtIndex:
addObject:
removeLastObject
replaceObjectAtIndex:withObject:


In a subclass, you must override all these methods. You must also override the primitive methods of the NSArray class.




因為有這些method影響了作為Class的設定,因此需要覆寫原有的method才能使用,否則會產生錯誤。使用上太過麻煩,因此不建議使用NSMutableArray來作為class使用。