Getting EXC_BAD_ACCESS after splitting NSString - objective-c

I am trying to create a game in which people answer questions. I am having the questions be loaded from a text file. Different parts are separated with • and questions with §. The code for separating works fine, except for when I try to create a question with it.
for(int n = 0; n<[questionsFromFile count]; n++)
{
NSArray *params = [((NSString *)questionsFromFile[n]) componentsSeparatedByString:#"•"];
NSString *fact = params[0];
NSString *prompt = params[1];
NSString *image = params[2];
NSString *answerAsString = params[3];
BOOL answer = [answerAsString boolValue];
YNQuestion *question = [[YNQuestion alloc]initWithFact:fact prompt:prompt image:image answer:answer];
[self.allQuestions addObject:question];
}
In questions.txt:
fact•prompt•unknown.png•YES
§fact•prompt•unknown.png•NO
When I run it, it works fine until a question is loaded. Then, it crashes with EXC_BAD_ACCESS(code=2). If i replace fact, prompt, image etc. to equal #"hello" or #"goodbye" it works fine.
I am using ARC. Also, here is the code for the YNQuestion.
#interface YNQuestion : NSObject
#property(assign, nonatomic)NSString *fact;
#property(assign, nonatomic)NSString *prompt;
#property(assign, nonatomic)NSString *image;
#property(assign, nonatomic)BOOL answer;
-(id)initWithFact:(NSString *)fact prompt:(NSString *)prompt image:(NSString *)image answer: (BOOL) answer;
-(BOOL)checkIfCorrect: (BOOL)answer;
#end
Now, it works. Only with ones that are not my default.
Surprise! It doesn't work again. I believe the error is with having hardcoded answers and answers in the .txt file. I'm testing.

You need to keep strong references to the strings you pass to your initializer. Set NSString properties to strong instead of assign and it will stop crashing.

You are accessing an index that probably doesn't exist in the array. Try logging the count of the array, to see how many entries are in the array.
Also, set all-exception breakpoint to see where exactly the app crashes. Or you could set a breakpoint right after you load the array, to see its contents.

Related

Trying to create a category for NSArray, but do not understand why the thread stalls/breaks

I'm trying to follow team treehouse's course on objective-C.
This part, the course is trying to teach us about implementing categories. So I have the code below
main.m
#import <Cocoa/Cocoa.h>
#import "NSArray+mahem.h"
int main()
{
NSArray *letters = #[ #"alfa", #"bravo", #"charlie"];
NSLog(#"letters %#", letters);
NSLog(#"cap %#", [letters capitalizeStrings]);
return 0;
}
NSArray+mahem.h
#import <Foundation/Foundation.h>
#interface NSArray (mahem)
-(NSArray *)capitalizeStrings;
#end
NSArray+mahem.m
#import "NSArray+mahem.h"
#implementation NSArray (mahem)
-(NSArray *)capitalizeStrings{
NSMutableArray *cap = [NSMutableArray array];
for (NSString *string in self) {
[cap addObject:[string capitalizedString]];
}
return cap;
}
#end
Basically, I am trying to capitalize every word in the NSArray letters. However, when I run main.m in xcode 6, the program reaches a breakpoint at the line for (NSString *string in self) { in the file NSArray+mahem.m.
I have never used the xcode debugger before, so am unsure what I'm seeing or how I should fix this. It seems to say that self does have 3 objects (#"alfa", #"bravo", #"charlie"), but cap has 0, and string apparently equals 0xa1a1a1a1. I'm assuming this means it broke on the first loop, or else cap would have at least one object. Why did string get gibberish?
Is this because NSArray is const and is not mutable? How do I fix this? If anyone can explain this to me, it would be very helpful. Thanks
From your description, everything works just fine. When execution flow gets to the string for (NSString *string in self) {
Your self array is fully populated - it was created previously in main()
Your cap has been declared and initialized, but not populated - for loop wasn't executed once.
Your string was declared but not initialized - it exists, but points to some random place in memory. It is not broken. If you'll step over in debugger, you will see, that string becomes #"alpha", but cap is still empty. One more step over - string is still #"alpha" and cap is populated with one object.
Everything should work fine. Feel free to ask anything left unclear

Creating an Array From a CSV File Using Objective C

I'm new to coding so please excuse me if this seems like a simple question.
I'm trying to plot coordinates on a map.
I want to read a CSV file and pass the information to two separate arrays.
The first array will be NSArray *towerInfo (containing latitude, longitude and tower title)
the second, NSArray *region (containing tower title and region) with the same count index as the first array.
Essentially, I believe I need to;
1) read the file to a string.....
2) divide the string into a temporary array separating at every /n/r......
3) loop through the temp array and create a tower and region object each time before appending this information to the two main storage arrays.
Is this the right process and if so is there anyone out there who can post some sample code as I'm really struggling to get this right.
Thanks to all in advance.
Chris.
I have edited this to show an example of my code. I am having the problem that I'm receiving warnings saying
1) "the local declaration of 'dataStr' hides instance variable.
2) "the local declaration of 'array' hides instance variable.
I think I understand what these mean but I don't know how to get around it. The program compiles and runs but the log tells me that the "array is null."
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize dataStr;
#synthesize array;
-(IBAction)convert {
//calls the following program when the onscreen 'convert' button is pressed.
NSString *dataStr = [NSString stringWithContentsOfFile:#"Towers.csv" encoding:NSUTF8StringEncoding error:nil];
//specifies the csv file to read - stored in project root directory - and encodes specifies that the format of the file is NSUTF8. Choses not to return an error message if the reading fails
NSArray *array = [dataStr componentsSeparatedByString: #","];
//splits the string into an array by identifying data separators.
NSLog(#"array: %#", array);
//prints the array to screen
}
Any additional help would be much appreciated. Thanks for the responses so far.
NSString* fileContents = [NSString stringWithContentsOfURL:filename ...];
NSArray* rows = [fileContents componentsSeparatedByString:#"\n"];
for (...
NSString* row = [rows objectAtIndex:n];
NSArray* columns = [row componentsSeparatedByString:#","];
...
You'll probably want to throw in a few "stringTrimmingCharactersInSet" calls to trim whitespace.
Concerning your warnings:
Your code would produce an error (not a warning), since you need to declare your properties in the interface file before you synthesize them in the implementation. You probably remember that #synthesize generates accessor methods for your properties. Also, before using the #synthesize directive, you need to use the #property directive, also in the interface.
Here's an example:
#interface MyObject : NSObject {
NSString *myString;
}
#property (assign) NSString *myString;
#end
#implementation MyObject
#synthesize myString;
// funky code here
#end
Note that the property declaration is followed by a type (assign in this case, which is the default). There's an excellent explanation about this in Stephen G. Kochans's book: Programming in Objective-C 2.0
But assuming for argument's sake, that you omitted the correct #interface file here.
If you first declare a property in the #interface, and then declare another property in your method, using the same variable name, the method variable will take precedence over the instance variable.
In your code, it would suffice to omit the declaring of the variable name, like so:
dataStr = [NSString stringWithContentsOfFile:#"Towers.csv" encoding:NSUTF8StringEncoding error:nil];
array = [dataStr componentsSeparatedByString: #","];
I'm assuming that the core of your question is "how to parse a CSV file", not "what to do with the data once it's parsed". If that's the case, then check out the CHCSVParser library. I have used it in projects before and find it to be very reliable. It can parse any arbitrary string or filepath into an NSArray of rows/columns for you. After that, whatever you do with the data is up to you.

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;

Objective C 's NSString subStringToIndex causing strange issue

Hi everyone I am working on a game and I have run into a really weird issue and was hoping to get some help. Basically sometimes the text is a bit long to show in one textbox so I wanted it to break down a string and store the undisplayed text in a string to be used in the next message box. So when I use this code..
NSString * Talkin1 = #"Hello I am an annoying string";
NSString * Talkin2 = [Talkin1 substringToIndex:5];
It makes Talkin2 the value of Hello which is what I want. Then I store it in the object with..
[_window setMultiMessage:Talkin2];
which goes to my set and get methods in an object i made.
In it's interface...
NSString * multiMessage;
in its .m
-(void) setMultiMessage:(NSString*)messageTwo
{
multiMessage = messageTwo;
}
-(NSString*) getMultiMessage
{
return multiMessage;
}
then later the main object pulls it out again, when it is done closing the first window with...
NSString * talking = [_window getMultiMessage];
Now in debugging, I have noticed talking's value will be "out of scope" when i get the string back from _window. So then it crashes my program when it tries to use it.
But. If i do this everything works fine.
NSString * Talkin1 = #"Hello I am an annoying string";
//NSString * Talkin2 = [Talkin1 substringToIndex:5];
[_window setMultiMessage:Talkin1];
So it works perfect (except for splitting the string like I want) when I use #" " but not when I use any result of substringToIndex or substringFromIndex.
I am new to objective c so I assume it is something basic I am missing. Any help would be wonderful! Thank you in advance.
(Assuming no GC.)
-substringToIndex: returns an autoreleased object. But you are not retaining the object in the setter, thus no one "owns" the Talkin2 and it will be deallocated "later". You need to copy or retain the string in the setter.
-(void) setMultiMessage:(NSString*)messageTwo {
if (multiMessage != messageTwo) {
[multiMessage release];
multiMessage = [messageTwo retain];
}
}
Actually you should really use Objective-C 2.0's declared property feature. It allows correct setter and getter be generated automatically.
#interface .... { ... }
...
#property(copy) NSString* multiMessage; // <--
...
#end
#implementation ....
#synthesize multiMessage; // <--
...
#end
...
_window.multiMessage = Talkin2;
// or: [_window setMultiMessage:Talkin2];
NSString* talking = _window.multiMessage;
// or: talking = [_window multiMessage];

Crashing Strings.... :(

I have in the .h file :
NSString *dataHML;
NSString *dataHML2;
NSString *dataHML3;
NSString *dataHML4;
NSString *dataHML5;
NSString *dataHML6;
NSString *dataHMLtotal;
in the .m file I merge them with :
NSString *dataHtmlTotal = [NSString stringWithFormat:#"%#%#%#%#%#%#", dataHtml, dataHtml2, dataHtml3, dataHtml4,dataHtml5,dataHtml6];
But unfortunately it crashes at some point because of this.
Could anyone give me a other solution and post it please, because I already tried nsuserdefault or a nsarray, but without I couldn't get it working.
If you really do have 6 variables numerically named like that, you could be better off with an array.
NSMutableArray *dataHMLStrings = [NSMutableArray array];
[dataHMLStrings addObject:#"String1"];
[dataHMLStrings addObject:#"String2"];
.
.
.
[dataHMLStrings addObject:#"String100"]; // or however many you have.
NSString *dataHMLTotal = [dataHMLStrings componentsJoinedByString:#""];
You can give the componentsJoinedByString: method a different string (I passed an empty string here because you didn't want anything to appear between each dataHML string).
Please make sure your strings are all allocated and initialized (neither points of which you mention in your question.) If you do not do so then you run the risk of manipulating data at the location of the garbage pointers, and your application will very likely crash.