I realize this question is pretty basic, but I'm really stuck. I have a plist. I'm trying to read that into an array so I can work with it in various classes. So in one class I have:
+ (NSArray*)questionArray
{
static NSArray* questions = nil;
if(!questions)
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"Questions" ofType:#"plist"];
NSDictionary *dic = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *questionsArray = [dic objectForKey:#"groups"];
NSMutableArray *questionObjects = [[NSMutableArray alloc] initWithCapacity: [questionsArray count]];
for(NSDictionary* questionDic in questionsArray)
{
QuestionContainerObject* object = [[self alloc] initWithDictionary:questionDic];
[questionObjects addObject:object];
[object release];
}
questions = questionObjects;
[dic release];
}
return questions;
}
I want to be able to access the things I pull out of the array from another class. I tried calling it like NSString *str = [QuestionContainerObject questionArray]; from my other class (after importing the header) but I get the 'class method +questionArray not found' warning.
Can someone please point me in the right direction? I'm really lost! Thanks!!
The warning is because the compiler does not know the questionArray method exists.
define this method in the header (QuestionContainerObject.h)
#interface QuestionContainerObject
+ (NSArray*)questionArray;
#end
and in the file using it:
#import QuestionContainerObject.h
Also remember to release objects created using [class alloc]
Did you put the class method declaration in the header file?
Btw, move the static NSArray* questions = nil; outside of the method body and remove the = nil, or else its not going to have the effect you wanted, i think.
Related
I´m doing an app and I can´t get a mutable array to accept objects. I´v tried setting breakpoints to see what´s happening but it keeps saying that the mutable array is nil. Does anyone has an answer?
My code:
- (void)save:(id) sender {
// All the values about the product
NSString *product = self.productTextField.text;
NSString *partNumber = self.partNumberTextField.text;
NSString *price = self.priceTextField.text;
NSString *quantity = self.quantityTextField.text;
NSString *weigh = self.weighTextField.text;
NSString *file = [self filePath];
//Singleton class object
Object *newObject = [[Object alloc] init];
newObject.product = product;
newObject.partNumber = partNumber;
newObject.price = price;
newObject.quantity = quantity;
newObject.weigh = weigh;
//Array declaration
mutableArray = [[NSMutableArray alloc]initWithContentsOfFile: file];
[mutableArray addObject:newObject];
[mutableArray writeToFile:file atomically:YES];
}
While initWithContentsOfFile: can be called on an NSMutableArray, it was inherited from NSArray. The return value is an NSArray which is not mutable. If you want to add objects to your mutable array, you have to do something like this:
mutableArray = [[[NSMutableArray alloc] initWithContentsOfFile: file] mutableCopy];
[mutableArray addObject:newObject];
[mutableArray writeToFile:file atomically:YES];
Now, the addObject: call should work.
Best regards.
[NSMutableArray initWithContentsOfFile:] returns nil by default if the file can't be opened or parsed. Are you sure the file you're loading exists and is formatted correctly?
Try to check with break point on
mutableArray = [[NSMutableArray alloc]initWithContentsOfFile: file];
Line. Move your cursor on mutableArray if it shows you __NSArrayI that means it is an immutable array i.e. you cant update it and if it shows you __NSArrayM that means it is a mutable array and you can update this array.
In your case you're getting immutable array thats why you cant update it.
So you have two way to get mutable Array from this file -
Method:1
mutableArray = [[[NSMutableArray alloc] initWithContentsOfFile: file] mutableCopy];
Method:2
NSArray *anyArray = [[NSArray alloc]initWithContentsOfFile: file];
mutableArray = [[NSMutableArray alloc]initWithArray:anyArray];
In both case mutableArray woud be a mutable Array. You can update it.
I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.
I'm new to iPhone development and have had great success with with answers from here so I am hoping to receive help directly. I am reading data into a tableview from a plist. The application works fine but I get 2 warnings when I compile. I know why I get the errors but I have been unsuccessful with resolving the issues. Although this app works I really would like to resolve the warnings efficiently. When I tried changing the NSDictionary to NSArray the warning goes away but the table is no longer populated.
Any help would be greatly appreciated.
Staff and Data are defined as NSArray in the Delegate .h file. The warnings show in the delegate .m file below.
My Delegate has the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *DataPath = [Path stringByAppendingPathComponent:#"Data.plist"];
NSString *SPath = [[NSBundle mainBundle] bundlePath];
NSString *StaffPath = [SPath stringByAppendingPathComponent:#"Staff.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
**self.data = tempDict;**
[tempDict release];
NSDictionary *staffDict = [[NSDictionary alloc]initWithContentsOfFile:StaffPath];
**self.staff = staffDict;**
[staffDict release];
In my staff ViewController I have the following:
if(CurrentLevel == 0) {
//Initialize our table data source
NSArray *staffDict = [[NSArray alloc] init];
self.tableDataSource = staffDict;
[staffDict release];
Midwest_DigestiveAppDelegate *AppDelegate = (Midwest_DigestiveAppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
}
else
self.navigationItem.title = CurrentTitle;
An NSArray holds a one dimensional list of items where an NSDictionary maps keys to values.
Array:
[a, b, c]
Dictionary:
{#"a" = #"first item", #"b" = #"second item"}
Could you declare data as NSDictionary *data; and populate it as data = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
You then access values in the dictionary with [data valueForKey:#"key"]
Everything in your code suggests that the staff and data properties are NSDictionary instances. You initialize them to dictionary objects and you reference them as dictionary objects. Why then are you declaring them as NSArray objects?
You should change how they are declared so they are NSDictionary in your header file rather than NSArray. That seems to me the most logical way to remove your warnings.
This should still work assuming the contents of your "staff" NSDictionary has a key named "Rows" whose value is an NSArray. The code you have to initialize self.tableDataSource with an empty NSArray seems redundant, as you immediately overwrite the value with the
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
line in your code
After a ASIFormDataRequest , i create a temporary NSMutableArray *resultArray from the JSON then add it to a defined NSMutablearray *myData
-(void)viewDidLoad{
myData = [[NSMutableArray alloc] init];
//request that calls gotInfo method
}
-(void)gotInfo:(ASIFormDataRequest *)request{
NSString *responseString = [request responseString];
NSMutableArray *resultArray = [responseString yajl_JSON];
[myData addObject:resultArray];
}
-(IBAction)doSomethingWithData:(id)sender{
//something with myData
}
but when i try to call myData from outside of the gotInfo: method, i get bad access errors and when i inspect myData outside of the method, it shows a kern_protection_failure. So i'm guessing that outside of the method, the resultArray is obviously released, but it's also released from myData since the object inside myData is sharing the same memory location?
I also tried
-(void)gotInfo:(ASIFormDataRequest *)request{
NSString *responseString = [request responseString];
[myData addObject:[responseString yajl_JSON]];
}
How do I preserve myData??
in my header file:
#import <UIKit/UIKit.h>
#class ASIFormDataRequest;
#interface EventsTableController : UITableViewController <UITableViewDataSource>{
NSMutableArray *myData;
}
-(void)gotInfo:(ASIFormDataRequest *)request;
UPDATE:
so in the gbd, the myData is allocated as 0x5e96560 so i did
po 0x5e96560
and then i get the EXC_BAD_ACCESS with the reason being KERN_PROTECTION_FAILURE at address: 0x00000009
but if i do
po [[0x5e96560 objectAtIndex:0] objectForKey:#"key"]
then i get the value! whyyyyyy?
#property(nonatomic,retain) NSMutableArray *myData
and create the object
self.myData = [[NSMutableArray alloc] init];
and
// and i assume your resultArray is a mature NSMutableArray object
[self.myData addObject:resultArray];
The best way of using copy I can think of, is to always set NSString properties to "copy" instead of retain. That way you get more accurate readings from the Leaks instrument if you mess up and forget to release a string an object is holding onto. Other uses of copy need to be more carefully thought out.
NOTE : You are responsible to release myData after no use of that variable.
You dont really have any way to correctly access myData as you declare it as a member inside of EventsTableController, but you dont set the #property for it, and do not synthesize it either. By synthesizing it in your EventsTableController.m file you are telling xcode to generate the getter/setters you need to correctly touch myData, which is where your program seems to be failing. If you do this, this should solve your problem.
-Karoly
Except for the different name of your ivar (mienVar vs. myVar), I don't see a problem. Some other code must be releasing your ivar, or you are accessing it before viewDidLoad has the opportunity to actually create the array (I bet it is the latter).
I think you should put the code in viewDidLoad in your initialization method instead. Don't forget to release the array in dealloc.
You could, of course, also write your own myData getter method, doing lazy initialization, instead of creating it in the init method:
- (NSMutableArray *) myData
{
if (!myData)
myData = [[NSMutableArray alloc] init];
return myData;
}
Note that now, you should access self.myData if you want to use it.
I think the NSString yajl_JSON category can return an array or a dictionary - you might need to inspect the type of the result array on the line below as it may be an NSDictionary:
NSMutableArray *resultArray = [responseString yajl_JSON];
IF you are treating it as an array when its a dictionary that might be causing your problems.
(relevant code from the NSObject+YAJL category below)
YAJLDocument *document = [[YAJLDocument alloc] initWithData:data parserOptions:options error:error];
id root = [document.root retain];
[document release];
return [root autorelease];
(and in YAJLDocument object)
#interface YAJLDocument : NSObject <YAJLParserDelegate> {
(id root_; // NSArray or NSDictionary
i want to populate the tablecell with title and imageurl from xml list.
i manage to store the title (NSMutableDictonary *sections )and imageURL (NSMutableDictonary *sectionsImg) into 2 NSMutableDictionary respectively.
/*******This is in viewDidLoad***/
Directory *allDirectory = [appDelegate.directories objectAtIndex:0];
for (allDirectory in appDelegate.directories)
{
NSDictionary *dica = [NSDictionary dictionaryWithObject:allDirectory.dirTitle forKey:#"dirTitle"];
NSDictionary *dico = [NSDictionary dictionaryWithObject:allDirectory.imageURL forKey:#"imageURL"];
[dirName addObject:dica];
[dirImage addObject:dico];
//NSLog(#"dic of items : %#",dirImage);
}
for (allDirectory in appDelegate.directories)
{
//retrieve the first letter from every directory title (dirTitle)
NSString * c = [allDirectory.dirTitle substringToIndex:3];
NSString * m = allDirectory.imageURL;
found = NO;
find = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
for (NSString *stra in [self.sectionsImg allKeys])
{
if([stra isEqualToString:m])
{
find = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc]init] forKey:c ];
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
if (!find)
{
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
}
for (NSDictionary *directory in dirName)
{
[[self.sections objectForKey:[[directory objectForKey:#"dirTitle"] substringToIndex:3]] addObject:directory];
//NSLog(#"hehehe have : %#",sections);
}
for (NSDictionary *directoryImg in dirImage)
{
//[[self.sectionsImg objectForKey:[[directoryImg objectForKey:#"imageURL"] substringFromIndex:0]] addObject:directoryImg];
[[self.sectionsImg objectForKey:[directoryImg objectForKey:#"imageURL"]] addObject:directoryImg];
//NSLog(#"HOHOHO have : %#",sectionsImg);
}
And on cellForRowAtIndexPath i declare a dictionary
NSDictionary *dictionary = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [dictionary objectForKey:#"dirTitle"];
but when i tried to declare a dictionary for imageURL
NSDictionary *dictionaryImg = [[self.sectionsImg valueForKey:[[[self.sectionsImg allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
it gives me a error :
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
any idea why? the logic is supposed to be the same where xml title and url can be retrieve and be displayed. Title is retrievable but imageURL is not. Help is deeply appreciated !
You are trying to sort an array... except for the fact your array isn't an array, but a NSDictionary.
Your code isn't the best at the moment. Your getting the idea of Dictionaries wrong and may be confusing them with arrays, so my best guess is your quite new to programming into objective-c.
You have two lists of things, if I'm not mistaken. The first list is the list of names, and the second list is an image corresponding with that name.
Below I'm going to do two things:
Firstly, I'm giving you two ways on how to fix your problem. It has a sample code included and gives you a small explanation with it. The possibility exist you don't understand parts of what I describe. In that case, you should;
Check out the link I described below the two solutions. It has a tutorial which makes you understand everything about arrays, dictionaries, tables and, as a bonus, XML-parsing.
So, in my opinion, you can do two things:
The first one is using an array of NSDictionaries. You'd be using a code which looks like:
NSMutableDictionary *itemOne = [[NSMutableDictionary alloc] init];
NSMutableDictionary *itemTwo = [[NSMutableDictionary alloc] init];
NSMutableArray *listOfAll = [[NSmutableArray alloc] init];
NSString *itemOneName = [[NSString alloc] initWithFormat:#"This is picture 1"];
NSString *itemTwoName = [[NSString alloc] initWithFormat:#"This is picture 2"];
NSData *imageOneData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic1.jpg"]];
NSData *imageTwoData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic2.jpg"]];
UIImage *itemOneImage = [UIImage imageWithData: imageOneData];
UIImage *itemTwoImage = [UIImage imageWithData: imageTwoData];
[itemOne setObject:itemOneNameString forKey:#"Name"];
[itemOne setObject:itemOneImage forKey:#"Image"];
[itemTwo setObject:itemTwoNameString forKey:#"Name"];
[itemTwo setObject:itemTwoImage forKey:#"Image"];
[listOfAll addObject:itemOne];
[listOfAll addObject:itemTwo];
Anything can be filled using that array. Just use something with a for-loop to iterate through your array.
for (int i = 0; i < [listOfAll count]; i++)
{
NSMutableDictionary *currentItem = [[NSMutableDictionary alloc] initWithDictionary:[listOfAll objectAtIndex:i]];
//Do something with that current item
}
You can also use that index in your tableView. In that case, you have to use your variable section instead of i to get your desired index.
The second one is using two arrays. Imagine you get an image named imageOne with the text imageName. Then you should use:
NSMutableArray *nameList = [[NSMutableArray alloc] init];
[nameList addObject: imageName];
NSMutableArray *imageList = [[NSMutableArray alloc] init];
[imageList addObject: imageOne];
If you want to use a certain item out of those lists, you just have to use the same indexnumber.
For example:
[theTitleLabel setText:[[NSString alloc] initWithFormat:#"%#", [nameList objectAtIndex:x]]];
[theImageView setImage:[imageList objectAtIndex:x]];
Make sure the x's are the same number.
I understand this is all a lot of information, especially if you're new to Objective - C. A tutorial exists which gives you a lot of information about how to use arrays, dictionaries and table views. As a bonus, you get to know a little about XML-parsing.
I suggest you walk through that tutorial and do everything and read everything it says. This should give you a nice start into the world of programming in iPhones.
Good luck!