Does this arrangement of objects leak? - objective-c

I have two classes A and B. In A, I am creating an object of B, and in B I am accessing the back Button property of class A. In B, I have declared A as a weak reference variable. The code runs fine without any crash. However I am not sure if there is memory leak occurring in my implementation. Also, do I have to declare backButton as a weak reference in A ?
#interface A : UIViewController
{
IBOutlet UIButton *backButton;
B * cntrl;
}
#property (nonatomic, strong) UIButton *backButton;
// Here is the implementation of A
#implementation A
#synthesize backButton;
// pushing to B
cntrl = [[B alloc]initWithNib:nil bundle:nil];
cntrl.parent = self;
[self.navigationController pushViewController:cntrl animated:YES];
#interface B:UIViewController
{
A __weak *parent;
}
#implementation
-(void)method
{
parent.backButton.enable = NO;
[self.navigationController popViewControllerAnimated:YES];
}

No, your code is fine.
If you have any doubt, you can test your app with instruments.

Related

Properties are null outside of viewDidLoad

Two properties:
#property (retain, nonatomic) NSString *drinkType;
#property (retain, nonatomic) NSString *wheelType;
When accessed from viewDidLoad as self.drinkType, etc, they hold the value I expect. However, when accessed from a public method
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
they are null. What is happening here?
The "selectedAromas" array is passed from another controller to this method.
ViewController *aromaVC = [[ViewController alloc] init];
[aromaVC updateSentenceWithSelectedAromas:selectedAromas];
ViewController.h
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
#property (retain, nonatomic) NSString *drinkType;
#property (retain, nonatomic) NSString *wheelType;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// This is working
NSLog(#"The drink type is:%#", self.drinkType);
}
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas {
// This returns null
NSLog(#"The drink type is:%#", self.drinkType);
}
I think your missing quite a few things, which leads me to think that you're missing some basic understanding of variable scope in ObjectiveC, let's see if this helps you in some way:
First, your selectedAromas array has no relation whatsoever with drinkType and wheelType. So passing this array to the ViewController seems irrelevant.
Second, in your ViewController you're declaring your own drinkType and wheelType variables, so there's no way they will have the value of some other class or Controller.
You probably aren't setting your properties soon enough (init would be a good place). viewDidLoad is called much later in relation to the code you posted.
Okay, Michael Dautermann was absolutely right. The method updateSentenceWithSelectedAromas was in fact running in a separate instance of the view controller. To solve this problem I implemented a protocol listener with my method and set the delegate of the child controller to its parent using a segue.
Thank you everyone for all your help.
In case anyone stumbles upon this, here is what I did:
ViewController2.h
#protocol updateSentenceProtocol <NSObject>
//Send Data Back To ViewController
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
#end
#interface ViewController2 : UIViewController
// delegate so we can pass data to previous controller
#property(nonatomic,assign)id delegate;
#end
ViewController2.m
#synthesize delegate;
-(void)someMethod {
[delegate updateSentenceWithSelectedAromas:selectedAromas];
}
ViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"viewController2Segue"])
{
// Get reference to the destination view controller
ViewController2 *vc = [segue destinationViewController];
vc.delegate = self;
}
}
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas {
// do stuff with array and properties as needed
}

Objective C - Proper way to Allocate and Remove View Controllers with ARC

Lets say I have a popup. When a button is tapped the popup has to be allocated and presented. When the close button delegate is called it has to be closed and removed properly. I don't think I am doing this correctly for some reason:
In .h file:
#interface MainViewControoler : UIViewController
{
PopupViewController* popupView;
}
#property (retain, nonatomic) PopupViewController* popupView;
#end
In .m file:
..
-(void)openButtonPressed
{
if (!popupView)
{
popupView = [[PopupViewController alloc] init];
popupView.delegate = self;
}
[self.view addSubview:popupView.view];
popupView.view.frame = self.view.frame;
}
..
-(void)closePopup
{
[popupView.view removeFromSuperview];
}
I don't want the this to have any leaks where if the popup is opened and closed 1000 times, it would not crash....Is this the correct way?
Your code does not generate leaks, as you are allocating the popover once. However, you do not need to create an instance variable manually, or to set the popover property to nil, ARC will take care of this, unless you want to also deallocate the popover before your VC is deallocated.
Just make sure you are not adding the popover view multiple times to the parent view (i.e. not calling openButtonPressed again before calling closePopup.
.h:
#interface MainViewControoler : UIViewController
#property (nonatomic) PopupViewController *popupView;
#end
.m:
-(void)openButtonPressed
{
if (!_popupView)
{
_popupView = [[PopupViewController alloc] init];
_popupView.delegate = self;
}
[self.view addSubview:_popupView.view];
_popupView.view.frame = self.view.frame;
}
-(void)closePopup
{
[_popupView.view removeFromSuperview];
}

Calling NSObject superclass method with [self performSelector:#selector]?

I have a Subclass of NSObject in which I want to call IMMEDIATELY (a sort of -(void)viewDidLoad) a method (in this case to load a MkMapView): what's the better way to do this? I think I cant use viewDidLoad, so can I use performSelector?
SubClass.h
#interface Mysubclass : NSObject <MKMapViewDelegate> {
}
SubClass.m (1st alternative)
-(id)init{
self = [super init];
if ( self != nil ) {
// THE CODE TO INITIALIZE MKMAPVIEW
}
return self
}
OR
SubClass.m (2nd alternative)
-(id)init{
[self performSelector:#selector(myMethod)];
return self;
}
-myMethod{
// THE CODE TO INITIALIZE MKMAPVIEW
}
What's the better (or correct) alternative? Its possible to avoid -(id)init? Or everytime I add a subclass, to call a method I have to write it into -(id)init? Thank you!
There is no reason to use -performSelector: in this context. If you want to add a method that initializes the MKMapView when your object is created, call the method from within the if (self) block:
- (id)init
{
self = [super init];
if (self) {
[self setupMapView];
}
return self;
}
- (void)setupMapView
{
// THE CODE TO INITIALIZE MKMAPVIEW
}
It is a matter of personal preference/style whether to have a second method -setupMapView or to simply leave the code for setting up the MKMapView in the if (self) block of the -init method or to break the code off into a second method -setupMapView called from -init.
That being said, it sounds like other things may be off with your setup. Your MKMapView should [most likely] be within a UIViewController subclass (which will probably have an associated XIB), so you will be have access to -viewDidLoad. Note that your UIViewController subclass will serve as the delegate to your MKMapView.
Update 1
In your UIViewController subclass instance (I'll assume you called it ViewController, you should have an IBOutlet to an MKMapView object. Do this in ViewController.h either by (1) adding an instance variable
#interface ViewController : UIViewController
{
IBOutlet MKMapView *myMap;
}
#end
or by (2) adding a property
#interface ViewController : UIViewController
#property (nonatomic, strong, readwrite) IBOutlet MKMapView *myMap;
#end
Now open ViewController.xib in Interface Builder. You should have an MKMapView inside the view. If you don't already, add one from the Object Library. Right click on File's Owner. Locate the row with the item myMap. Drag from the circle on the right end of the row to the MKMapView in the visible view.
Your ViewController class now has an outlet to the MKMapView. You will be able to send messages to the MKMapView subview of your view controllers view after it has been loaded.
You should have a property or an instance variable for your SubClass instance so that it doesn't get destroyed as soon as -viewDidLoad returns. Do this again by either adding an instance variable to ViewController.h
#interface ViewController : UIViewController
{
IBOutlet MKMapView *myMap;
SubClass *istance;
}
#end
or by adding a property
#interface ViewController : UIViewController
#property (nonatomic, strong, readwrite) IBOutlet MKMapView *myMap;
#property (nonatomic, strong, readwrite) SubClass *istance;
#end
Now, in ViewController.m, you need to define -viewDidLoad so that self.istance is set as the delegate of self.myMap. In the comments, I had suggested creating your own initializer -initWithMapView:. If you plan on having SubClass do some extensive set-up of your MKMapView, that makes sense. If you just want SubClass to be the delegate of the MKMapView, there's no need for such a method.
Let's consider both cases:
(1) using a method -[SubClass initWithMapView:]:
In ViewController.m you'll have (within the #implementation of ViewController)
- (void)viewDidLoad
{
self.istance = [[SubClass alloc] initWithMapView:self.myMap];
}
In SubClass.h you'll have (within the #interface of SubClass)
- (id)initWithMapView:(MKMapView *)mapView;
#property (nonatomic, weak, readwrite) MKMapView *mapView;
In SubClass.m you'll have (within the #implementation of SubClass)
- (id)initWithMapView:(MKMapView *)mapView
{
self = [super init];
if (self) {
self.mapView = mapView;
self.mapView.delegate = self;
//more setup of mapView.
}
return self;
}
(2) using -[SubClass init]:
Instead, in ViewController.m you'll have
- (void)viewDidLoad
{
self.istance = [[SubClass alloc] init];
self.myMap.delegate = self.istance;
}

Can't get property from another class

I have a class RootViewController where I have a UIBarButtonItem declared. The method to display it is in another class FirstDetailViewController.
I am trying to access it in another class SecondDetailViewController, but it is always null. I tested with some other variables and they were null as well. Here's what I have:
RootViewController.h
#interface RootViewController : UITableViewController <UISplitViewControllerDelegate> {
}
#property (nonatomic, retain) UIBarButtonItem *rootPopoverButtonItem;
...
#end
RootViewController.m
#import "RootViewController.h"
#import "FirstDetailViewController.h"
#implementation RootViewController
#synthesize popoverController, splitViewController, rootPopoverButtonItem;
- (void)splitViewController:(UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController:(UIPopoverController*)pc {
NSLog(#"splitviewController will hide");
// Keep references to the popover controller and the popover button, and tell the detail view controller to show the button.
barButtonItem.title = #"Menu";
self.popoverController = pc;
self.rootPopoverButtonItem = barButtonItem;
UIViewController <SubstitutableDetailViewController> *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:rootPopoverButtonItem];
}
FirstDetailViewController.m
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
NSLog(#"show root popover button item");
// Add the popover button to the toolbar.
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray insertObject:barButtonItem atIndex:0];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
SecondDetailViewController.h
#import "RootViewController.h"
#class RootViewController;
#interface SecondDetailViewController : UIViewController <SubstitutableDetailViewController, UIScrollViewDelegate, UITextFieldDelegate, UITextViewDelegate> {
...
}
#property (nonatomic, retain) RootViewController *root;
#end
SecondDetailViewController.m
#import "SecondDetailViewController.h"
#implementation SecondDetailViewController
#synthesize root;
...
NSLog(#"view class : %#", [root.splitViewController class]);
[detailViewController showRootPopoverButtonItem:root.rootPopoverButtonItem];
...
You probably aren't setting the "root" property of SecondDetailViewController to the instance of RootViewController that you want to access the UIBarButtonItem for. Then you're reading an uninitialized instance of RootViewController in your SecondDetailViewController code, and the only reason you don't get an error is that Objective C silently ignores calls to methods on nil objects (in this case the rootPopoverButtonItem getter method, which the root.rootPopoverButtonItem is shorthand for).
If your instance of "RootViewController" is called "myRootViewController", then somewhere in your code you have to do something like:
SecondDetailViewController *mySecondDetailViewController = [[SecondDetailViewController alloc] init];
mySecondDetailViewController.root = myRootViewController;
Then you'll be accessing the copy of RootViewController that has the bar button you want.

Objective-C: Calling class 2 instance from class1 [alloc init] way is not working

I've got the following method on a GameScreen.m file, with its own declaration - (void) drawNumbers on a GameScreen.h file:
//GameScreen.h
#import <UIKit/UIKit.h>
#interface GameScreen : UIView
{
IBOutlet UIButton *cell00;
}
- (void) drawNumbers;
- (IBAction) onCellClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *cell00;
#end
//GameScreen.m
#import "GameScreen.h"
- (void) drawNumbers
{
//testing if this works, so far it doesn't
[cell00 setTitle:#"Whatever" forState:UIControlStateNormal];
[cell00 setTitle:#"Whatever" forState:UIControlStateHighlighted];
}
I'm trying to call this method from my GameScreenViewController.m file, this way:
//GameScreenViewController.m
#import "GameScreenViewController.h"
#import "GameScreen.h"
...
- (void) viewDidLoad
{
GameScreen *aGameScreen = [[GameScreen alloc] init];
[aGameScreen drawNumbers];
[aGameScreen release];
[super viewDidLoad];
}
This is supposed to change the title of a button in a GameScreen.xib file where GameScreenViewController.m is the viewController and GameScreen class is the event handler where I get all the button clicks, timers running, etc. I am trying to call [drawNumbers] from [viewDidLoad] since I want the title to be changed when the screen is brought up front (screen management is done through the AppDelegate files).
The thing is, if I call drawNumbers instance from inside the same class through
//GameScreen.m
#import GameScreen.h
-(void) onButtonClick:(id)sender
{
//some other code
[self drawNumbers];
}
it works (as to say, nothing wrong with the code implementation or the graphic interface).
I've browsed through Apple Guide and tons of pages on the Internet, but I can't seem to find any light to this. Any further help (including answers as to where exactly find the answer in the ADG) would be really appreciated.
(Edited: here goes the AppDelegate code to flip to the specific view, just in case):
//myAppAppDelegate.h
#import <UIKit/UIKit.h>
#class myAppViewController, GameScreenViewController;
#interface myAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
myAppViewController *viewController;
GameScreenViewController *gameScreenViewController;
}
- (void) flipToGameScreen;
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) GameScreenViewController *gameScreenViewController;
#end
//myAppAppDelegate.m
-(void) flipToGameScreen
{
GameScreenViewController *aGameScreenView = [[GameScreenViewController alloc] initWithNibName: #"GameScreen" bundle:nil];
[self setGameScreenViewController:aGameScreenView];
[aGameScreenView release];
[gameScreenViewController.view.frame = [[UIScreen mainScreen] applicationFrame];
[viewController.view removeFromSuperview];
[self.window addSubview:[gameScreenViewController view]];
}
Since your cell00 is to be set by a NIB it will be nil if you simply do [[GameScreen alloc] init]. It will only be set if the corresponding NIB is loaded (and a connection is actually set up).
If the cell can be accessed in your viewDidLoad, create a property on GameScreen and pass it through the property (or a dedicated initWithCell: or something).
If you have something like an IBOutlet GameScreen *aGameScreen; on your GameScreenViewController (and also established a connection to cell00 in the same NIB) you should access that instead.