I am trying to addObject to a NSMutableArray once the user taps the Add to Favorites button I can get the data into a NSDictionary, but when I pass the NSDictionary to the Array the NSLog comes back with nil. Am I missing something?
-(IBAction) addtofavorites: (id)sender
{
NSArray *key = [NSArray arrayWithObjects:#"Title", #"Description", nil];
NSArray *objects = [NSArray arrayWithObjects:CurrentTitle, description.text, nil];
NSDictionary *fadd = [[NSDictionary alloc] initWithObjects:objects forKeys:key];
FavoritesViewController *fvc = [[FavoritesViewController alloc] init];
[fvc.favorites addObject:fadd];
[FavoritesViewController release];
}
What I would do is in your header file create an instance variable for your FavoritesViewController that you retain. Then use lazy init when the addtofavorites is pressed. Something like below
if (!detailViewController)
{
self.fvc = [[FavoritesViewController alloc] init];
}
[self.fvc.favorites addObject:fad];
Then just release the FavoritesViewController object in dealloc
- (void)dealloc {
[fvc release];
[super dealloc];
}
FavoritesViewController *fvc = [[FavoritesViewController alloc] init];
[fvc.favorites addObject:fadd];
[FavoritesViewController release];
doesn't look right. You should already have a FavoritesViewController initialized (in which can just access it and call -addObject:)...
EDIT
First, how does the current view controller (the one that has -addtofavorites: defined on it) relate to FavoritesViewController? How would the user navigate between these two view controllers?
Second, would the list of favorites persist across different runs of the app? If so, how do you plan to save/restore favorites?
Third, rather than add favorites to a view controller, you would probably do better to add them to an underlying 'model' that would be used to populate the FavoritesViewController.
Separating the 'model' from the 'view' can be very powerful, while also simplifying your code.
Related
I'm really frustrated after spending like three hours googling around to solve this problem!! It's probably an easy solution to it aswell.
I'm creating a really simple TableView app for the iPhone. It's supposed to fetch an XML-document and parse it (already fixed) and then put the data into objects called HPobject!
One HPobject represents one day of data from the XML-file. Anyhow!
Then I want the object to be stored in a NSMutableArray so I can grab it later when I'm creating the table rows. But I can't access it! My NSMutableArray is ALWAYS null! No matter what I do!
Here's my code:
//THE .h FILE
#import "TBXML.h"
#import "HPobject.h"
#interface RootViewController : UITableViewController {
NSMutableArray *listOfItems;
}
- (void)traverseElement:(TBXMLElement *)element;
//THE .m FILE
#import "RootViewController.h"
#import "HPobject.h"
#implementation RootViewController
- (void)viewDidLoad
{
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
TBXML * tbxml = [[TBXML tbxmlWithURL:[NSURL URLWithString:#"http://www.hpprov.se/istasts.php?q=xxxxxxx"]] retain];
if (tbxml.rootXMLElement)
[self traverseElement:tbxml.rootXMLElement]; //Works fine!
[tbxml release];
[super viewDidLoad];
}
- (void) traverseElement:(TBXMLElement *)element {
do {
//Lots of parsing code which all works fine and gets me the variables up next!
HPobject *currentObject = [[HPobject alloc] init];
currentObject.antalRegistrerade = numRegistrerade;
currentObject.inkomstBrutto = numBrutto;
currentObject.inkomstNetto = numNetto;
[listOfItems addObject:currentObject];
NSLog(#"Array: %#", listOfItems); //RETURNS null!
NSLog(#"Reg: %#, Net: %#, Brutt: %#", currentObject.antalRegistrerade, currentObject.inkomstNetto, currentObject.inkomstBrutto); //Returns the correct values!
NSLog(#"%d stycken!", listOfItems.count); //Returns 0!! :(
[currentObject release];
} while ((element = element->nextSibling));
}
You are defining listOfItems locally in viewDidLoad and then you try to access that in another method.
Make sure you are using an instance variable defined in your interface definition (header).
replace this line in viewDidLoad:
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
with
listOfItems = [[NSMutableArray alloc] init];
I suppose that listOfItems is an ivar. So to fix your problem change this:
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
to this
listOfItems = [[NSMutableArray alloc] init];
Take a look at the scope of your array. You have created that as a variable of another method. It will not visible in others. Make an instance var.
I've got a tableView in a view which is a subview to my MainViewController. When the subview is loaded, I have this in my viewDidLoad method to initialize my tableData:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.listData = array;
[array release];
[super viewDidLoad];
Then I use other methods to add to this data within this view.
When I switch back to my other view, however, then bring up this view again, it initializes the data all over again, erasing my changes. How can I fix this problem?
EDIT:
I tried to initialize this array in MainViewController by doing this:
NSMutableArray *array = [[NSMutableArray alloc] init];
HistoryViewController.listData = array;
[array release];
But it says Accessing unknown 'setListData:' class method.
you should not be initializing your array in view did load
// this is clearing out all of your data
NSMutableArray *array = [[NSMutableArray alloc] init];
self.listData = array;
There is not much code, but I would suggest initializing the listData property when you initialize the ViewController.
I might suggest you review this tutorial here on UITableView, and your code above should be edited
HistoryViewController.listData = array;
should be
self.listData = array;
When adding objects to an NSArray using "initWithObjects" can anyone confirm for me that the items are retained. I am pretty sure they are, but can't find it mentioned anywhere with regards to initWithObjects?
// CREATE DRINKS
Coffee *drink1 = [[Coffee alloc] initWithName:#"Flat White"];
Coffee *drink2 = [[Coffee alloc] initWithName:#"Cappucino"];
Coffee *drink3 = [[Coffee alloc] initWithName:#"Latte"];
Coffee *drink4 = [[Coffee alloc] initWithName:#"Mocha"];
Coffee *drink5 = [[Coffee alloc] initWithName:#"Hot Chocolate"];
// SET ARRAY
NSArray *tempArray = [[NSArray alloc] initWithObjects:drink_1, drink_2, drink_3, drink_4, drink_5, nil];
[self setCoffeeList:tempArray];
// CLEAN UP
[drink_1 release];
[drink_2 release];
[drink_3 release];
[drink_4 release];
[drink_5 release];
[tempArray release];
[super viewDidLoad];
cheers Gary
initWithObjects retains all items in the array.
initWithObjects: count:
(id) initWithObjects: (id*)objects count: (NSUInteger)count;
Availability: OpenStep
This is a designated initialiser for the class.
Subclasses must override this method.
This should initialize the array with count (may be zero) objects.
Retains each object placed in the array.
Calls -init (which does nothing but maintain MacOS-X compatibility), and needs to be re-implemented in subclasses in order to have all other initialisers work.
Objects are expected to take ownership of the things they need to keep around. An array is responsible for its items, therefore it retains them. See the memory management guide for complete details. (No, seriously, read it. You'll thank yourself later when you don't have to ask this question about every class you use and your program isn't crashing every five seconds.)
I have an NSMutableArray called playlist. This is in a method called getAllPlaylists. The code is something like this:
-(NSMutableArray *)getAllPlaylists
{
//playlist is an instance variable
playlist = [[NSMutableArray alloc] init]; //memory leak here
...
//some code here which populates the playlist array
[playlist addObject: object1];
...
return playlist;
}
The array allocation step of playlist is causing a memory leak. In such a scenario where can i release this array? Or can i avoid allocation n initialization of playlist here by doing something else? Any help will be greatly appreciated!!
2 solutions:
Use autorelease:
- (NSMutableArray*)getAllPlaylists
{
playlist = [[NSMutableArray alloc] init];
...
return [playlist autorelease];
}
or instead of using [[NSMutableArray alloc] init] to create your NSMutableArray object, use [NSMutableArray array] which is equivalent to [[[NSMutableArray alloc] init] autorelease]:
- (NSMutableArray*)getAllPlaylists
{
playlist = [NSMutableArray array];
...
return playlist;
}
You should autorelease newly created objects that you want to return that are not owned by the object (local variables, not instance variables).
playlist = [[[NSMutableArray alloc] init] autorelease];
Alternatively, you can use the convenience method to do that more easily:
playlist = [NSMutableArray array];
For items the object owns (instance variables), you should make sure you release the old value first and implement a dealloc method that also releases the value.
- (NSMutableArray*)getAllPlaylists {
[playlist release];
playlist = [[NSMutableArray alloc] init];
return playlist;
}
- (void)dealloc {
[playlist release];
[super dealloc];
}
For more info, see the memory management guide.
I have an application that has a UITabBarController with two tabs, each having its own navigation controller. Now I want to store the state of the application when the user closes it, so that when the user relauches the application will show the same place as the last time before it was closed.
So, in applicationWillTerminate: I have
[NSKeyedArchiver archiveRootObject:tabBarController toFile:#"lastVisitedTab"];
Then, in applicationDidFinishLaunching: I have
UITabBarController *last= (UITabBarController *)[NSKeyedUnarchiver unarchiveObjectWithFile:#"lastVisitedTab"];
if (last)
tabBarController = [last retain];
I also have an extension to UIImage to make it compliant to NSCoding. However, this doesn't work, as the state is not preserved. The first tab gets selected all the time, and no navigation is preserved either.
Can someone tell me what's wrong, or show me how to do it correctly?
I think it's overkill to persist the actual objects. Instead, just save the selectedIndex property (use [NSNumber numberWithInt: tabBar.selectedIndex]) and then read it back and set the property on launch. Maybe this doesn't properly answer your question, but it might be sufficient for what you are trying to achieve.
I figured out how to do it finally, thanks to Felixyz's idea. Below is what I have to do to store tabs, regardless of their data. If, says, a view is loaded with data downloaded from an URL, store the URL instead of the whole view. You would have to override
- (void)encodeWithCoder:(NSCoder *)encoder
- (id)initWithCoder:(NSCoder *)decoder
in your UIViewController subclass to tell the view controller to save appropriate data before the application stops.
Now in your application delegate save the data before quiting
- (void)applicationWillTerminate:(UIApplication *)application
// data buffer for archiving
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
// the index of selected tab
[archiver encodeInt:tabBarController.selectedIndex forKey:#"TAB_INDEX"];
// array of keys for each navigation controller, here I have 3 navigation controllers
NSArray *keys = [NSArray arrayWithObjects:
#"NAVIGATION_CONTROLLER_1",
#"NAVIGATION_CONTROLLER_2",
#"NAVIGATION_CONTROLLER_3", nil];
for (int i = 0; i < keys.count; i++) {
UINavigationController *controller = [tabBarController.viewControllers objectAtIndex:i];
NSMutableArray *subControllers = [NSMutableArray arrayWithArray:controller.viewControllers];
// the first view controller would already be on the view controller stack and should be removed
[subControllers removeObjectAtIndex:0];
// for each of the navigation controllers save its view controllers, except for the first one (root)
[archiver encodeObject:subControllers forKey:[keys objectAtIndex:i]];
}
[archiver finishEncoding];
// write that out to file
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[data writeToFile:[documentsDirectory stringByAppendingPathComponent:#"ARCHIVE_PATH"] atomically:YES];
}
And then, when relaunching
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// set up the tabs
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = [NSArray arrayWithObjects:
[[[UINavigationController alloc] initWithRootViewController:rootViewController1] autorelease],
[[[UINavigationController alloc] initWithRootViewController:rootViewController2] autorelease],
[[[UINavigationController alloc] initWithRootViewController:rootViewController3] autorelease], nil];
// look for saved data, if any
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSData *archive = [NSData dataWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:#"ARCHIVE_PATH"]];
// if no data found, skip this step
if (archive) {
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:archive];
// set the tab
tabBarController.selectedIndex = [unarchiver decodeIntForKey:#"TAB_INDEX"];
NSArray *keys = [NSArray arrayWithObjects:
#"NAVIGATION_CONTROLLER_1",
#"NAVIGATION_CONTROLLER_2",
#"NAVIGATION_CONTROLLER_3", nil];
// push view controllers up the stack
for (int i = 0; i < keys.count; i++) {
NSArray *controllers = [unarchiver decodeObjectForKey:[keys objectAtIndex:i]];
for (UIViewController *controller in controllers) {
[((UINavigationController *)[tabBarController.viewControllers objectAtIndex:i]) pushViewController:controller animated:NO];
}
}
}
// Add the tab bar controller's current view as a subview of the window
[window addSubview:tabBarController.view];
}