I have some problem with my singleton and UIViewController there;
Singleton.h
#property (nonatomic, retain) UIViewController *viewController;
Singleton.m
...
#synthesize viewController = _viewController;
- (void)load {
self.viewController = [[[UIViewController alloc] initWithNibName:#"NibName" bundle: nil] autorelease];
}
- (void)unload {
[_viewController release];
}
This viewController using by different part of the application via pushViewController:animated:. But sometimes I need to release viewController by calling method - (void)unload of Singleton class! If pushViewController:animated: never call for viewController everything is well and dealloc is calling, but if pushViewController(and viewController perform viewDidLoad), dealloc isn't work. If I do something like self.viewController = nil; dealloc calling twice... What I'm doing wrong???
Your unload function should only consist of:
- (void)unload {
self.viewController = nil;
}
When you set a retained property to nil, it releases the instance variable AND nils it. You are simply leaving a dangling pointer on your property here.
You need to set it to nil after releasing it:
[_viewController release];
_viewController = nil;
Otherwise the next person who comes along will try to do stuff with an invalid pointer.
Related
I have a really strange delegate behavior in iOS. I'm setting a custom delegate in a subclassed UIViewController like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.baseNavigationBar = [[BaseNavigationBar alloc] initWithNibName:nil bundle:nil];
self.baseNavigationBar.delegate = self;
self.baseNavigationBar.navigationController = self.navigationController;
[self.navigationController.navigationBar addSubview:self.baseNavigationBar];
}
The initWithNibName can use nil, when using the default nib, because internally it will check fora nil in the nibName.
if (!nibNameOrNil) {
nibNameOrNil = NSStringFromClass([self class]);
}
The delegate declaration from the baseNavigationBar object looks the following:
#property (nonatomic, retain) id<BaseNavigationBarDelegate> delegate;
/*same thing with assign, which I you should use*/
/*and in the .m of course #synthesize*/
And now 2 screenshots from the running application.
The first one show's the debugger values from the BaseListViewController, which is a subclass of BaseCoreViewController, which is a subclass of UIViewController.
The screenshot is taken, when the viewDidLoad method is called.
The second one show's the values from the BaseNavigationBar, which is a UIView subclass.
The screenshot is taken at a time, when the user clicks the "next" button
- (IBAction)nextAction:(id)sender {
if (self.delegate) {
[self.delegate navigationBarDidClickNextButton:self];
}
}
So why is this a problem? By clicking a button in the BaseNavigationBar my delegate is always nil, so the program is stuck. But when looking at the values from the BaseCoreViewController at the same time the delegate is not nil. Very strange.
Edit
The BaseNavigationBar is loaded from a nib file using the UINib loadNibNamed:owner:options: function.
Edit 2
So that's pretty much all of the code.
Edit 3
Finally we got the source of the error in the last pieces of the code... setting self = something totally not allowed...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (!nibNameOrNil) {
nibNameOrNil = NSStringFromClass([self class]);
}
if (!nibBundleOrNil) {
nibBundleOrNil = [NSBundle mainBundle];
}
NSArray *topLevelObjects = [nibBundleOrNil loadNibNamed:nibNameOrNil owner:self options:nil];
/*Here is the bad part*/
self = [topLevelObjects objectAtIndex:0];
state = BaseNavigationBarButtonStateNext;
if (self) {
self.title = #"";
}
return self;
}
Solved using a Class method instead of defining a custom init.
If you are using nib file, you should declare the nib class while initialising your class object.
You have initialised initWithNibName:nil
self.baseNavigationBar = [[BaseNavigationBar alloc] initWithNibName:nil bundle:nil];
but it should be like initWithNibName:#"BaseNavigationBar"
self.baseNavigationBar = [[BaseNavigationBar alloc] initWithNibName:#"BaseNavigationBar" bundle:nil];
Not sure what the problem is. Require some more code to analyze the problem. But what I can see is address of BaseNavigationBar in both the screen shots is different. It means the BaseNavigationBar object present in the controller is not the one which got the nextAction event as in second screen shot.
First thing i do is create a ViewController and push it to the Navigation Controller.
viewController = [[MyViewController alloc] init];
[navController pushViewController:viewController animated: NO];
[viewController release];
Retain count is 2 now (pushViewController uses 2 retain apparently but not my responsibility) so far so fine.
Inside MyViewController i'm createing a instance of a class and sets the ViewController as delegate to the instance.
timer = [[MyBackgroundTimer alloc] initWithInterval:20];
[timer setDelegate:self];
Now the viewControllers retain count has increased by 1 becouse of setDelegate:
But when i'm releasing the viewController later it will never call dealloc becouse i have one more retain count.
How should you correctly drop the retain count when you set your self as delegate?
Don't retain your delegate. If you're using a property, define your delegate as assign, not retain. Somebody else needs to retain your delegate, not you.
Your class MyBackgroundTimer should have the delegate property as assign and not retain.
#property (nonatomic, assing) id delegate;
And this class should retain the delegate just when it needs to use it, and release when it is done.
#implementation MyBackgroundTimer
#synthesize delegate;
-(void) startTimer {
[self.delegate retain];
//... do some actions
}
-(void) timerStopped {
//... call delegate methods
[self.delegate release]
}
#end
It is important to remember that you can have your delegate as a retain property. But to do so the right way, you have to ensure that you release it before dealloc is called (like the timerStopped method in the example above)
I say that because if you try to release the delegate at the dealloc method, the class that instantiates MyBackgroundTimer is the same class as the delegate, and it also releases MyBackgroundTimer at the dealloc (which is pretty much the common case), the dealloc method of both classes will never be called, as each class will have the ownership of the other, resulting in a memory leak (that will not be shown at instruments).
I have a view controller that was presented using
[self presentModalViewController:myVC animated:YES];
this VC has several declared retained properties (#property) that I have to release on its dealloc.
The variables are declared as
#property (nonatomic,retain) myClass1 *myProperty;
#property (nonatomic,retain) myClass2 *myProperty2;
// etc... and then synthesized on .m
The problem is that when I dismiss the viewController using
[self dismissModalViewControllerAnimated:YES];
it crashes on the dealloc, when releasing the retained properties I have declared, with the error *modifying layer that is being finalized *
Apparently the the viewController is gone at the time its own dealloc runs and then it crashes.
How do I solve that? Thanks in advance.
edit
the code that presents the viewController is on the rootViewController and is this:
UIViewController *myVC = [[UIViewController alloc] init];
myVC.delegate = self;
UINavigationController *navigator = [[UINavigationController alloc] initWithRootViewController:myVC];
[self presentModalViewController:navigator animated:YES];
[navigator release];
[myVC release];
and this is the what the dealloc code on myVC contains
- (void) dealloc {
[myProperty1 release]; // see this properties at the beginning of this question
[myProperty1 release]; // if I comment these 2 relesases it stops crashing
[super dealloc];
}
myVC is dismissed from inside itself, but that's fine according to the docs. I have also tried to dismiss it from the rootviewController but it continues to crash. The only way to stop crashing is to disable the release lines on the dealloc.
How do you set the properties?
self.myProperty =
? My guess is that you do not retain them. Are you doing
myProperty = ...
by any chance, with neither a retain or self.? The setter you synthesize needs a chance to actually retain your newly created object...
So in full it should read something like this:
MyClass1 *aProperty = [[MyClass1 alloc] init];
self.myProperty1 = aProperty;
[aProperty release];
...i asked where you allocate and init "myProperty1" and "myProperty2"...
i'm afraid that you make confusion thinking that this:
#property (nonatomic,retain) myClass1 *myProperty;
#property (nonatomic,retain) myClass2 *myProperty2;
need that you release myProperty and myProperty2
well, you are wrong!
you are just declaring the kind of objects you are going to use, not allocating them
you need to release them just if you alloc them someWhere...
This class is a subclass of UITabBarViewController.
In my init parent view controller file I have this:
UIBarButtonItem *button1 = [[UIBarButtonItem alloc]
initWithTitle:#"Button1"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(button1:)];
self.navigationItem.rightBarButtonItem = button1;
[button1 release];
And the method:
-(IBAction)button1:(id)sender {
if (self.nvc == nil) {
ChildViewController *vc = [[ChildViewController alloc] init];
self.nvc = vc;
[vc release];
}
[self presentModalViewController:self.nvc animated:YES];
I want to get an value from the parentviewcontroller in my childviewcontroller class, which also is a UITabBarViewController subclass.
How do I do this, I have tried several hours, and I only get a nil-reference.
The object I want to get(which is a property in the parent) is a NSString.
Thanks in advance
The cleanest way would probably be to create a ChildViewControllerDelegate protocol that the parent view controller implements. This is a common idiom in iOS development.
#protocol ChildViewControllerDelegate
- (NSString *)getSomeNSString;
#end
Then you should make ChildViewController have this delegate as an instance variable and be assignable via a property
#property (nonatomic, assign) id<ChildViewControllerDelegate> delegate;
Now from within ChildViewController you can use this delegate to access methods on the delegate which in your case will be ParentViewController. This will allow you to retreive the string you want.
[delegate getSomeNSString]
This may seem like a lot of work for something simple but it avoids the problems inherit with storing a back reference from ChildViewController to its parent ParentViewController.
There are a lot of ways to do this. The easiest would be to add a property to ChildViewController that points to your parent view controller. You could call it delegate. Then the method will look like:
-(IBAction)newbuilding:(id)sender {
if (self.nvc == nil) {
ChildViewController *vc = [[ChildViewController alloc] init];
vc.delegate = self;
self.nvc = vc;
[vc release];
}
[self presentModalViewController:self.nvc animated:YES];
}
Then from the ChildViewController instance you can access self.delegate.someProperty.
There are also ways to get the parent view controller without your own explicit reference (typically self.tabBarController, self.navigationController depending on context), but the above method is fool-proof, understandable and easy to debug.
I have a view controller. The view controller has a retained object called streamController, which is an NSObject subclass that handles all of the data I/O with my server. All is well, except I'm trying to figure out why some things are leaking on said streamController. I drop an NSLog in there and I never see it firing. I'm completely puzzled as to why, because I'm releasing the controller in my dealloc method for my view controller.
from view controller interface...
StreamController *streamController;
#property (nonatomic, retain) StreamController *streamController;
from view controller implementation...
#synthesize streamController;
- (id)init {
[super init];
self.streamController = [[StreamController alloc] init];
}
- (void)dealloc {
NSLog(#"dealloc view controller");
[streamController release];
[super dealloc];
}
from StreamController implementation...
- (void)dealloc {
NSLog(#"dealloc stream controller");
[super dealloc];
}
this last dealloc NEVER gets called. Why?
I believe you are just leaking memory,
if your property has retain attribute then you should take a look at the following examples:
//A
self.streamController = [[StreamController alloc] init];
//B
StreamController * st = [[StreamController alloc] init];
self.streamController = st;
[st release];
//C
streamController = [[StreamController alloc] init];
if you check retain counts you will see that in A approach your streamController object will have a retainCount of 2, while in B it will be only 1.
Reason:
In A by doing [[StreamController alloc] init]; your object has already a retainCount of 1 before passing it to your property. Then, since you declared it as retain, it will be retained, hence it's retainCount becomes 2.
In B is basically the same but you are releasing the object just after you pass it to your property. Hence, it ends with it's retainCount in 1. (This is what we want)
In C you are not using the property, you are setting the value directly. Hence it will be retained only once. This is fine in this case because is the initialization.
I will suggest to use B or maybe C if you are sure that streamController is nil (like the initialization of your object)
Hope this helps