Null output troubles - cocoa-touch

Okay I've been trying at this for about 2-3 hours now and I don't seem to quite get it. Here is the code and a brief explanation:
I'm trying to make two lists of words, pull one word from each of those lists at random, and display both words (along with a third) on the screen when a button is pressed. Here is the code:
#import "Project001ViewController.h"
#implementation Project001ViewController
-(ArrayOfWords *) advs
{
if(!advs){
advs = [[ArrayOfWords alloc] init];
NSString* advpath = #"/WordLists/adverbs.txt";
NSLog(#"1");
[[self advs] populateListOfWords:advpath];
}
return advs;
}
-(ArrayOfWords *) adjs
{
if (!adjs) {
adjs = [[ArrayOfWords alloc] init];
NSString* adjpath = #"/WordLists/adjectives.txt";
[[self adjs] populateListOfWords:adjpath];
NSLog(#"2");
}
return adjs;
}
- (IBAction)generate:(UIButton *)sender;
{
//int randy = arc4random() % 11;
//NSNumber* num= [NSNumber numberWithInteger:randy];
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
//NSLog(#"%#",thePhrase);
[display setText:#"Hi"];
}
#end
I'm having trouble on the last NSLog line:
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
Instead of getting the two randomly selected words (using arc4random() to produce them) the array returns Null. But I know FOR CERTAIN. That the array's are not empty because the NSLog Line where I print [adjs size] and [advs size] I get the correct sizes of the list of words. I just want to know what is causing them to print Null here.
populateListOfWords, randomItem, and size methods:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
-(NSString*) randomItem{
//returns random object in list
return (NSString*)[list objectAtIndex:(arc4random() % (int)length)] ;
}
-(int) size{
//returns size of list
return (int)length;
}
(If more code is needed let me know and thank you in advanced for any and all help).

I believe there is a problem with the paths. It is impossible to have access to the path /WordLists/adjectives.txt in iOS due to the application sandbox. I suggest you add these files to the application by dragging and dropping them onto the project. You can get the file paths for resources in application bundle using
NSString * path = [[NSBundle mainBundle] pathForResource:#"adjectives" ofType:#"txt"];
Now pass this path to the method populateListOfWords:.
Because of the incorrect path, I believe wordListString is nil and everything else follows to be that.
Another thing is that int and NSNumber are not toll free bridged like NSStrings and other foundation objects. So
length=(NSNumber*)([words count]);
is incorrect. I suggest you define length as int or better NSUInteger to match the type count method returns.

This method is the problem:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
It wasn't actually putting the words in a list that anyone else could access. I had to just modify it like so:
- (void) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
list = words;
length=(int)([words count]);
}
Now it gives me the correct output. But for some reason when I press the button twice it crashes. Oh well that's a new problem. Thanks again for all the help.
UPDATE
Turns out advs and adjs were being released so the second go around it was trying to access a nil value because when I call [self advs] [self adjs] the pointers exist, but their contents do not. I had to go back and refill them each time basically removing the if (!advs) and if (adjs) parts. It now works as intended.

Related

load imagesĀ into ikimagebrowser from a folder?

I'm new to cocoa and I have an IKImageBrowserView and that's how I load images to it:
- (IBAction)loadImages:(id)sender
{
NSMutableArray *urls = [[NSMutableArray alloc]init];
int i = 1;
for (i=1; i<55; i++) {
NSString *photoNumber = [NSString stringWithFormat:#"%i", i];
NSMutableString *urlString = [[NSMutableString alloc] initWithString:#"Australia"];
[urlString appendString:photoNumber];
NSURL* url = [[NSBundle mainBundle] URLForImageResource:urlString];
[urls addObject:url];
}
[self addImagesWithPaths:urls];
}
- (void)addAnImageWithPath:(NSString *)path
{
myImageObject *p;
/* add a path to our temporary array */
p = [[myImageObject alloc] init];
[p setPath:path];
[_importedImages addObject:p];
}
- (void)addImagesWithPath:(NSString *)path recursive:(BOOL)recursive
{
NSInteger i, n;
BOOL dir;
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&dir];
if (dir)
{
NSArray *content = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
n = [content count];
// parse the directory content
for (i=0; i<n; i++)
{
if (recursive)
[self addImagesWithPath:[path stringByAppendingPathComponent:[content objectAtIndex:i]] recursive:YES];
else
[self addAnImageWithPath:[path stringByAppendingPathComponent:[content objectAtIndex:i]]];
}
}
else
{
[self addAnImageWithPath:path];
}
}
/* performed in an independant thread, parse all paths in "paths" and add these paths in our temporary array */
- (void)addImagesWithPaths:(NSArray *)urls
{
NSInteger i, n;
n = [urls count];
for ( i= 0; i < n; i++)
{
NSURL *url = [urls objectAtIndex:i];
[self addImagesWithPath:[url path] recursive:NO];
}
/* update the datasource in the main thread */
[self performSelectorOnMainThread:#selector(updateDatasource) withObject:nil waitUntilDone:YES];
}
Now my images are loaded by name - #"Australia". That's kind of inconvenient as your images need to have same name and a number. How do I load images with different names from the folder, which has been imported to xcode?
So at the moment I'm loading images by name Australia1, Australia2, Australia3, Australia4... and so on.
How do I load images from a bundle folder?
Your data source needs to return items to the image browser view that conform to the IKImageBrowserItem protocol. Your myImageObject class is a good place to start with that.
In that protocol, three methods are required:
imageUID: Returns a string that uniquely identifies this item (image).
imageRepresentationType: Returns a constant string that identifies how imageRepresentation represents the image.
imageRepresentation: Returns an object that represents the image.
For a start, I'd just use the path that you're already giving every myImageObject. You can use that as both the identifier string and the image representation.
Depending on what else you're doing in this app, you may find it advantageous later on, for memory and/or speed reasons, to load each image yourself. If, after profiling, you do come to that conclusion, you can load the image yourself as an NSImage or CGImage, change your representation type appropriately, and return the image as the representation.
As the data source, you'll return the number of items in your _importedImages array, and, when asked for the item at an index, return that item via objectAtIndex:.
More info:
The IKImageBrowserDataSource protocol reference
The IKImageBrowserItem protocol reference

ObjC: constructing a mutable dictionary in a loop

I feel like I have read many (simple) examples that do exactly what I am trying to do. I just cannot seem to get this to work. I need a second eye on my code, and I don't have anyone around, so pardon me if this seems very simple... The code compiles without a problem. Thank you!
#implementation Engine
- (id) initWithInventory: (NSString *) path {
if (self = [super init]) {
NSString *contents = [NSString stringWithContentsOfFile:#"ingredientList.csv" encoding:NSASCIIStringEncoding error:nil];
NSLog(#"%#",contents); // This yields the contents of the file appropriately
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSRange ingredientRange = {0,96}; // This is done because I want to omit the last element of the array... the 97th is an empty string caused by the end of file newline character. I know it's bad coding...
NSEnumerator *enumerator = [[lines subarrayWithRange:ingredientRange] objectEnumerator];
NSString *curString;
NSArray *ingredientElements;
NSRange activeEffectRange = {1,4}; // Element 0 will be the key, elements 1-4 are the array to be stored.
while (curString = [enumerator nextObject]) {
ingredientElements = [curString componentsSeparatedByString:#","];
Ingredient *theIngredient = [[Ingredient alloc] initWithName:[ingredientElements objectAtIndex:0] andActiveEffects:[ingredientElements subarrayWithRange:activeEffectRange]];
NSLog(#"%#",[theIngredient ingredientName]);
NSLog(#"%#",[theIngredient activeEffects]); //These both print out correctly.
NSString *theName = [theIngredient ingredientName];
[allIngredients setObject:theIngredient forKey:theName];
NSLog(#"%#",[allIngredients objectForKey:[theIngredient ingredientName]]); // ***This yields (null)***
}
}
return self;
}
EDIT: I should add, that allIngredients is an instance variable of the class being initiated, so it is defined properly as an NSMutableDictionary:
#interface Engine : NSObject {
NSMutableDictionary *allIngredients;
}
- (id) initWithInventory: (NSString *) path;
#end
Where are you creating allIngredients? You've declared it, but you haven't allocated it before you use it.
allIngredients = [[NSMutableDictionary alloc] init]

Reading float from a custom file

I want to read some float value one by one from a custom file I defined "player.geo".
player.geo is a file I created using Xcode 4 ("Empty File" from the File > New menu)
I'm currently trying to do it like this:
- (id) initWithGeometryFile:(NSString *) nameOfFile
{
NSFileHandle *geoFile = NULL;
NSString *geoFilePath = [[NSBundle mainBundle] pathForResource:#"player" ofType:#"geo"];
geoFile = [NSFileHandle fileHandleForReadingAtPath:geoFilePath];
if(geoFile == NULL)
{
NSLog(#"Failed to open file.");
}
else
{
NSLog(#"Opening %# successful", nameOfFile);
NSMutableData *fileData = [[NSMutableData alloc] initWithData:[geoFile readDataOfLength:4]];
float firstValue;
[fileData getBytes:&firstValue length:sizeof(float)];
NSLog(#"First value in file %# is %f", nameOfFile, firstValue);
}
return self;
}
I'm not getting the expected value of -64.0, rather I'm getting 0.0.
Is this the right way to go about it?
Do I really have to read the file as a string and then parse float the string contents to get the float value?
NSData objects deal with raw bytes, not strings. If you are typing in a string into a txt file, this will not work. If you are using NSData objects, then you will need to first write the data using the data object methods such as writeToFile:atomically:.
Alternately, you can use the NSString functions stringWithContentsOfFile and componentsSeperatedByString to generate an NSArray containing each string on it's own line, like so:
NSString *tmp;
NSArray *lines;
lines = [[NSString stringWithContentsOfFile:#"testFileReadLines.txt"]
componentsSeparatedByString:#"\n"];
NSEnumerator *nse = [lines objectEnumerator];
while(tmp = [nse nextObject]) {
NSLog(#"%#", tmp);
}

Memory errors when trying to create and populate a NSMutableDictionary

I am not a Cocoa developer, but I have been dabbling in it to build some plugins for PhoneGap. This particular plugin method is either 1) crashing the app without saying why or 2) complaining about how I release/don't release an object. I have tried a ton of things on my end, including using an Enumerator instead of the for loop. If anyone can point me in the right direction, that would be awesome. I don't mind legwork:
- (void)getPreferences:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
NSUInteger argc = [arguments count];
NSString* jsCallback = nil;
if (argc > 0) {
jsCallback = [arguments objectAtIndex:0];
} else {
NSLog(#"Preferences.getPreferences: Missing 1st parameter.");
return;
}
NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSMutableArray *keys = (NSMutableArray *) [options objectForKey:#"keys"];
NSMutableDictionary *values = [[NSMutableDictionary alloc] init];
NSUInteger ky = [keys count];
for (int i = 0; i < ky; i ++) {
#try {
[values setObject:[defaults objectForKey:[keys objectAtIndex:i]] forKey:[keys objectAtIndex:i]];
}
#catch (NSException * err) {
NSLog(#"Error %#", err);
}
}
[keys release];
NSString* jsString = [[NSString alloc] initWithFormat:#"%#(%#);", jsCallback, [values JSONRepresentation]];
[defaults release];
[values release];
[webView stringByEvaluatingJavaScriptFromString:jsString];
[jsString release];
}
Human version:
options contains a dictionary with a single key of "keys"
that key contains an array of strings (that are going to be used as keys for lookup)
I want to loop through that array and
For every value that exists in defaults for that key, copy it to values using the same key
Finally, I want to send that values back as JSON (This part was working when I just passed the entire defaults object in, so I think the JSON method is working)
From your code, it follows that you 'own' objects values and jsString (the ones you created with alloc), so you should release them and not any other.
You can read more on memory management here.
Is this the whole code? Also, what exactly error do you get?
Nikita is right, it looks as though you're overreleasing defaults, which would cause a crash later when the autorelease pool gets released. Also, if I understand what you're trying to do correctly, you could create the values dictionary with a single line of code:
NSDictionary *values = [defaultsDict dictionaryWithValuesForKeys:keys];

ObC : app crashes after returning NSMutableArray?

I am new to ObC and have a problem that i just cant fix. There may be other issues as well but the main issue is this:
Starting the app
Press button = load new view
In the new viewDidLoad i call another object/function and send a NSMutableArray
Process data and send back a NSMutableArray
App crash, see comment where. Most often when i go back and back again but sometimes the first time
As i am new to this i guess i do a lot of this wrong but could someone nice take a look at the code and give me some advice. I would assume i have problem with releasing something.
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#" ");
NSLog(#"viewDidLoad ");
NSLog(#" ");
NSLog(#">>Processing prepareGame<<");
NSMutableArray *propArray1 = [[NSMutableArray alloc] initWithObjects:#"9999", nil]; //Init with dummy numbers
AccessPropertiesFile *readMyProperties = [AccessPropertiesFile new]; //Init function call to read file
NSLog(#"Prepare to call readProperties");
propArray1 = [readMyProperties readPropertiesFile:propArray1];
NSLog(#"Back from readProperties:error after this");
/*
for (NSString *element in propArray1) {
NSLog(#"Elements in prop2Array; %#", element);
}
*/
[readMyProperties release];
[propArray1 release];
}
-(NSMutableArray *)readPropertiesFile:(NSMutableArray *)readDataArray {
NSLog(#"Processing readProperties");
// For error information
NSError *error;
//Prepare File Manager
NSString *filePath = [self dataFilePath];
NSFileManager *fileMgr;
fileMgr = [NSFileManager defaultManager];
NSArray *propertiesArray = [NSArray alloc]; //Alloc array
//Check from what module the call is coming from to ecide what to do
if ([fileMgr fileExistsAtPath: filePath] == NO) {
NSLog (#"File not found");
//File does not exists, this is the first time the game starts
//Set up default parameters
NSString *fileString =#"0\n30\n30\n10\n1\n1\n1\n2\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n";
// Write default parameters to file
[fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
propertiesArray = [fileString componentsSeparatedByString:#"\n"]; // each line, adjust character for line endings
}
else { //File exists
NSLog (#"File exists");
NSString *fileString = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding error:nil]; // reads file into memory as an NSString
propertiesArray = [fileString componentsSeparatedByString:#"\n"]; // each line, adjust character for line endings
}
//Clean readDataArray
[readDataArray removeAllObjects];
//Populate return array
for (NSString *element in propertiesArray) {
//NSLog(#"Elements in propertiesArray; %#", element);
[readDataArray addObject:element];
}
NSLog(#"readDataArray: %#", readDataArray);
[propertiesArray release];
[readDataArray autorelease];
NSLog(#"returning from readProperties");
return readDataArray;
}
#end
You are over-releasing readDataArray (known as propArray1 in the method that didn't create it). You create it and autorelease it in your second method, then you release it again at the end of your first method (where it wasn't created).
I suggest you use Analyze feature that comes with latest XCode. It is a good feature that I always use to track if I forget to release or release too much.
I also spotted that you also over-release the propertiesArray because it contains the result from [fileString componentsSeparatedByString:], which will be autorelease according to Cocoa convention.