I've defined a struct and want to assign one of its values to a NSMutableDictionary. When I try, I get a EXC_BAD_ACCESS. Here is the code:
//in .h file
typedef struct {
NSString *valueOne;
NSString *valueTwo;
} myStruct;
myStruct aStruct;
//in .m file
- (void)viewDidLoad {
[super viewDidLoad];
aStruct.valueOne = @"firstValue";
}
//at some later time
[myDictionary setValue:aStruct.valueOne forKey:@"key1"]; //dies here with EXC_BAD_ACCESS
This is the output in debugger console:
(gdb) p aStruct.valueOne
$1 = (NSString *) 0xf41850
Is there a way to tell what the value of aStruct.valueOne is?
Since it is an NSString, why does the dictionary have such a problem with it?
------------- EDIT -------------
This edit is based on some comments below.
The problem appears to be in the struct memory allocation. I have no issues assigning the struct value to the dictionary in viewDidLoad, as mentioned in one of the comments. The problem is that later on, I run into an issue with the struct. Just before the error, I do:
po aStruct.oneValue
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
0x9895cedb in objc_msgSend ()
The program being debugged was signaled while in a function called from GDB.
GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off"
Evaluation of the expression containing the function (_NSPrintForDebugger) will be abandoned.
This occurs just before the EXC_BAD_ACCESS:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"MM-dd-yy_HH-mm-ss-A"];
NSString *date = [formatter stringFromDate:[NSDate date]];
[format开发者_如何学编程ter release];
aStruct.valueOne = date;
So the memory issue is most likely in my releasing of formatter. The date var has no retain. Should I instead be doing
NSString *date = [[formatter stringFromDate:[NSDate date]] retain];
Which does work but then I'm left with a memory leak.
I recreated your code and put the NSDictionary setValue method, right under the aStruct.valueOne = @"firstValue" line. It works perfectly without any error. So, your problem is not with the NSString, but with one of the objects (either aStruct or myDictionary) getting deallocated somewhere down the line. You can try the following:
static myStruct aStruct;
To print the value of aStruct.valueOne, you may use:
NSLog(@"aStruct.valueOne = %@ \n", aStruct.valueOne);
Also, you can check the retain counts of myDictionary to see if it is still allocated. However, trying to check for the retain count of an already deallocated object will result in an error. But it will tell you in the error log, that the object is already deallocated.
NSLog(@"Retain Count of myDictionary = %i \n", myDictionary.retainCount);
Hope that helps.
Not sure why it's crashing, but instead of p, use po and it will print the contents of the NSString or description of any NS/CF object.
Try to enable zombies to check where do you over-release objects. Project -> Edit Active Executable -> Arguments tab -> Add variable NSZombieEnabled and assign it YES. (and set checkbox to YES).
Then you should get more info about your error in the trace.
Don't forget to disable NSZombieEnabled checkbox then.
EDIT:
When you assign a valueOne
:
aStruct.valueOne = @"firstValue";
you actually create NSString
object and put it into autorelease pool. So later when you try to get that object to pass it to your dictionary, it might be autoreleased already, that's why you are getting EXC_BAD_ACCESS
. You have to implement some method which will do memory management for you when you assign new pointers to your struct. Something like this:
- (void)setValueOne:(NSString *)newValueOne {
[aStruct.valueOne autorelease];
aStruct.valueOne = [newValueOne retain];
}
So in your viewDidLoad
you have to use your new method:
- (void)viewDidLoad {
[super viewDidLoad];
// make sure it is nil "on startup" because setValueOne: method will send autorelease method
aStruct.valueOne = nil;
aStruct.valueTwo = nil;
[self setValueOne:@"firstValue"];
}
Any 'class *instance' is a pointer to an object. That $1 = (NSString *) 0xf41850
you see is actually the pointer to the memory space that is currently allocated to hold valueOne. The issue usually associated with EXEC BAD ACCESS is that the memory space is not permanently allocated to hold valueOne, and is reclaimed later on, usually as soon as the method finishes execution (called garbage collection). Then, when you later try to access it, like from a different method or even the same method but on a subsequent execution, the system says 'hey, that memory address is used for something else", it throws the Exec Bad Access error.
Is there a way to tell what the value of aStruct.valueOne is?
Well, the debugger, at the moment you establish the value, should be able to pick up on the definition and show you the literal value; it should know that the memory space at 0xf41850 maps to an NSString class, which is an array of bytes of some length, and that the bytes at that address are encoded a certain way, and thus should map into some string that can be displayed on your display in the debugger. However, later on, no, the system has no (valid) idea of what that space contains, since it could contain, well, anything.
So, when you run into EXEC BAD ACCESS, it means that you are not retaining that value for long enough period, either because you have not purposely retained it, or have released it (on purpose or inadvertently let the system release it).
精彩评论