多线程与托管以及堆栈共同作用造成的内存问题

《原创作品,转载请注明出处》


花费三个小时,终于解决了一个特别隐蔽的内存问题。特此分享。希望各位不要在犯这类毛病~~真的会死人。

高手全当娱乐新闻,只望和我一样水平有限的同仁不要犯相同的错误。

问题代码及讲解如下。


原始问题代码(经过简化处理):

环境描述:mac, xcode4.2iOS5(iphone4s)cocos2d工程,objective-c语言


可跳过代码往后看,后面描述很简单==========================================================

//通知执行指定的动画,action-动画类型

- (void)runSpecialAction:(HeroAction)action

{   

    //获得动画

    CCAnimation *animation = [allSpecialAction objectForKey:[NSNumber numberWithInt:action]];

    //创建动画

    CCAnimate* animate = [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO];

    //执行动画序列

    [self runAction:[CCSequence actions:animate, 

                     [CCCallFuncND actionWithTarget:self 

                                           selector:@selector(runSpecialActionDone:Data:) 

                                               data:(void *)&action],  //action作为参数传递

                     nil]];

    CCLOG(@"值:%x  地址:%08x", action, &action);

    //输出 :值:4  地址:bfffdbe0

}


//指定的动画播放结束后执行此函数。data-数据

- (void)runSpecialActionDone:(CCNode *)pTarget Data:(void *)data

{

    CCLOG(@"值:%x  地址%08x", *((int *)data), data);

    //输出内容:值:98da0511  地址:bfffdbe0

    ...

}


两次地址相同,证明参数传递正常,既:函数1action地址顺利传递到了函数2data中。但对应的值却发生了变化,cocos2dBUGNO。完全是程序逻辑问题。并且主要出在[CCSequence actions:(CCFiniteTimeAction *), ..., nil]上,其原因是CCSequence actions不会阻塞,甚至在函数1返回之后,函数2仍然没有得到执行。此时函数1被出栈,导致函数1栈上的action内存被重用。就产生了内存被莫名奇妙修改了的问题。好了,如果你已经了解,下面内容就不需要浪费时间看了。


仔细分析一下,问题变的非常简单。


=============================上面代码看不懂直接跳转到这里=============================


简化一下程序(c格式):


//产生问题的函数定义。参数:一个函数指针,以及一个void*作为参数(最终传递给pfun2指向的函数)。

//函数actions在另一个线程中执行,并且不会阻塞,甚至会在fun2调用开始之前就返回。

actions((void)(pfun2 *)(void *), void *a)

{

    pfun2(a)添加到一个委托程序,等待指定的事件发生后在执行;

    返回;

}


(void)fun2(void *b)

{

                //问题所在:bfun1函数中的a具有相同地址,没有在任何地方修改,但是值却不同。

    *(int *)b;  //使用b(b转换成整型指针,间接访问,以获得其值)

}


(void)fun1(int a)

{

    (void)(pfun2 *)(void *); //定义函数指针

    pfun2 = fun2;

    actions(pfun2, &a);      //将函数指针传递给actions,并将a的地址作为参数

}



关于fun2中参数b的值和fun1中参数a的值不同,解释如下:

fun1调用actions,当actions函数返回后,fun1函数随即返回。这时,fun2函数还在等待执行。

注意fun2的参数void *bb指向fun1函数的参数ab存储着fun1函数的 参数a的地址)

那么,fun2*b(间接访问b),就应该是a的内容。

事实上我们的确在fun2中访问到了a的内容,只是这个内容已经被覆盖。

在我们访问*b时,fun1函数已经返回,而fun1的参数a只是fun1栈上的一个地址空间,

随着fun1返回,其栈空间已被释放(出栈),当然a也就不存在了。

所以我们通过fun2访问a时,得到了一个不确定的内容。


在系统内存利用率低的情况下,这种问题很难发现。但是在移动设备上内存紧缺,这种问题就容易暴露出来了。



堆栈调用如下:

fun1(int a)

{

通知actions调用fun2(a);

    actions

    {

    fun2代理给其他程序,并迅速返回到fun1;

    }

actions出栈;

}

fun1出栈,同时参数int a被释放;


fun2(a)被调用:a已经被释放。


=完=

你可能感兴趣的:(多线程,animation,action,XCode4,fun,ios5)