NSNumber大于等于13不会保留。其他所有数字都会被保留。

4
我目前正在处理的代码需要将NSNumber对象添加到数组中。所有值为0-12的NSNumbers都可以正常添加,但是从13开始会导致EXC_BAD_ACCESS。我打开了NSZombieEnabled,现在得到了“***-[CFNumber retain]:message sent to deallocated instance 0x3c78420”的错误信息。
以下是调用堆栈:
#0 0x01eac3a7 in ___forwarding___
#1 0x01e886c2 in __forwarding_prep_0___
#2 0x01e3f988 in CFRetain
#3 0x01e4b586 in _CFArrayReplaceValues
#4 0x0002a2f9 in -[NSCFArray insertObject:atIndex:]
#5 0x0002a274 in -[NSCFArray addObject:]
#6 0x00010a3b in -[Faves addObject:] at Faves.m:24
#7 0x000062ff in -[ShowController processFave] at ShowController.m:458
#8 0x002af405 in -[UIApplication sendAction:to:from:forEvent:]
#9 0x00312b4e in -[UIControl sendAction:to:forEvent:]
#10 0x00314d6f in -[UIControl(Internal) _sendActionsForEvents:withEvent:]
#11 0x00313abb in -[UIControl touchesEnded:withEvent:]
#12 0x002c8ddf in -[UIWindow _sendTouchesForEvent:]
#13 0x002b27c8 in -[UIApplication sendEvent:]
#14 0x002b9061 in _UIApplicationHandleEvent
#15 0x02566d59 in PurpleEventCallback
#16 0x01e83b80 in CFRunLoopRunSpecific
#17 0x01e82c48 in CFRunLoopRunInMode
#18 0x02565615 in GSEventRunModal
#19 0x025656da in GSEventRun
#20 0x002b9faf in UIApplicationMain
#21 0x00002498 in main at main.m:14
如果不是仅限于某个范围内的NSNumbers,我会认为我在内存管理方面出了问题,但是我现在完全没有头绪。
有任何想法吗?

谢谢,
乔希


8
这是因为ObjC有十三恐惧症 :p。 - kennytm
2个回答

11

当我回答另一个问题(链接)时,我发现0到12这些数字是特殊的。请注意,这是一些实现细节,而不是语言规范。

基本上,小于等于12的数字会给你一个对已经存在的NSNumber的引用,这可能是由于它们是不可变的事实导致的。调查表明,大于13的数字给了您另一个实例。

因此,您可能已经搞砸了内存管理。只是在这些情况下,小于13的数字可能是对已经存在的数值的引用,这就拯救了您。建议您发布更多代码,以便我们可以追踪到具体的问题。


根据您在这里的另一个答案的评论:

我在代码中添加了一个保留行,现在一切都完美了。没想到为什么。我会继续前进。谢谢!

我认为你会发现,在你自己得到NSNumbers(将计数增加到2)之前,小于13的NSNumbers已经有了保留计数为1的计数,并且这就是它们不会导致EXC_BAD_ACCESS的原因。显然,您的代码正在丢失所有分配的数字,但是由于它们仍在使用(保留计数为1或更多),系统不会释放小于13的数字。


1
太棒了!像“数字0到12是特殊的”这样的语句不能不经过测试,结果你完全正确! :) - Dave DeLong
哇,这太糟糕了。这是我真正讨厌 Objective-C 的事情之一。所有数字都应该是不可变的。所有字符串都应该是不可变的。Python 做得很对。 - vy32
刚才@vy32,我以为你在攻击我的答案“非常错误”,而不是ObjC的行为。:-)但我不确定我是否理解了你的意思。NSNumbers确实是不可变的,这就使得这种优化成为可能(例如,您无法更改NSNumber(15)“后面”的数字以破坏其他人使用它)。 - paxdiablo
哦,不,"so wrong" 是针对苹果的,而不是针对你的。如果 NSNumbers 是不可变的,那么为什么要将它们特殊化?或者为什么要在12处切断,而不是在1M处?这只是如此武断... - vy32
2
嗯,这只是一种优化。他们似乎已经决定将数字设置为12以内的常用数字,这样值得提前设置,或者可能他们有一个演示时钟应用程序运行缓慢,他们想让它看起来更好:-) 我可能会停止预分配-1、0和1,因为它们是最常见的。但是有一个权衡。你可以分配高达一百万,但那将使用大量内存来存储很多人不会使用的数字。想想你在应用程序中需要174,563这个数字的次数。 - paxdiablo
感谢您发布关于为什么会发生这种情况的内容。这是我遇到过的最奇怪的错误,而且这在 Objective-C 中发生确实很奇怪。 - jkap

2

显然,大于12的NSNumber将被保留。我建议您编写一个非常小的程序来证明这一点。然后将该程序制作成函数,并在程序早期调用它。逐渐将该函数移动到程序的后面,直到出现错误。这样您就可以找到真正的错误。


我在代码中添加了一个保留行,现在一切都完美地运作了。不知道为什么,但我打算继续使用它。 - jkap
@jkap,我建议你值得花时间研究内存管理规则,并弄清楚为什么现在它能够正常工作。如果你理解了如何解决这个问题,以后就不太可能遇到类似的问题了。 - David Gelhar
1
@jkap:在你的代码中添加随机的保留操作会导致你最终得到无法释放的程序,从而泄漏大量内存。一旦你理解了保留/释放规则,它们实际上是非常清晰的,所以花时间去理解发生了什么。 - Felixyz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接