Cocos2d Delegate retain cycle - objective-c

I just cannot figure out this retain cycle, and would appreciate if someone could help me spot it.
I have a RootController object that holds strong a strong reference to a RootView.
#interface RootController : CCNode <TouchDelegate, GUIDelegate, ModelViewDelgate>
...
#property (nonatomic, weak) CCDirector *director;
#property (nonatomic) RootView *view;
...
#end
#implementation
- (id)init {
...
_view = [[RootView alloc] initWithController:self];
[self addChild:_view];
...
}
return self;
}
#end
I have a RootView object that holds a reference to the controller, as well as an activeGame, enabling me to swap between game types without needing to know the specifics other than it conforms to a <TouchDelegate> protocol:
#interface RootView : CCScene
...
#property (nonatomic, assign) RootController *controller;
#property (nonatomic) GameplayLayer <ModelViewDelgate> *activeGame;
...
#end
#implementation RootView
- (id)init {
...
self.activeGame = [[GameplayLayer alloc] initWithDelegate:_controller root:self type:type];
[self addChild:self.activeGame];
...
}
return self;
}
#end
And lastly, I have the GameplayLayer, which is invoked by the RootView when necessary, and should be deallocated by the RootView:
#interface GameplayLayer : CCLayer <ModelViewDelgate, Updatable>
...
#property (nonatomic, assign) RootView *rootView;
#property (nonatomic, assign) RootController <TouchDelegate> *touchDelegate;
...
#end
When a controller class decides it's time to cleanup the game (usually a hard reset of the game), literally every other class in my project is deallocated except for this GameplayLayer, which never receives the dealloc method. What am I missing? Here is how I 'restart' my game...
[[CCDirector sharedDirector] replaceScene:[RootController node]];

Thanks to some thought-provoking questioning by #JoshCaswell and #LearnCocos2D, we were able to solve the problem. It turns out, there really wasn't a problem. As per requirements of Cocos2d, in the onExit method, you must tell the CCDirector to remove any touch delegates previously assigned to that CCNode. If you override that method, however, you must call [super onExit] before exiting the method, otherwise Cocos2d will fail to remove children, and thus certain elements will never dealloc.

Related

How to correctly implement a NSWindowController subclass with xib-file

I am trying to implement a NSWindowController subclass with new xib-file, I read up in lots of books, and researched on StackOverflow, but none of the steps provided made my window show, nor did the subclass code get executed. The new xib-file has its File's Owner set to "LogNavigatorController" and connections to the window and its contents have been made.
My AppDelegate.h:
#import <Cocoa/Cocoa.h>
#class LogNavigatorWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
LogNavigatorWindowController *logsWindowController;
}
#end
My AppDelegate.m:
#import "AppDelegate.h"
#import "LogNavigatorWindowController.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
logsWindowController = [[LogNavigatorWindowController alloc] initWithWindowNibName:#"LogNavigatorWindowController"];
[logsWindowController showWindow:self];
}
#end
My LogNavigatorWindowController.h:
#import <Cocoa/Cocoa.h>
#interface LogNavigatorWindowController : NSWindowController
{
NSArray *directoryList1;
NSArray *directoryList2;
NSMutableArray *directoryList;
NSMutableArray *filePaths1;
NSMutableArray *filePaths2;
}
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTableView *logsTableView;
#property (unsafe_unretained) IBOutlet NSTextView *logsTextView;
#property (assign) IBOutlet NSArrayController *LogListController;
#property (retain) NSMutableArray *logsArray;
- (void) myDirectoryLogFunction;
#end
My LogNavigatorController.m:
#import "LogNavigatorWindowController.h"
#interface LogNavigatorWindowController ()
#end
#implementation LogNavigatorWindowController
#synthesize logsTableView;
#synthesize logsTextView;
#synthesize window;
- (id)init
{
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
if (self)
{
// Initialization code here.
[self myDirectoryLogFunction];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (void) myDirectoryLogFunction
{
NSLog(#"Code execution test successful");
}
#end
You don't need to create the window property since it is already available for NSWindowController subclasses. Maybe that causes the problem.
Also your init method contains a lot of code that doesn't belong there. Remove
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
as well as replace
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
with
self = [super init];
You may want to remove the init method at all, since you don't need it in your case.
and move
[self myDirectoryLogFunction];
to the windowDidLoad method.
Also always check that the code for instantiating the window controller (in your case from the app delegates didFinishLaunching: ) is called. Sometimes it helps to create a new project and test there, if you may have changed too much within the original project and by accident removed delegate connections or similar.

Can this code works without overridden dealloc method (Objective-C)

Manual memory management is used. The following code runs well and no crash occurs. But there is no -(void)dealloc method. Is this code wrong? Should I add -(void)dealloc?
MyClass.h
#import <UIKit/UIKit.h>
#interface MyClass : NSObject {
#private
BOOL flag;
UIView *view;
UILabel *label;
UIButton *button;
UITabBar *tabBar;
UIWebView *webView;
UIImageView *imageView;
}
#property (nonatomic, retain) UIView *view;
#property (nonatomic, retain) UILabel *label;
#property (nonatomic, retain) UIButton *button;
#property (nonatomic, retain) UITabBar *tabBar;
#property (nonatomic, retain) UIWebView *webView;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize view;
#synthesize label;
#synthesize button;
#synthesize tabBar;
#synthesize webView;
- (id)init {
self = [super init];
if (self) {
// Initialization code
// Among other code,
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
}
// Other methods here.
// But -(void)dealloc is not overridden here in the MyClass.m
#end
If we must add -(void)dealloc for the above code, should it be like this:
Overridden -(void)dealloc
-(void)dealloc {
[view release];
[label release];
[button release];
[tabBar release];
[webView release];
[super dealloc];
}
Update 1
#synthesize added, see above.
Update 2
Didn't put this into another post because this seems rather related issue:
See the above MyClass.m/.h, there is a private ivar (not sure it should be called ivar or field here) UIImageView *imageView;, it has no property for it, no #synthesize, initialization given there, how can we dealloc it? Also [imageView release]; in -(void)dealloc?
Update 3
Do we have to check availability before releasing ivars? That is, instead of [view release];, use this:
if (nil != view) {
[view release];
}
Yes. You need to implement dealloc.
You dealloc will look like :
-(void)dealloc {
[_view release];
[_label release];
[_button release];
[_tabBar release];
[_webView release];
[super dealloc];
}
Any retained/copy property should be released on dealloc.
Your iVar have no meaning. They do not have the same information as the properties, so you can remove your iVars.
If you want your properties to be backed up by your iVars you should #synthesize them like:
#synthesize view = view;
#synthesize label = label;
#synthesize button = button;
#synthesize tabBar = tabBar;
#synthesize webView = webView;
As you are not using ARC, your code (including the dealloc method) is correct, however you are missing the #synthesize statement in the implementation for the properties to work:
#implementation MyClass
// this will synthesize the getters and setters
#synthesize view, label, button, tabBar, webView;
- (id)init {
self = [super init];
if (self) {
// Initialization code
}
return self;
}
// Other methods here.
-(void)dealloc {
[view release];
[label release];
[button release];
[tabBar release];
[webView release];
[super dealloc];
}
#end
While Daniel's answer is correct in addressing your question, it does not cover what you should do.
This is how your code would be written in a modern world:
turn on ARC. Especially if you are learning or this is your first project. There is no reason to not use ARC. Learning manual-retain-release is valuable, but not critical at this time as the tools do a very good job of providing analysis of when ARC based patterns are leaking memory (either the analyzer or using Instruments, both of which you would need to use under MRR and neither of which work as well under MRR).
Don't use #synthesize and don't declare iVars (and certainly don't declare iVars in your .h file). Let the compiler autho-synthesize the _ prefixed ivars automatically. The _ prefix has the added advantage of disallowing you from accidentally directly accessing an ivar in code. I.e. self.foo vs. foo; the second won't compile.
Or to translate into code:
#interface MyClass : NSObject
#property (nonatomic, retain) UIView *view;
#property (nonatomic, retain) UILabel *label;
#property (nonatomic, retain) UIButton *button;
#property (nonatomic, retain) UITabBar *tabBar;
#property (nonatomic, retain) UIWebView *webView;
#end
And:
#implementation MyClass
{
BOOL _flag;
UIImageView *_imageView;
}
- (id)init {
self = [super init];
if (self) {
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
}
// no -dealloc
#end
Note that I declared _imageView as an instance variable that will be compatible with the obvious imageView #property should you need to refactor your class later to make that externally available.
If you realy must use manual retain-release, then add a -dealloc method that calls -release on all the ivars. I.e.[_view release];, [_imageView release];, etc...
Don't get what you mean by "Note that I declared _imageView as an
instance variable that will be compatible with the obvious imageView
#property should you need to refactor your class later to make that
externally available."
If you were to decide that _imageView needs to be accessible to other objects, then you would delete the iVar declaration and add:
#property(nonatomic, retain) UIImageView *imageView;
The compilers automatic synthesis would create an instance variable named _imageView automatically and none of the rest of your code would have to change.
Do we have to make sure an ivar is not nil before releasing it in
dealloc method? (See Update 3 above.)
No. In Objective-C, nil eats messages. That is, [nil release]; is perfectly valid and does absolutely nothing at runtime.
In your code the BOOL flag; has disappeared. Do we have make a
property for BOOL flag;, that is, #property BOOL flag;? Or all we have
to do is just placing a private field in the MyClass.h as #private
BOOL flag; up there in my original question?
I forgot it. You could create a property for it. Or you could declare _flag as an iVar next to _imageView as I've done above.
Most importantly, there is no longer any reason (save for a very rare case that is generally too be avoided) to declare instance variables in your .h.

passing data from parent to child

In my app I'm trying to pass data from the parent view to the child view. However, when I run the app the delegate method isn't being called.
Here is the code on how I implemented the custom delegate:
parent.h
#protocol SLBWallViewControllerDelegate <NSObject>
- (void) pictureToBeUploaded:(id)picture;
#end
#interface SLBWallViewController : UIViewController <UIActionSheetDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
- (IBAction)createPotButtonPressed:(id)sender;
#property (weak, nonatomic) id <SLBWallViewControllerDelegate> delegate;
#end
parent.m
[self.delegate pictureToBeUploaded:info[UIImagePickerControllerEditedImage]];//i'm taking the pic from a uiimagepicker
child.h
#interface SLBPostViewController : UIViewController <SLBWallViewControllerDelegate>
#property (weak, nonatomic) IBOutlet UIImageView *picture;
#end
child.m
#pragma mark - Wall View Controller Delegate
- (void)pictureToBeUploaded:(id)picture{
self.picture.image = picture;
}
Is there anything wrong or missing?
Well, the problem probably (edit: confirmed in the comments) is that self.delegate is nil at that point. So you send a message to nil and nothing happens of course. What you have to do is make sure that you assign a Child instance to delegate property before trying to send the message. Something like this for example:
//...
self.delegate = //.. a 'Child' instance
//...
[self.delegate pictureToBeUploaded:info[UIImagePickerControllerEditedImage]];
//(1) In Child in .h file create one property
#property (nonatomic,strong) UIImage * someImage;
//(2) In Child in .m file override the property
-(void)setSomeImage:(UIImage *)someImage{
_someImage=someImage;
self.picture.image = self.someImage;
}
//(3) In Parent in .m file set image to child's 'someImage' property
childsObject.someImage= UIImagePickerControllerEditedImage;

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;
}

View's dataSource property set to nil after I set it

New to iOS/Objective-C here. I've spent a lot of time trying to figure this out, but just can't manage it. Here's what's happening:
Click a 'Graph' button on my RootViewController's view
GraphViewController takes over, has a graphView property
I set the graphView's dataSource property to self
Checking self.graphView.dataSource with NSLog confirms that it does indeed point to self
When GraphView's drawRect: is called, self.dataSource is set to (null), where I expected it to point to the GraphViewController object
To summarise: I'm instantiating the graphView property in the view controller, then setting its dataSource, but by the time the view's drawRect: is called the dataSource is no longer set.
GraphViewController.m:
#import "GraphViewController.h"
#import "GraphView.h"
#implementation GraphViewController
#synthesize graphView = _graphView;
#synthesize program = _program;
- (GraphView *)graphView {
if(!_graphView) {
_graphView = [[GraphView alloc] init];
[_graphView setDataSource:(id)self];
}
return _graphView;
}
#end
GraphViewController.h:
#import <UIKit/UIKit.h>
#import "GraphView.h"
#interface GraphViewController : UIViewController
#property (strong, nonatomic) IBOutlet GraphView *graphView;
#end
GraphView.m:
#import "GraphView.h"
#implementation GraphView
#synthesize dataSource = _dataSource;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSLog(#"initWithFrame called, self.dataSource=%#", self.dataSource);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawRect:");
NSLog(#"\tself=%#", self);
NSLog(#"\tself.dataSource=%#", self.dataSource); // is (null), shouldn't be
[self.dataSource programToGraph];
}
#end
GraphView.h:
#import <UIKit/UIKit.h>
#class GraphView;
#protocol GraphViewDataSource
- (id)programToGraph;
#end
#interface GraphView : UIView
#property (nonatomic, weak) IBOutlet id <GraphViewDataSource> dataSource;
#end
So it looks like the GraphViewController instance is being deallocated, which will nil out the dataSource property.
So you should go back and look at how you are creating and managing that GraphViewController. A common mistake is to create a view controller like that, then borrow its view and throw it into some other view controller's view hierarchy, and then let the original view controller just go away.
I would look at that, and if you don't see it there, post the code where you create and present the GraphViewController.
Oh, and where are you calling initWithFrame: from?? It almost looks like you could also have two different instances of GraphView floating around. Try logging 'self' along with self.datasource to check that also.