views:

199

answers:

2

The code I'm currently working on requires adding an NSNumber object to an array. All of the NSNumbers with value 0-12 are added fine, but 13 onward causes a EXC_BAD_ACCESS. I turned on NSZombieEnabled and am now getting *** -[CFNumber retain]: message sent to deallocated instance 0x3c78420.

Here's the call stack:
#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

If it wasn't isolated to NSNumbers of a certain range, I'd assume I screwed something up with my memory management, but I've just got no idea.

Any ideas?

Thanks,
Josh

+2  A: 

Clearly NSNumbers >12 will retain. I suggest that you write a very small program that proves this to yourself. Then take that program, make it a function, and call it early in your program. Slowly move the function to later points in your program until the error appears. You will thus find your real bug.

vy32
I added a retain line into the code and everything works perfectly now. No idea why. I'm just going to roll with it.Thanks!
jkap
@jkap may I suggest that it would be worth your while to study up on the memory management rules and figure out *why* it works now? If you understand how you fixed this problem, you'll have less chance of having similar issues in the future.
David Gelhar
@jkap: adding random retains into your code will cause you to end up with unreleasable programs that leak tons of memory. the retain/release rules are actually very clear once you're grokked them, so take the time to understand what's going on.
Felixyz
+6  A: 

The numbers 0 through 12 are special as I discovered when answering another question here. Keep in mind that this is an implementation detail, not a language specification thing.

Basically, numbers up to (and including) 12 give you a reference to an already existing NSNumber, something which is possible due to the fact that they're immutable. Investigation showed that numbers 13 or greater gave a separate instance.

So you probably have screwed up your memory management after all :-) It's just that the fact that numbers less than 13 are likely references to numbers already in existence that's saving your bacon in those cases. I suggest you post more code so that we can track down that specific problem.


And based on your comment to another answer here:

I added a retain line into the code and everything works perfectly now. No idea why. I'm just going to roll with it. Thanks!

I think you'll find that the fact that NSNumbers less than 13 already have a retain count of 1 before you get your own (bumping the count up to 2) is why they're not causing the EXC_BAD_ACCESS. Obviously your code is losing all the numbers you allocate, but the system isn't freeing those under 13 since they're still in use (retain count of 1 or more).

paxdiablo
fascinating! a statement like "numbers 0 through 12 are special" can't go untested, and Lo And Behold, you're absolutely correct! :)
Dave DeLong
wow. This is so wrong. This is one of the things I really hate about Objective-C. all numbers should be immutable. All strings should be immutable. Python got this right.
vy32
For a second there, @vy32, I thought you were having a go at my answer being "so wrong" as opposed to ObjC behaviour. :-) But I'm not sure I understand you. NSNumbers are *indeed* immutable, that's what makes this optimisation possible (so you can't change the number "behind" NSNumber(15) to screw up everyone else using it, for example).
paxdiablo
Huh. No, the "so wrong" applies to Apple, not to you. If NSNumbers are immutable, then why make them special? Or why draw the cut-off at 12, and not at 1M? it's just so arbitrary...
vy32
Well, it's just an optimisation. They seem to have decided that the numbers up to 12 would be so commonly used that it was worth setting them up in advance, or maybe they had a demo clock application that ran slow and they wanted to make it look better :-) I probably would have stopped at pre-allocating -1, 0 and 1 since they would be the most comman of all. But there's a trade-off. You could allocate up to a million but that would use a fair chunk of memory for a lot of numbers no-one would use. Think about the number of times you've ever needed the number 174,563 in your applications.
paxdiablo
Thank you for posting why this happens. It was the strangest error I've ever come across and it's definitely weird that this happens in objc.
jkap