iOS - NSMutableDictionary reverting to previous state? - ios

I'm trying to make a "Delete" button on an "Edit Item" page, which when pressed will go back and remove the relevant entry in an NSMutableDictionary on the previous page, and then update the tableview list of entries.
It starts with an IBAction method on the "Edit Item" page, which basically does nothing but feed the key to be deleted back to the controller for the previous page:
- (IBAction)deleteItem:(id)sender {
[_parentController removeItemWithDeleteButton:[_mainFactoidTextField text]];
}
which is where the action actually starts (on the list page):
- (void)removeItemWithDeleteButton:(NSString *)key {
[_currentItemsDict removeObjectForKey:key];
[self.navigationController popViewControllerAnimated:YES];
}
At the start of that method, _currentItemsDict has 8 objects, which drops to 7 after removeObjectForKey: is called. Then it pops off the top ViewController, which is the "Edit Item" page, returning us to the list of entries.
Then when that method is done, the breakpoint immediately jumps to:
- (void)viewWillAppear:(BOOL)animated {
NSArray *tempArray = [NSArray arrayWithArray:[_currentItemsDict allKeys]];
NSArray *sortedArray = [tempArray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
_currentItemsArray = [NSMutableArray arrayWithArray:sortedArray];
[_tableView reloadData];
}
But by the first line of viewWillAppear:, _currentItemsDict has reverted back to 8 objects. Everything works fine from then on, but it's all working with the original 8 entries, meaning that nothing got deleted.
From my limited experience with these sorts of things (I'm definitely still a beginner), I'm going to guess this has something to do with the popViewControllerAnimated: method, but I cannot figure out what. I've heard distantly of certain variables being stored in different ways that might make them come back as an earlier version of themselves, but I haven't wrapped my mind around the concept. Or maybe it's something else entirely, I don't know. All I can tell is that _currentItemDict has 7 objects, and then one line later it has 8 again.
Can anyone help out a new guy, and explain where I'm going wrong? Or if it's easier, can you suggest a better solution for removing an entry in an NSMutableDictionary from a different ViewController? (Code samples and tutorial links are much appreciated, as I'm currently just noobish enough to sometimes be unable to translate theory into practice on my own!)
Thank you in advance for your help!

Do you know how properties work in Objective-C 2.0? I would declare a property in your parentViewController to contain your _currentItemsArray, declared as a strong reference.
#property (nonatomic,strong) NSMutableArray *_currentItemsArray;
Also, set a weak property in your childViewController (the one you push to for deletion)
#property (nonatomic, weak) NSMutableArray *parentItemsArray;
Set the property on the childViewController upon push
childViewController.parentItemsArray = self._currentItemsArray;
[self.navigationController pushViewController:childViewController animated:YES];
Remove the item inside the array within childViewController instead of calling a method on your parentController
[self.parentItemsArray removeObjectAtIndex:indexOfKey];
Doing this, you don't have to sort the array again in viewWillAppear. Instead, just ask the tableView to reloadData
Does any of this make sense?
It also may be wise to include your logic inside cellForRowAtIndexPath, numberOfRowsInSection and the other tableViewDataSource methods.

Related

proper way to push view controller on didSelectRowAtIndexPath

I am working with an application in which there is a table view controller. When the user clicks on the cell, I want to display specifics for that cell in a separate view controller. My first attempt at doing this was the following:
CellViewController * cvc = [self.storyboard instantiateViewControllerWithIdentifier:#"cellVC"];
[cvc.name setText:fName];
[cvc.homeNum setText:hNum];
[cvc.mobileNum setText:mNum];
[self.navigationController pushViewController:cvc animated:NO];
However, this produces a view controller without the updated values fName, hNum, and mNum. I guess I have 2 questions:
1) Is there a strong difference in terms of good practice for instantiateViewControllerWithIdentifier vs. PerformSegueWithIdentifier. (I'm using the former currently because I just don't like segues as much)
2) If instantiateViewControllerWithIdentifier is an okay way of doing it, why are those values not being updated? Instead the raw 'Label' just shows up
Note: I have looked at performSegueWithIdentifier vs instantiateViewControllerWithIdentifier And I believe the question is slightly different/they didn't really answer my question. They also seem to claim the former way is bad practice but I have seen other cases where people used that way, so I am confused.
Thanks
The problem that contols, like cvc.name is not initialized, because after instantiating view controller is not loaded.
Declare properties in CellViewController, like:
#property (nonatomic, strong) NSString * nameString;
and in CellViewController.m
- (void) viewDidLoad {
[name setText:_nameString];
}

Creating TableviewController and presenting it

I'm developing IOS messanger app, I have inbox(tableview) in which I have cells(conversations) and when I select a conversation, I would like to present this conversation(tableviewController full of messages), but i dont like how much time it takes to present this controller. So my idea was to create whole controllers(tableviewController full of messages) objects before selecting conversation, and then just push them. First time I select conversation, it is blank, after going back and then selecting it again, it work. Problem is obvious, some variables are initialized in viewDidLoad method. I have tried to move them to init method but then every time conversation was blank.
Do you have any experiences with this? Any hint will be appreciated a LOT.
Thank you!!!
in tableviewController full of messages:
.h file:
#property (nonatomic, assign) BOOL firstAppear;
.m file
self.firstAppear = NO; //in init method
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
if (self.firstAppear) {
//add a indicator view here
}
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (self.firstAppear) {
//get tableView data here, then [tableView reloadData] to show data
//remove the indicator
self.firstAppear = NO;
}
}
It sounds to me like you are doing premature optimization. Creating and pushing a table view controller should take a small fraction of a second. If it's taking longer, something is wrong. Are you loading the conversation data from a remote server or something?
You might want to use Instruments to figure out what is taking extra time and causing a delay. Then you can focus on the actual cause rather than guessing.

Is this code over releasing?

I can´t find where the problem is, but this code is crashing. Am I over releasing any object?
settings = [[SettingsViewController alloc] initWithNibName:#"SettingsController" bundle:nil];
settings.hidesBottomBarWhenPushed = YES;
NSArray * arrayWithRootController = [[NSArray alloc] initWithObjects:settings, nil];
[(UINavigationController*)([self.tabBar.viewControllers lastObject])setViewControllers:arrayWithRootController];
[arrayWithRootController release];
[settings release];
If I remove the line
[settings release];
The app doesn´t crash. but I am pretty sure its correct. May the problem be in another place?
Any ideas? Thanks a lot
You're not overreleasing in this snippet, but obviously something isn't right. Adding the root view controller to the array will retain it, but only for the life of the array. When the array dies, all the objects within it are released as well (my guess as to what's happening here).
Couple things to try:
First, make sure you're putting your array where you think you are:
[(UINavigationController*)([self.tabBar.viewControllers lastObject])setViewControllers:arrayWithRootController];
What do you expect [self.tabBar.viewControllers lastObject] to be? Are you sure this is where you want to be assigning your new array? If the receiver is invalid, your array won't be retained in your next line which means the view controller will also be released.
I'm not familiar with your architecture, but it appears you're assigning an array of view controllers to a view controller. self.tabBar is a navigation controller, and you can call setViewControllers on it. But self.tabBar.viewControllers lastObject... well presumably that's a view controller, but not neccessarily a navigation controller, it may not respond to setViewControllers (in which case it should crash, unless it's nil, which I'm guessing it might be).
Finally, try using the Instruments tool Zombies to see if you can pinpoint where the unintended release is coming from.

App hangs when property is included in block

I have a UIViewController which displays a table of data that is pulled from an online database. I have a singleton manager to handle the pulling of this data and provide the data the table needs.
This is an example of how the manager works:
#property (nonatomic) NSArray *dataArray;
...
- (void)refreshDataSource
{
[AClass fetchInBackgroundWithCompletionHandler:^(NSArray *objects) {
self.dataArray = [NSArray arrayWithArray:objects];
}
}
...
- (NSArray *)tableViewDataSource
{
return self.dataArray;
}
The view controller requests an update by calling -refreshDataSource in -viewDidLoad but in the meantime provides its UITableView with cache data from the manager by pointing to -tableViewDataSource.
When the view controller presents itself for the first time, everything is fine. The second time I go to present the same view controller, the app hangs. The network request doesn't fire either.
The only fix I've found is moving my -refreshDataSource call to -viewDidAppear: instead. But it itches me why this would be happening and discomforts me that something here must be wrong.
If anyone could provide any help or suggestions that would be great!
Your question hasn't explained everything, but here are a couple of ideas that might help you.
1) viewDidLoad is only called the first time your view loads. If you switch to a different view, then return to your tableView, refreshDataSource will not be called.
2) viewDidLoad might be firing before an array has been allocated and initialised, so it's nil when you're refreshing the data, whereas viewDidAppear might not have the same problem.
I can't give a more concrete answer without more information. Can you explain "the first time, everything is fine. The second time I go..." more clearly? Step-by-step what you do, if possible.

iOS Design Pattern memory management with delegate and parent object

Im struggling to solve in a very clean way a problematic involving memory overload (management).
Im having a serie of view that include other views, in my project I have a situation like this:
MainView
|_PageView
|_CustomButton
soo far soo good, easy as a cake. CustomButton have a delegate (protocol) in it for some reasons, so we have in PageView a "for cycle" that creates N CustomButtons, set the delegate as self in PageView (PageVew extend CustomButtonDelegate) and release the buttons afer attaching them like
{
CustomButton *customButton_ = [[CustomButton alloc] initWithFrame:CGRectMake(100.0,50+(i*55.0),200.0);
customButton.delegate = self;
[self addSubView:customButton_];
[customButton_ release];
}
soo far soo good again. Button will be press, PageView get the protocol method, do some code and voilà, done. One problem is that at one point, MainView must remove PageView, so In a method I call
[pageView_ removeFromSuperview];
[pageView release], pageView_ = nil;
pageView_ = [PageView alloc] initWithFrame.....];
and I recreate the object with other data to display.
I noticed that PageView never gets release and removed from the memory because its retainCount is exactly how many CustomButton I created inside PageView and assign the delegate to self plus one of course. My question is, what is the cleanest way to remove safely all the objects and be able to remove PageView too, free the memory (because Im loading a quite large amount of data to display in it) ?
Right now i'm doing:
Create in PageView a NSMutableArray, that I CustomButton the objects in
it, and before to remove PageView, I cycle it and set the delegate = nil and then release
each object, after I release the NSMutableArray (called "holder").
But the problem is that if I want to add more objects of different types with other protocols, adding to this array, can lead to other problems of retaining the objects.
Where do I lack guys, knowledge so I need to study more (quite sure I can say) or do I need to approach with another OOD?
Thank you guys, im going overload with this problem and my brain is stuck in a close road. :)
Looks like your CustomButton's delegate is a retain property of CustomButton. Delegate should be an assign property, not retain nor copy. See here.

Resources