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.
Related
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.
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.
I am having an issue with ARC. It is not retaining the webview. The scenario is I have to send a webview from one viewcontroller to another one. The reason is when the user searches for something I want to take him to a new screen with some other options. (I have to use the same webview)
Here is the sample code: I have a ViewController1 which has a webview (I added it in the xib.) I am loading say google in it and once the user searches for something and when its done loading I have to take him to a new view controller and show the same webview in the new viewcontroller.
//ViewController1
#interface ViewController1 : UIViewController <UIWebViewDelegate>
#property (nonatomic, retain) UIWebView* testWebView;
#end
#implementation ViewController1
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
testWebView = [[UIWebView alloc]init];
testWebView.delegate = self;
[testWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"https://www.google.com"]]];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *html = [testWebView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
if ([self.testWebView.request.url.absoluteString rangeOfString:#"output=search"].location != NSNotFound) {
ViewController2* newViewController = [[ViewController2 alloc] init];
[newViewController setTestWebView:self.testWebView];
[self.navigationController setViewControllers:[NSArray arrayWithObject:newViewController] animated:NO];
}
}
- (void)dealloc{
[self.testWebView stopLoading];
self.testWebView.delegate = nil;
self.testWebView = nil;
}
In the second view controller I am loading stackoverflow.com after a delay of 10 secs. The problem is it is loading stackoverflow fine, but it is not calling any of the delegate methods. Why?
#interface ViewController2 : UIViewController <UIWebViewDelegate>
#property (nonatomic, retain) UIWebView* testWebView;
#end
#implementation ViewController2
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.testWebView.delegate = self;
[self.view addSubview:testWebView];
[self performSelector:#selector(loadDifferentPage) withObject:nil afterDelay:10];
}
-(void)loadDifferentPage{
[self.testWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.stackoverflow.com/"]]];
}
-(void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(#"%s", __PRETTY_FUNCTION__);
return YES;
}
ViewController2 is retaining the webview but the delegate methods are not being called. Why?
Thanks
Sai
ViewController1 delloc method was causing the issue:
If I uncomment out self.textWebView.delegate = nil it works fine. The reason is first we are setting the webview for newViewController and later in dealloc of ViewController1 we are setting its delegate to nil.
- (void)dealloc{
[self.testWebView stopLoading];
if(self.testWebView.delegate == self)
self.testWebView.delegate = nil;
self.testWebView = nil;
}
First thing I noticed is you're not specifying the instance variable name when synthesizing a property. That's just asking for collisions. Here's an example of how that should look:
#interface ViewController1 : UIViewController <UIWebViewDelegate>
#property (nonatomic, strong) IBOutlet UIWebView* testWebView;
#end
#implementation ViewController1
#synthesize testWebView=_testWebView;
Also, I noticed in ViewController1 you used IBOutlet so everything is probably wired up in Interface Builder. Try making sure that you set the delegate property in Interface Bulider because you don't set it in the implementation. That would be why you're not receiving any messages.
ViewController2 looks like you set the delegate in code. The problem is, you DON'T have IBOutlet in front of the property. Normally this would mean that you simply setup the WebView in code, but in your example you do not ever create a new instance of a UIWebView control and assign it to self.testWebView. This means that if it does display on the page, it's because Interface Builder was used to create it. You couldn't set the delegate in code without using IBOutlet in front of the testWebView declaration so that's probably why it's not working in exmaple two.
#interface ViewController2 : UIViewController <UIWebViewDelegate>
#property (nonatomic, strong) UIWebView* testWebView; // Mising IBOutlet
#end
#implementation ViewController2
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
// missing any code that would create the webview [[UIWebView alloc] init]
self.testWebView.delegate = self; // You set the delegate in code here
[self.view addSubview:testWebView];
[self performSelector:#selector(loadDifferentPage) withObject:nil afterDelay:10];
}
Hope this helps, I'd have to see your full implementation to get more specific than this.
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.
I am using the nav controller and pushing another view into the stack and setting a variable too. While trying to access the variable I get EXEC_BAD_ACCESS :(
Here's the code (I am not using any NIB) :
#import <UIKit/UIKit.h>
#interface detailedView : UIViewController {
NSString *movieName2;
}
#property (nonatomic, retain) NSString *movieName2;
#end
and
#import "detailedView.h"
#implementation detailedView
#synthesize movieName2;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void) viewDidAppear:(BOOL)animated
{
self.view=[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view.frame=CGRectMake(213, 300, 355, 315);
self.view.backgroundColor=[[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"black.png"]];
self.title=self.movieName2;
NSLog(#"%#",movieName2);
}
Relevant code in the caller function :
detailedView *details;
#property (nonatomic, retain) detailedView *details;
//properly synthesized and released
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Did Reach inside...");
status=1;
self.title=#"Back";
details.movieName2=self.movieName;
NSLog(#"```");
NSLog(#"2nd VC %# sdfsdf",details.movieName2); //Getting the ERROR here
NSLog(#"1st VC %# wrewrw",self.movieName);
//viewerSearch *viewerSearchController=[[viewerSearch alloc] init];
[[self navigationController] pushViewController:details animated:NO];
}
init:
details=[[detailedView alloc] init];
//movieName is a NSString and has #property(nonatomic,copy)
When an app crashes, there will be a crash log or you'll be able to get a backtrace from the debugger. Always post that.
Also -- class names should always start with a capital letter.
Try Build and Analyze; make sure your memory management is correct.
Then turn on Zombie detection once you fix any build and analyze found problems.
Based on a test by calling a retain on a alloc-init-released NSString and then following it up by logging it, I think your problem is that self.movieName is deallocated. Please check whether the memory management rules have been properly followed with regards to self.movieName.
On a side note, if you are not using a NIB for detailedView then you should create the view in loadView and not viewDidAppear: as you seem to be doing. viewWillAppear: only if the view exists, right? I suggest you move the related code from viewDidAppear: to loadView.
You should always copy strings instead of retaining them.
So changing
#property (nonatomic, retain) NSString *movieName2;
to
#property (nonatomic, copy) NSString *movieName2;
will probably fix your problem.
Also, if you create the value in self.movieName with #"something", do not release the value.