I have 2 view controller,
FirstViewController - > SecondViewController via
[self presentViewController:SVC animated:YES completion:nil];
on SecondViewContrller when I do
[self dismissViewControllerAnimated:YES completion:nil];
My question is, Why is the objects not release on secondViewController after I dismiss this viewcontroller. As you can see on the graph It didn't go down after dismiss. BTW whats the best way to release/dismiss a ViewController?
[EDIT]
I NSLog a message on dealloc method on every VC, When I start from FVC->SVC->[dismiss SVC]. this is my logs
This can be pretty rough stuff. I had similar issues before. Search your code and see if you have strong or wrong references to objects.
One of my top mistakes (and what I have seen on the internet hundreds of times) are delegate properties. I wrote them like #property (nonatomic, retain) id<protocol>delegate; for quite a long time as I realized that if I do so, the delegated object does not get released. One have to use assign in this case.
Hope that help you...
I have made some investigation with this behavior.
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)dealloc {
NSLog(#"First Dealloc");
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIButton *pressMe=[UIButton buttonWithType:UIButtonTypeCustom];
pressMe.frame = CGRectMake(0, 0, 100, 40);
pressMe.center = self.view.center;
[pressMe setTitle:#"PressMe" forState:UIControlStateNormal];
[pressMe addTarget:self action:#selector(pressMeAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pressMe];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) pressMeAction:(id) sender
{
SecondViewController *svc = [[SecondViewController alloc] init];
[self presentViewController:svc animated:YES completion:nil];
NSLog(#"Present Second");
}
#end
SecondViewController.m
is pretty the same except
-(void) pressDissmissButtonAction:(id) sender
{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Dismiss Second");
}
and this is Allocation dynamics
As you can see after pressMeButtonAction invoked secondViewController allocated and after pressDissmissButtonAction invoked secondViewController is successfully deallocated.
BUT: Most of the time it deallocated immediately, but if you present and dismiss it very quickly (twice a second or so), dellocation not fired immediately, but after a while.
I Assume that this is by design implementation of ARC deallocation procedure. Not sure.
try this ...
[self presentViewController:SVC animated:YES completion:nil];
SVC = nil;
After spending many hours on this, I finally found a missing piece of the puzzle: Not only do you have to set any strong references to the ViewController to nil, you also have to invalidate any timers and be aware of block retain cycles.
Any time you use self in a block you create a retain cycle! Instead you should declare a variable like so
__unsafe_unretained ViewController *weakSelf = self;
and use it instead of self in the block.
Check all IBOutlets in your application. There might be "strong" property assigned to them. Make them "weak". For example, an IBOulet should be like this:
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
Check all delegates (if any) in your application. Every delegate should be like this:
#property (nonatomic, assign) id <yourProtocol> delegate;
Note that, it takes some amount of time for ARC to recover memory.
Timers were the issue in my case. Added timer invalidate to viewWillDisappear and the view controllers were then released.
Related
Hey I converted my project to ARC automatically and the only thing I had to fix was that Xcode changed array that had a size of [8] to [4] (still don't know what was going on there)
However, I now realized I got another problem: every time I hit a button in my Popup, the app crashed if it is linked to an IBAction. If I remove the reference to the header and main file, the button is clickable, but as soon as i assign it to a method (even if the method is empty), the whole app jut freezes/crashes.
This is how I initiate my popup:
PopUpViewController *popViewController =
[[PopUpViewController alloc] initWithNibName:#"PopUpViewController" bundle:nil];
[popViewController setTitle:#"This is a popup view"];
[popViewController showInView:self.view
animated:YES];
popViewController.view.center=self.view.center;
Pretty basic stuff, I ripped it off a tutorial I found online. My header is this:
#interface PopUpViewController : UIViewController
- (IBAction)baa:(id)sender;
#property (strong, nonatomic) UIButton *ba; // I was trying to add those buttons as properties here
#property (strong, nonatomic) IBOutlet UIButton *kl; //but no luck whatsoever
#property (strong, nonatomic) IBOutlet UIView *popUpView;
#property (strong, nonatomic) IBOutlet UIView *ppp;
- (IBAction)openSomething:(id)sender;
- (IBAction)openYoutube:(id)sender;
- (IBAction)openFacebook:(id)sender;
- (void)showInView:(UIView *)aView animated:(BOOL)animated;
#end
And my main
#import "PopUpViewController.h"
#interface PopUpViewController ()
#end
#implementation PopUpViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)showAnimate
{
// not important for this question
}
- (void)removeAnimate
{
// shortened
}
- (IBAction)baa:(id)sender{
// NSLog(#"asd"); I commented it out, but even an empty method kills the app
// only if I leave a button unassigned to any method, the app "survives" a button click
}
- (IBAction)openSomething:(id)sender
{
// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.test.de"]];
}
- (IBAction)openYoutube:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://www.youtube.com/"]];
}
- (IBAction)openFacebook:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://www.facebook.com/"]];
}
- (void) doSomething{
// also tried an empty method with a "new" name over here to eliminate any caching errors, no luck
}
- (IBAction)closePopup:(id)sender {
[self removeAnimate];
}
- (void)showInView:(UIView *)aView animated:(BOOL)animated
{
[aView addSubview:self.view];
if (animated) {
[self showAnimate];
}
}
- (void)viewDidLoad
{
self.view.backgroundColor=[[UIColor blackColor] colorWithAlphaComponent:.0];
self.popUpView.layer.cornerRadius = 5;
self.popUpView.layer.shadowOpacity = 1.0;
self.popUpView.layer.shadowOffset = CGSizeMake(0.2f, 0.2f);
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Any ideas what I could do different? I referenced everything to everything else possible now and I don't even know how it looked before anymore, but I somehow think it has something to do with the referencing.
Thanks a lot in advance, Alex
I was able to figure out on my own:
In the header of my main ViewController I added
#property (strong, nonatomic) PopUpViewController *popViewController;
Which still fired the crash. But then I adjusted the popup "opening" block to
_popViewController =
[[PopUpViewController alloc] initWithNibName:#"PopUpViewController" bundle:nil];
[_popViewController setTitle:#"This is a popup view"];
[_popViewController showInView:self.view
animated:YES];
_popViewController.view.center=self.view.center;
(added the underscores) and now it's working perfectly again.
Actually I am curious why it worked before but I am not going to investigate this any further.
So I'm trying to get a hang of using delegates, and I've watched a few tutorials on how to use them so far. I still find them confusing and after trying to implement one myself, have an issue that I can't seem to solve.
I have two ViewControllers, the first one ViewController contains a UITextField *sampleTextField and a button with the method switchViews. It also contains the protocol declaration with the method sendTextToViewController. SwitchViews is also linked to a segue that switches to the SecondViewController. In SecondViewController the only object is a UILabel *outputLabel When the user taps the button, it calls switchViews and the view changes to SecondViewController, and upon loading outputLabel should be changed to whatever text was entered in sampleTextField in ViewController. However the delegate method sendTextToViewController is never being called. All objects are created in Interface Builder.
Here is the code to make it a bit easier to understand:
ViewController.h
#import <UIKit/UIKit.h>
#protocol TextDelegate <NSObject>
-(void)sendTextToViewController:(NSString *)stringText;
#end
#interface ViewController : UIViewController
- (IBAction)switchViews:(id)sender;
#property (weak, nonatomic) IBOutlet UITextField *sampleTextField;
#property (weak, nonatomic) id<TextDelegate>delegate;
#end
Then declared this in ViewController.m
- (IBAction)switchViews:(id)sender {
NSLog(#"%#", self.sampleTextField.text);
[self.delegate sendTextToViewController:self.sampleTextField.text];
}
SecondViewController.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface SecondViewController : UIViewController <TextDelegate>
#property (weak, nonatomic) IBOutlet UILabel *outputLabel;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize outputLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
ViewController *vc = [[ViewController alloc]init];
[vc setDelegate:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)sendTextToViewController:(NSString *)stringText
{
NSLog(#"Sent text to vc");
[outputLabel setText:stringText];
}
I've looked at this and the first answer makes sense, but for some reason it's not working.
I do think that the problem is where I am setting calling [vc setDelegate:self], but not sure how to fix this. Some pointers in the right direction would be greatly appreciated. Keep in mind I'm new to obj-c so if you can explain what you are saying, that would be great. Thank you.
Your are creating a new instance of ViewController but you don't do anything with it.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
ViewController *vc = [[ViewController alloc]init];
[vc setDelegate:self];
}
The SecondViewController needs to have reference to the FirstViewController to be able to set itself as a delegate.
First you don't have to use delegation to do such a program.
A simpler way would be just creating a property in the SecondViewController that you'll pass the content of the textField into it.
Your code doesn't work because you called sendTextToViewController on a delegate that hasn't been set. You have set the delegate to a new instance of ViewController, not the one presented onscreen.
I need a ViewController to be called modally to show some UIButton and other UIView on top of the current window. I want the background to be partially transparent and showing the current window below it - something similar to a UIActionSheet but with a custom design. I coded my VC to do the following: 1) during init the VC sets self.view.frame equals to [[UIApplication sharedApplication]keyWindow].frame 2) when show() is called the VC adds self.view on top of [[UIApplication sharedApplication]keyWindow] subViews 3) when an internal button calls the private method release() the VC remove self.view from its superview. Example with a single release button as follows:
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
//set my view frame equal to the keyWindow frame
self.view.frame = [[UIApplication sharedApplication]keyWindow].frame;
self.view.backgroundColor = [UIColor colorWithWhite:0.3f alpha:0.5f];
//create a button to release the current VC with the size of the entire view
UIButton *releaseMyselfButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[releaseMyselfButton setTitle:#"Release" forState:UIControlStateNormal];
releaseMyselfButton.frame = CGRectMake(0, 0, 90, 20);
[releaseMyselfButton addTarget:self action:#selector(releaseMyself) forControlEvents:UIControlEventTouchDown];
//add the button to the view
[self.view addSubview:releaseMyself];
}
return self;
}
- (void) show
{
//add self.view to the keyWindow to make sure that it will appear on top of everything else
[[[UIApplication sharedApplication]keyWindow] addSubview:self.view];
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
}
#end
If I create an instance of ModalController from another VC and I call show() everything goes as expected:
#interface CurrentVC ()
#property (strong, nonatomic) ModalController *myModalController;
#end
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.myModalController = [[ModalController alloc]init];
[self.myModalController show];
}
#end
To make it work I need to retain the ModalController in a property until release () is called. However I would like to have the same freedom I have with UIActionSheet and simply keep an instance of it in a local variable:
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
ModalController *myModalController = [[ModalController alloc]init];
[myModalController show];
}
#end
If I do this with the current code ARC will release myModalController straight after show() is called and the release button will be pointing to nil. How can I make this work without storing the object in a property? I've identified a work around but I'm not sure it's a good design option:
#interface ModalController ()
#property (strong, nonatomic) ModalController *myselfToAutorelease;
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
... ... ...
self.myselfToAutorelease = self;
}
return self;
}
- (void) show
{
... ... ...
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
self.myselfToAutorelease = nil;
}
What I've done is making ModalController "self sufficient" - it stores a pointer to itself during init and set it to nil when it's ready to release himself. It works but I have the feeling that this is against the ARC design principles! Is this approach correct? If not, how can I handle this differently?
Thanks in advance for your help.
Doesn't work like that.
You don't keep a reference to self.
In the main view controller you just create your object. If you need it to be around longer keep it in a property in the main view controller , when done, set the property to nil in the main view controller.
I am working on a very basic app that displays a popover when the user is entering text into a UITextField. Unfortunately, the popover is not showing up and the default keyboard is appearing (which shouldn't). Here is my relevant code below:
NumberPadViewController.h
#import <UIKit/UIKit.h>
#import "NumberViewController.h"
#interface NumberPadViewController : UIViewController <UITextFieldDelegate> {
IBOutlet UITextField *numTest;
}
#property (nonatomic, strong) NumberViewController *numberPicker;
#property (nonatomic, strong) UIPopoverController *numberPickerPopover;
#end
NumberPadViewController.m
- (BOOL)textFieldShouldBeginEditing:(UITextField *) textField
{
// Create popover controller if nil
if(_numberPickerPopover == nil){ //make sure popover isn't displayed more than once in the view
_numberPickerPopover = [[UIPopoverController alloc]initWithContentViewController:_numberPicker];
}
[_numberPickerPopover presentPopoverFromRect:numTest.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
return NO;
}
My popover class is called NumberViewController.h
#interface NumberViewController : UIViewController {
}
#property (strong, nonatomic) IBOutlet UIButton *oneButton;
NumberViewController.m
#import "NumberViewController.h"
#interface NumberViewController ()
#end
#implementation NumberViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSInteger buttonHeight = _oneButton.frame.size.height * 4;
NSInteger buttonWidth = _oneButton.frame.size.width * 3;
self.contentSizeForViewInPopover = CGSizeMake(buttonWidth, buttonHeight);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I have created the UITextField in Storyboard, and set the delegate there. Can anyone see what I am doing wrong?
Thanks in advance to all who reply.
Ensure the textFieldShouldBeginEditing delegate method is being called. The rest of the code looks correct.
This is just a thought, but when a popover does a popover thing i think it becomes first responder, but your text view is first responder... so it might not be able to over do it.... if this is the error then before you tell the popover to appear you can say [textView resignFirstResponder]; and see if that helps.... it's just a thought though not 100% sure i will have to do some testing ~
also check to see if _numberPicker is not nil aswell i don't know what happens if you try to display a popover with no controller but you can see if that's the problem
Seeing as your popover isn't represented in the storyboard (if I read your post right) I think you need to add the popover view as a subview in code. Something like:
[self addSubview:_numberPickerPopover];
There are potentially a few places to do this. Probably makes the most sense in your textFieldShouldBeginEditing: method, after you've inited it.
I am trying to make a custom protocol that i hope somebody might help with.
I have a mainViewController (mainVC) that has a label. That label needs to be updated with a string when i press a button in edwwVC.
I am using ARC and storyboard.
The problem is when i press the Done Button on the edwwVC, the "done" method is called BUT the delegate method is not called in mainVC.
Whereas, if i call the done method VIA the mainVC, then the done method is called AND the delegate method. So I can see the connection is there, I just do not understand why the delegate method is not called when i press the done button in the edwwVC.
I imagine it has something to do with the init of the edwwVC. Because it is already initiated by storyboard, so it looks to me as if I am initializing it again the in the viewDidLoad method of the mainVC. But that is how far i got :)
Thanks in advance!
edwwVC.h
#import <UIKit/UIKit.h>
#import "IIViewDeckController.h"
#class EDWWViewController;
#protocol EDWWViewControllerDelegate <NSObject>;
#optional
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC;
#end
#interface EDWWViewController : UIViewController <IIViewDeckControllerDelegate> {
__weak id<EDWWViewControllerDelegate> delegate;
NSMutableArray *edwwPoints;
}
#property (weak) id<EDWWViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) NSString *testString;
- (IBAction)done:(id)sender;
- (IBAction)add:(id)sender;
#end
edwwVC.m:
#pragma mark - delegate method
- (IBAction)done:(id)sender {
testString = #"This is the test string!";
[delegate edwwVCDidFinish:self];
[self.viewDeckController closeRightViewAnimated:YES];
NSLog(#"Done pressed");
}
MainVC.m:
- (void)viewDidLoad
{
[super viewDidLoad];
edwwViewController = [[EDWWViewController alloc] init];
edwwViewController.delegate = self;
}
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC {
edwwLabel.text= edwwVC.testString;
NSLog(#"delegate method called");
}
Remove the line ...
__weak id<EDWWViewControllerDelegate> delegate;
From the .h and change the line...
[delegate edwwVCDidFinish:self];
to...
[self.delegate edwwVCDidFinish:self];
In the .m.
That should sort it.
The way you have it set up the ivar delegate is not the same as the property delegate (which is actually an ivar called _delegate) (thanks #Joris Kluivers, just adding for clarity). They are pointing to different things.
If you add a breakpoint where you are calling the delegate method I think you'll find that delegate is nil. Whereas _delegate (or self.delegate) is not nil.
::EDIT::
Ahh... just spotted the second bit too.
If you are setting up the edwwvc in storyboard then you should be alloc initing it too.
If you are segue-ing to the edwwvc then you should intercept the segue in mainVC.m like this...
- (void)prepareForSegue: //blah the rest of the name...
{
if ([segue.identifier isEqualToString:#"the name of your segue"])
{
EDWWViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
}
This will take the controller that you are pushing to from the storyboard and set the delegate to it.
:: ANOTHER EDIT ::
If EDWWVC is inside a containerViewController then you can do this inside viewDidLoad in MainVC.m...
- (void)viewDidLoad
{
// other stuff...
for (UIViewController *controller in self.childViewControllers) {
if ([controller isKindOfClass:[EDWWViewController class]]) {
EDWWViewController *edwwvc = (EDWWViewController*)controller;
eddwvc.delegate = self;
}
}
}
You may find this code has to go in viewDidAppear or something but I think viewDidLoad shouldd work just fine.
You may actually be able to set the delegate property directly by using the storyboard to (but I'm not 100% certain on this).
The answer was in the containerVC of both controllers.
Where i initialized the view controllers: the viewDidLoad of the containerVC m file:
- (void)viewDidLoad
{
[super viewDidLoad];
mainVC = (MainViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"MainVC"];
edwwVC = (EDWWViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"EDWWVC"];
//THIS LINE WAS MISSING
edwwVC.delegate = mainVC;
self.centerController = mainVC;
self.rightController = edwwVC;
}
BUT guys thanks for the help! :) Appreciate it got me in the right direction! :) THANKS! :)