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

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


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

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

问题代码及讲解如下。


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

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


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

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

- (void)runSpecialAction:(HeroAction)action

{

//获得动画

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

//创建动画

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

//执行动画序列

[selfrunAction:[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

...

}


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


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


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


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


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

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

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

{

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

返回;

}


(void)fun2(void*b)

{

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

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

}


(void)fun1(inta)

{

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

pfun2 = fun2;

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

}



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

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

注意fun2的参数void *b,b指向fun1函数的参数a(b存储着fun1函数的参数a的地址)。

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

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

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

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

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


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



堆栈调用如下:

fun1(inta)

{

通知actions调用fun2(a);

actions

{

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

}

actions出栈;

}

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


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


=完=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值