iPhone EXC_BAD_ACCESS with NSMutableArray of strings - objective-c

Problem context is a ViewController with several button handlers and a scores.list data file of 1000 NSString objects. If I click on buttonOne, the handler code checks if the file scores.list exists in the User Documents directory. If yes, it loads the data in an NSMutableArray called score, if not it creates the NSMutableArray in memory (to be stored on disk later) like this:
- (void)readScores
{
// Setup path + filename pathUserDocDirScorelist);
...
// test for presence scores.list in documents dir
if ([fileManager fileExistsAtPath: pathUserDocDirScorelist])
{ // Read scores.plist into NSMutableArray
score = [NSMutableArray arrayWithContentsOfFile:pathUserDocDirScorelist];
} else { // Initialize empty scores array with 1000 empty NSString entries
score = [[NSMutableArray alloc] init];
int i;
for(i = 0; i < 1000; i++) {
[score addObject:#""];
}
}
// at this point there is always a valid array score with 1000 entries
}
Basically this code works; in both cases (reading data from scores.list or in-mem build-up) I can verify in the debugger (with 'po score') that an array with 1000 entries is present afterwards.
Now comes the problem that is blocking me for 2 days now:
In the handler of buttonTwo, statements like [score count] crash, but only in case the array score gets its data from disk, not if build-up in memory. In the first case is the array still valid though until the last line of handler code of buttonOne, but then 'evaporates' as soon as the array is addressed in a next handler (EXC_BAD_ACCESS).
No, it is not caused by a premature release statement, since there are none (yet) in my entire app. :)
(not concerned with memory leaks yet).
How is this possible that within one view a NSMutableArray is valid at the end of button handler 1, but invalid at the beginning of the next button handler 2 if no explicit release statement is executed in between?
Extra info:
ViewController.h:
#interface ViewController : UIViewController {
NSMutableArray *score;
...
}
#property (nonatomic, retain) NSMutableArray *score;
...
- (IBAction) buttonOne: (id) sender;
- (IBAction) buttonTwo: (id) sender;
#end
And in ViewController.m I have:
#synthesize score;
...
- (IBAction) buttonOne: (id) sender {
if (score == nil) {
[self readScores];
}
...
NSLog(#"Number of entries in score = %i", [score count]); // never crashes
}
- (IBAction) buttonTwo: (id) sender {
NSLog(#"Number of entries in score = %i", [score count]); // **crash point**
}
P.S. I tried NSZombieEnabled by starting the app with alt/cmd/R and adding 'NSZombieEnabled=YES', but that does not result in extra information in the debug console.

It's always a little dangerous to do this:
score = [[NSMutableArray alloc] init];
outside of an init method. Because, the problem is that perhaps your method readScores is executed twice. I'd guess it almost certainly is.
What happens then is the the first scores object is never released. That causes a memory leak and sooner or later you get the dreaded EXC_BAD_ACCESS.
So, the best thing is either to first check:
if (score)
{
[score release];
}
Or, alternatively, set up scores as a synthesized retained object in your .h file. Then you can replace your code as follows and let everything happen automatically:
self.scores = [NSMutableArray array];
Note that here, I didn't use:
self.scores = [NSMutableArray alloc] init];
because that would cause two retains to happen, and of then EXC_BAD_ACCESS due to over-retention.

Related

Singleton dictionary is empty, and I don't see why

I have an iOS app that matches incoming text fields to standard fields used to import records. My problem is that a NSMutableDictionary that uses those fields is empty! Here is the code that saves the mapping:
-(void)mapUserFields: (id) sender { // move contents of each textField when user has finished entering it
SingletonDictionary *sd = [SingletonDictionary sharedDictionary];
UITextField *tf = (UITextField *)sender; // textfield contains the pointer to user's half of the equation
int tagValue = (int)tf.tag; // get the tag value
[sd.dictionaryOfUserIndexes setObject:tf.text forKey:[NSString stringWithFormat:#"%d", tagValue]]; // value found in textField id'd by tag
NSLog(#"\nfield.text: %# tagValue: %d nsd.count: %d\n",tf.text, tagValue, sd.dictionaryOfUserIndexes.count);
}
This is the result of the NSLog:
field.text: 1 tagValue: 38 nsd.count: 0
This is the definition of the singleton in the .h file:
#property (nonatomic, retain) NSMutableDictionary *dictionaryOfUserIndexes;
This is the code to initialize the singleton in the .m file:
//-- SingletonDictionaryOfUserIDs --
+ (id) sharedDictionary {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
dictionaryOfUserIndexes = [NSMutableDictionary new];
}
return self;
}
#end
I believe my problem is because the sd.dictionaryOfUserIndexes is not initialized, but I am not sure if that's true, and if so, how to initialize it (I tried several different variants, all of which created build errors). I looked on SO and Google, but found nothing that addresses this particular issue. Help would be greatly appreciated!
There are a few things we could improve in this code, but the only thing wrong with it is the reference to dictionaryOfUserIndexes in the init method. The code as posted wouldn't compile, unless: (a) you have a line like:
#synthesize dictionaryOfUserIndexes = dictionaryOfUserIndexes;
so that the backing variable is named without the default _ prefix, or (b) you refer to the ivar with the default prefix, as in:
_dictionaryOfUserIndexes = [NSMutableDictionary new];
The other way -- preferable in most every context except within an init method -- is to use the synthesized setter, like:
self.dictionaryOfUserIndexes = [NSMutableDictionary new];
But with that change alone (so it will compile) your code runs fine, adds a value to the dictionary and logs an incremented count.

Clarifications needed for a crash using NSArray, blocks and Manual Reference Counting

I need some clarifications on a crash I'm encountering using NSArray, blocks and Manual Reference Counting. My goal is to store blocks on a collection (NSArray in this case) in order to reuse them in the future.
I've setup a small sample to replicate the issue. In particular, I have a class Item that looks like the following:
#import <Foundation/Foundation.h>
typedef void(^MyBlock)();
#interface Item : NSObject
- (instancetype)initWithBlocks:(NSArray*)blocks;
#end
#import "Item.h"
#interface Item ()
#property (nonatomic, strong) NSArray *blocks;
#end
#implementation Item
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
NSMutableArray *temp = [NSMutableArray array];
for (MyBlock block in blocks) {
[temp addObject:[[block copy] autorelease]];
}
_blocks = [temp copy];
}
return self;
}
The usage is described below (I'm using in the app delegate).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = ^() {
[weakSelf doSomething1];
};
MyBlock myBlock2 = ^() {
[weakSelf doSomething1];
};
NSArray *blocks = #[myBlock1, myBlock2];
// As MartinR suggested the code crashes even
// if the following line is commented
Item *item = [[Item alloc] initWithBlocks:blocks];
}
If I run the app, it crashes with an EXC_BAD_INSTRUCTION (note that I've already enabled All Exceptions breakpoints). In particular, the app stops in the main.
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}
Note: As suggested by Ken Thomases, if you use bt command on llvm console, you are to see the back trace. In this case it shows the following:
-[__NSArrayI dealloc]
If I comment the [weakSelf doSomethingX]; it works without crashes (it does not mean that is correct).
Modifying the code a little bit like the following, all runs ok.
// Item does not do anymore the copy/autorelease dance
// since used in the declaration of the blocks
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
_blocks = [blocks retain];
}
return self;
}
and
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
MyBlock myBlock2 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
NSArray *blocks = #[myBlock1, myBlock2];
Item *item = [[Item alloc] initWithBlocks:blocks];
What is the point here? I think I'm missing something but I don't know what.
Update 1
Ok. I'll try to recap my thoughts based on the comments with #Martin R and #Ken Thomases.
A block, by default, is created on stack if a copy message is not sent to it (ARC does this for us) in order to move it on the heap. So, the situation in this case is the following. I create an autorelease array and I add two blocks where retain is called in a implicit manner. When the applicationDidFinishLaunching method finishes is execution, the blocks, since created on the stack (they are automatic variables) disappear. In a later moment, the array called blocks will be released since has been marked as autorelease. So, it will crash since it will send a release object to blocks that do not exist anymore.
So, my question is the following: What does it mean to send a retain message to a block that is on the stack? Why the array is the source of the crash (see the back trace)? In other words, since a block is on the stack, will it bump the retain count of it? And when it goes out of scope? In addiction, why if I comment the [weakSelf doSomething1] line the code works without problems? Not very clear to me this part.
You are sticking an object from the stack into an autoreleased array. BOOM ensues.
Consider:
typedef void(^MyBlock)();
int main(int argc, char *argv[]) {
#autoreleasepool {
NSObject *o = [NSObject new];
MyBlock myBlock1 = ^() {
[o doSomething1];
};
NSLog(#"o %p", o);
NSLog(#"b %p", myBlock1);
NSLog(#"b retain %p", [myBlock1 retain]);
NSLog(#"b copy %p", [myBlock1 copy]);
NSLog(#"s %p", ^{});
sleep(1000000);
}
}
Compiled/run as -i386 (because the #s are smaller and more obvious):
a.out[11729:555819] o 0x7b6510f0
a.out[11729:555819] b 0xbff2dc30
a.out[11729:555819] b retain 0xbff2dc30
a.out[11729:555819] b copy 0x7b6511a0
a.out[11748:572916] s 0x67048
Since the object is at 0x7b, we can assume that is the heap. 0xb is really high memory and, thus, the stack.
The retain doesn't cause a copy (because doing so would have invariably led to leaks) and retain on a stack based object is meaningless.
If you change the [o doSomething1]; to [nil doSomething1]; then that becomes a static block and that lives in readonly mapped memory (readonly-executable pages from the mach-o's TEXT segment) and, thus, there is no allocation to deallocate and retain/release/autorelease are no-ops.
As you can see, the static block ended up around 0x67048 (this number may change from run to run, btw, for a variety of reasons. Low in memory.
In fact, because of the sleep(), we can run vmmap against the a.out process and see:
==== Writable regions for process 11772
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
__DATA 00067000-00068000 [ 4K] rw-/rwx SM=ZER /tmp/a.out
That is, the static block was in the first 4K segment of mapped writable regions from the mach-o file. Note that this doesn't mean the code is in that writable region (SECURITY HOLE if it were). The code is in the TEXT segment mapped into the readable regions.

Problem adding object to NSMutableArray

I am having problems adding objects to my NSMutableArray. It seems that something gets added (object count increases by 1 in debugger), but the only thing added is a 0x0 (null) instead of the address of an object. I've read through everything somewhat relevant that I could find, but I couldn't find anything that seemed to answer this issue. Most related posts seem to revolve around memory management, but the solution is not jumping out at me.
I appreciate any help you can provide.
I've included what I think are the relevant parts of the code. Please tell me if you need to see anything more.
GamePlayView.h
#interface GamePlayView : UIViewController
{
Player *gamePlayer;
NSMutableArray *boardObjects;
}
#property (retain, nonatomic) Player *gamePlayer;
#property (retain, nonatomic) NSMutableArray *boardObjects;
#end
GamePlayView.m
- (void)viewDidLoad
{
// Create player
Player *tempPlayer = [[Player alloc] initWithFrame: self.view.frame];
if (tempPlayer == NULL) {
NSLog(#"GamePlayView viewDidLoad: null Player");
}
else gamePlayer = tempPlayer;
// Create array of board objects
NSMutableArray *newArray = [[NSMutableArray alloc] init];
self.boardObjects = newArray;
[self.boardObjects addObject: gamePlayer]; // First breakpoint here
topObject = 0;
BoardObject *mine = [[BoardObject alloc] initWithFrame: self.view.frame];
[self.boardObjects addObject: mine]; // Second breakpoint here
topObject = 1;
[super viewDidLoad];
} // End viewDidLoad
I put breakpoints at the addObject lines (commented in code). When execution stops at the first breakpoint, the debugger shows a good tempPlayer, and a good gamePlayer (both with the same address). It shows 0 objects in boardObjects, like this:
boardObjects = (_NSArrayM *) 0x4b22080 0 objects
When I step over this breakpoint, the debugger shows 1 object in boardObjects, as follows:
boardObjects = (_NSArrayM *) 0x4b22080 1 objects
0 = (NSObject *) 0x0
When I continue program execution, and the debugger stops at the next breakpoint, I also see a good mine object, with boardObjects still described as above. After stepping over this breakpoint, boardObjects now looks like this:
boardObjects = (_NSArrayM *) 0x4b22080 2 objects
0 = (NSObject *) 0x0
1 = (NSObject *) 0x0
This could be the case that tempPlayer is a local variable, after returning from the function, the local variable is automatically released.
Someone suggested I display the boardObjects in the code anyway, even though it looked from the debugger that I only had null objects. I inserted this code after the last addObject:
NSLog(#"self.boardObjects is: %#", [self.boardObjects description]);
This displayed good objects: the gamePlayer and the mine!
I did verify that immediately before this NSLog statement, and immediately after, the debugger still displays two null entries in boardObjects.
It seems like the answer to my original question, then, is that the code itself is correct. However, it seems now that I have a new question: Does this mean I'll never be able to view into an NSMutableArray from within the debugger?
You should assign #property with self.
self.gamePlayer = tempPlayer;

making NSArray global

i have NSarray which i want to acces from all of my methods,(i want it Global), i m going to populate this array in one of my methods defined in .m file(only once).
my question is ... is it really possible to declare a NSArray in .h and define it place somewhere else or it just has to be defined when it is declared(initialization).
MY CURRENT CODE
.h file
#interface slots2ViewController : UIViewController {
NSArray* paylinesArr;
}
i m calling follwing method from ViewDidLoad
.m file
-(void)init_payline_arr
{
NSString* filePath = #"/Users/net4uonline/Desktop/slots2/paylines.txt";//filepath...
NSString *fileContents = [NSString stringWithContentsOfFile: filePath];
paylinesArr = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
}
i m not able to use paylinesArr array from other methods the app crashes if follwing function is getting called
-(IBAction)ShowVal
{
NSLog(#"number of elements! %#",[paylinesArr count]);
}
Or
should i use NSMutabbleArray instead?
if you want to see then i have uploaded my desktop video while i m using debug tools!
the link to video
in this video i press the record button of the debugger(i have ns zombie enabled and the retain count as well),the app starts,i press spin button and apparently it crashes...then i show you the code which has
paylinesArr = [[fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] retain];
which retains the paylineArr
NSMutableArray won't help you! The reason of your error is that your paylinesArr variable is autorelease variable, so probably it was deallocated before ShowVal is called. Try to retain it like
-(void)init_payline_arr {
...
paylinesArr = [[fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] retain];
...
}
This should work. But don't forget to release it in viewDidUnload method:
- (void)viewDidUnload {
[super viewDidUnload];
...
[paylinesArr release];
}

Objective C Reassignment/Memory Management Crash

As a relative Objective-C beginner, I'm obviously still not grasping certain memory management rules. I can't figure out how to make this not crash:
#interface MyClass { NSArray *playerArray4th; }
- (void) viewDidLoad { playerArray4th = [self getAudioPlayersForSoundFile:#"rimshot" ofType:#"aif"]; }
- (NSArray*) getAudioPlayersForSoundFile:(NSString*)soundFileName ofType:(NSString*)soundFileType {
//code instantiating objects....
NSArray *toRet = [[NSArray alloc] initWithObjects:toRetTickPlayer,toRetTickPlayerCopy,toRetTickPlayerCopy2,toRetTickPlayerCopy3, nil];
return toRet;
}
Then later, in a different function:
NSArray *currentArray = playerArray4th;
[currentArray release];
currentArray = nil;
currentArray = [self getAudioPlayersForSoundFile:fileName ofType:ofType];
And it crashes when trying to access the array again:
- (void) playSound:(NSString*)soundType {
AVAudioPlayer *currentPlayer;
if ([soundType isEqualToString:#"4th"]) {
if (playerArray4thCounter >= [playerArray4th count]) playerArray4thCounter = 0;
NSLog(#"Playing from array: %#",playerArray4th);
currentPlayer = [playerArray4th objectAtIndex:playerArray4thCounter];
playerArray4thCounter++;
}
}
Try to learn about properties and about using getters and setters. Don't take shortcuts unless you know exactly what's going on.
So define the playerArray4th property in your header file:
#property (nonatomic,retain) NSArray *playerArray4th;
And then in your .m file create getter/setter:
#synthesize playerArray4th;
Then, always use self.playerArray4th for assigning and getting the variable. The prior objects will be released when needed.
So this will not leak:
self.playerArray4th = [NSArray arrayWithObjects:#"text",#"text",nil];
self.playerArray4th = [NSArray arrayWithObjects:#"new array",#"text",nil];
because the second assignment releases the first array.
Furthermore, read about using autorelease. In short, if you alloc, copy or new, you should either release or autorelease. There's a lot to read about this here on SO which I will not repeat here now.
Don't forget to put self.playerArray4th = nil; in your dealloc method.