I have an app, with a lot of views and subviews.
In my app delegate, I open a subview each time I receive a notification.
I made a lot of tests :
- the notifications are always received well.
- the subview is always well created, it's never at nil.
But after a notification or two, the subviews do not show again, even if they exist and are well created in memory.
I think I am doing something wrong with the memory and I probably misunderstood something with the view hierarchy.
Here is the code that create and add the subview :
AppDelegate.h
#property (retain, nonatomic) ViewControllerNewOrder *sub;
AppDelegate.m
sub = [[ViewControllerDriverNewOrder alloc] init];
sub = [mainStoryboard instantiateViewControllerWithIdentifier:#"ViewControllerNewOrder"];
sub.view.frame = self.initialViewController.view.bounds;
[self.initialViewController.view addSubview:sub.view];
Thank you for your time and your help !
The following line saved my life :
[self.window.rootViewController.view.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
Then I added the method removeFromSuperView to all the views I needed to clear.
Of course, you will need, like me, to think about where you place this line.
Thanks ;)
Related
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.
I have a UIScrollView full of tacos.
I attached a pull-to-refresh handler to it via: https://github.com/samvermette/SVPullToRefresh
It extends uiscrollview, and exposes this method:
[scrollview addInfiniteScrollingWithActionHandler:^{
// Get me more tacos
}];
When InfiniteScrolling is triggered, I clear the scrollview's subviews and data array(intentionally) and replace it with a new set.
It works great the first time. However, when I want to load more tacos it crashes.
I get:
-[SVInfiniteScrollingView retain]: message sent to deallocated instance 0x1e5db5d0
Not surprisingly, if I leave 1 subview left in my UIScrollview, everything works fine.
Question: How can I fix this?
I thought about declaring my properties with a strong pointer, like:
#property (strong, nonatomic) IBOutlet tacoScroller *tacoScroller;
But, I worry about a retain cycle & it also doesn't work.
Any help would be appreciated, perhaps I'm missing something fundamental.
Edit:
I'm using ARC
Use an UITableView to show your tacos, this way you will reuse views and avoid wasting memory. Also it is the easiest and most convenient way to show a list of things.
One simplest solution according to your description is simply add an empty & hidden subview inside the scrollview, i am sure it wont occupy much amount of memory.
I think you are invoking the wrong method. Infinite scrolling is for other purposes.
You probably want to use
[scrollView addPullToRefreshWithActionHandler:^{ ... }];
Also, as others pointed out, you definitely should consider to use a UITableView to present your data, which seems to be very suitable for the task.
I added a subview to my main view, the subview has a button on it, but when I select the button on the subview, the application crashes with the following highlited in green:
Thread 1:EXC_BAD_ACCESS (code=1), address=0xf0000008
The subview has its own viewcontroller and xib file.
heres some of the code I used:
Subview.h
- (IBAction)setDummyTime:(id)sender;
Main view.m
PickupTimeViewController *pickupTimeView = [[PickupTimeViewController alloc]init];
[selectedView addSubview:pickupTimeView.view];
Thanks
Sounds like its been dealloc'ed on you.
Try turning on Zombie mode, crash the app again and see if it points you to whats going on, it will tell which object is trying to do something after being released.
Xcode -> Click your scheme -> Edit Scheme -> Run -> Diagnostics -> Tick Enable Zombie Objects
Another thing to try is to watch your subView in the debug area, this will tell you when your subView is released.
Example of setting watch
Also if you have not already tried it, try keep a strong/retain reference to your subview.
If you can't get it working post the code you create the view with.
EDIT:
in .h
#property (nonatomic, strong) PickupTimeViewController *pickupTimeView;
in .m
self.pickupTimeView = [[PickupTimeViewController alloc]init];
[selectedView addChildViewController:self.pickupTimeView]; // (i0S5+ only) if the view you add to is a view controller if not use self otherwise.
[selectedView addSubview:pickupTimeView.view];
Good luck,
BooRanger
(originally i posted this on Apple Developer Network and got no response in four days, so i am copy/pasting here)
ARC is enabled.
In my class i have an UITableViewController property,
#property (nonatomic, strong) UITableViewController* tableViewControllerSectionMenu;
which is used to hold table view for UIPopoverController.
This code creates UITableViewController and sets delagate and data source in viewDidLoad:
self.tableViewControllerSectionMenu = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
self.tableViewControllerSectionMenu.tableView.dataSource = self.dataSource;
self.tableViewControllerSectionMenu.tableView.delegate = self;
This works fine until memory warning occur.
After that the table is blank.
I tried putting reloadData at place where popover is invoked but that changes nothing.
Does somebody know why this is happening and how to remedy this?
By googling i have found several solutions and none have worked in my case.
I think your data is cleaned when the memory warning is received. Did you implement either didReceiveMemoryWarning or viewDidUnload by any chance?
When memory warning message is received, all views not currently visible on screen are purged. They are supposed to be recreated later when needed.
To recreate them means also to attach delegate and data source again. Since memory warning was received while in ViewController X, and it's view was on screen, my table view got purged.
But as i wasn't moving from X, delegate and data source were not attached again, as that was done in viewDidLoad.
To evade getting back one step from X, i created a new class for my table view where in viewDidLoad i attached delegate and data source.
For example, say I have a RootViewController class and AnotherViewController class, and I need to change a property in my RootViewController from AnotherViewController... is it safe to have a "RootViewController" property in AnotherViewController.h (so that I can access its instance variables)?
#interface AnotherViewController : UIViewController {
RootViewController *rootViewController;
}
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#implementation AnotherViewController
#synthesize rootViewController;
- (void)someMethod {
// set the data was added flag, so the rootViewController knows to scroll to the bottom of the tableView to show the new data
self.rootViewController.dataWasAdded = YES;
// if the user came in via a search result, make the search controller's tableView go away
self.rootViewController.searchDisplayController.active = NO;
}
If that's not a good idea, can anybody explain why?
In the code above, I know I could have used a protocol/delegate to handle the same thing - and I'm guessing I probably should. However, none of the books or other materials I've read has really discussed this.
The reason I'm asking is that I'm in the process of making my app universal, and using a UISplitViewController I've noticed that I need to often update my "master view" as the user makes changes in the "detail view". So, I took what seemed the easy route and started setting UIViewControllers as properties... but I'm experiencing some hard to track memory leaks and occasional crashes. I read something about "circular references", and wonder if that could be part of the issue (I do have a couple of places where UIViewControllers are set as properties of one another).
Thanks for any insight, or pointers to reference materials that cover this.
I'd avoid making a habit of this as there are better safer alternatives. Using a protocol/delegate is the preferred Apple way of managing data across classes. You can also set up NSNotifications to send/trigger data/events from one class to another. Key Value Observing (KVO) is also a decent way to listen in for changes.
In MVC structuring, the child views and downstream controllers really should have no idea (aka, keeping references) of their parents. It should always work the other way around where the parents manage and keep track of the children.