-Using ARC
-I have 5 separate view controllers all subclassing a class I made called "UIViewControllerWithLoadingView" which subclasses UIViewController.
-In both the subclasses and superclass I allocate and deallocate properties like this:
#property (strong, nonatomic) NSURLConnection *urlConnection;
- (void)viewDidUnload
{
[super viewDidUnload];
self.urlConnection=nil;
}
-Now when didReceiveMemoryWarning is called, the sub classes viewDidUnload method acts fine. BBBUUTTT if I set properties to nil in the super class, UIViewControllerWithLoadingView, the application will crash. Particularly right where I set the properties of the sub class to nil. So for right now I just don't set the properties to nil in the superclass, which becomes problematic because the live bytes just keep piling up at run time.
The rule of thumb is that methods that "clean up"--like dealloc or viewDidUnload--should make the call to super after they do everything else. (And methods that "set up"--like init--call to super first.) I can't tell if that's your problem without seeing all your subclass implementations, but that would be a place to start.
The problem was in the superclass I had a view that extended uiview which had a property reference to the viewcontroller. Well dealloc is automatically called in arc so dealloc would actually set the viewcontroller itself to nil causing a crash. Once I removed the property of the viewcontroller in the custom view class the problem no longer occurred
Related
There's a way to remove self as observer from all notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Is there a similar way to remove self as a delegate of all objects that the viewController is a delegate of?
I want to place it in dealloc method in my prototype viewController. The reason is that sometimes when I dismiss a viewController, I get a crash with a message that a UIScrollView (or any of the official subclasses) called a method on its delegate (the viewController) that has since been deallocated. Since I consciously intend to call no such methods, I'd rather set self.delegate = nil. But since this is a prototype viewController, I don't have outlets to those scrollViews, hence my question.
No, there is no such way. Once we are done with the delegate, it requires to simply nil that reference.
Also before calling any delegate method make sure, to check nil condition as well method availability condition like:
if (_delegate != nil && [self.delegate respondsToSelector:#selector(myDelegateMethod:)]) {
// then only call that delegate method
[self.delegate myDelegateMethod:param];
}
Once you are done with all your delegate things nil your reference:
self.delegate = nil;
I don't know any built-in mechanism for it. I think, that the code that was responsible for making your object a delegate should be responsible for freeing it of this responsibility. You could provide some interface for it, but it depends on your code.
Also, since delegates are weak-referenced, they will be automatically set to nil when the delegate object is deleted, but it's not your case, I believe.
UPDATE:
Since in your case delegates don't seem to be declared as weak, I guess, the only option is to keep track of all objects that set your viewController as their delegate manually.
For instance:
In your ViewController:
#interface YourViewController
#property (nonatomic, strong) NSMutableDictionary *objectsThatDelegateSomethingToUs;
#end
#implementation YourViewController
-(void)makeDelegateOfObject:(id)obj withDelegatePropertyName:(NSString*)delegatePropertyName {
[self.objectsThatDelegateSomethingToUs setObject:delegatePropertyName forKey:obj];
}
-(void)dealloc {
for (id obj in self.objectsThatDelegateSomethingToUs.allKeys) {
[obj setValue:nil forKey:[self.objectsThatDelegateSomethingToUs valueForKey:obj]];
}
}
#end
Where you set your viewController as a delegate:
scrollView.delegate = viewController;
[viewController makeDelegateOfObject:scrollView withDelegatePropertyName:#"delegate"];
But, sadly, in this case you'll have to set your ViewController as a delegate programmatically. If delegate property is always called delegate, NSMutableArray should do the trick.
Actually, it's weird that scrollView keeps working when it's parent ViewController is deallocated. May be this is the real problem, and it can be fixed somehow, but, unfortunately, I can't give you any advice on it now, so my answer is trying to deal with the problem that you originally asked about. I recommend you to leave the question open for a while, may be someone else will be able to suggest a better solution.
P.S. Check out Logan's comment to your question. If all objects that use your ViewController as their delegate, are parts of ViewControllers' view hierarchy, then his solution is simpler and more elegant.
Placing this code in my superclass' dealloc method solved my crashes:
for (id view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
[view setDelegate:nil];
}
}
I was considering doing this in my viewController where I have direct access to the UIScrollView. However, because ARC disallows calling [super dealloc], this does not allow the dealloc code in my superclass to be called.
Okay so i decided to move my code for my UITableView delegates into another class. a subclass if you will, A subclass so that it would make it easier to access all the elements my Cellforrowwithindexpath function does within said subclass.
But now there is a slight issue...
It works fine, as far as the UItableView is concerned, But then when i tried to use the navigation controller to push a view on top, it did not work, i then discovered that self...Within my main class was actually an instance of my subclass...What? so self is not actually equal to self...
Can anyone give me any insight as to what i am doing so colossally wrong here?
EDIT: So i changed it to instead be a subclass to a delegate and it works fine, just in case anyone else runs into This issue, But i am still confused as to why it was happening in the first place...
Code:
#interface OpenGameList : MainMenuViewContoller <UITableViewDelegate,UITableViewDataSource>
{
}
#end
In my MainMenuViewController's viewDidLoad function
_openGameList = [[OpenGameList alloc] init];
_openGameList.delegate = self;
friendsTable.delegate = _openGameList;
friendsTable.dataSource = _openGameList;
And than after that it seems that any use of self in MainMenuViewController is equal to OpenGameList hence why using [[fromView navigationController] pushViewController:toView animated:NO]; does not work
self always points to the object that was actually instantiated – the most derived class.
When you have an instance of a subclass, and you send a message to self, the subclass's implementation will always be invoked if there is one. It doesn't matter whether or not you're in the superclass's implementation file or the subclass's implementation file.
This is an essential for polymorphism: it's what allows subclasses to override the behavior of a parent class. Take -[UIView drawRect:] for example. To invoke the drawing code for subclasses, when code in UIView invokes [self drawRect:] it's the subclass's drawing implementation which needs to be called.
It might help to remember that superclasses and subclasses aren't parent and child objects, but less and more specific types which apply to the same object. A UITableView is also a UIScrollView, UIView, and NSObject, but when you make one, there is one object which is all of those things, and self always refers to that one.
When I release a UIViewController, the UIViewController is correctly destroyed but its dealloc method is not called.
If the UIViewController has been destroyed (it is nil in the console), then the retain count should be 0, consequently I expect the -(void)dealloc method to be called.
I've also checked for subclasses of my UIViewController, overriding dealloc without calling the superclass method, but this is not the case.
This is how I initialize it:
myViewController = [[MyViewController alloc] initWithViewController:statusPicker];
What could be the reason ?
Thanks
If the UIViewController has been destroyed (it is nil in the console),
then the retain count should be 0, consequently I expect the
-(void)dealloc method to be called.
Well, no. The fact of being nil, doesn't mean the UIViewController has been released. Put it simple, the pointer for the UIViewController, is now pointing to nil, but the memory where the UIViewController reside is still being occupied. Instead of being called dealloc, two things might be happening:
1) There is something else with a reference to the UIViewController (example: when you pushViewController B from A, A got a reference to B).
2) You got a memory leak.
Again I am basing my answer in what you said:
#Lefteris Automatic Referencing Counting = NO
Check that you haven't got any circular references. For instance if your view controller implements a delegate protocol, check that your code doesn't retain this delegate.
EDIT: changed the title. I didn't know it at the time but this is a duplicate of Why am I crashing after MKMapView is freed if I'm no longer using it?
This question is similar to Why is object not dealloc'ed when using ARC + NSZombieEnabled but different enough that I thought it worth throwing out there in case anyone understands and can explain to me what is happening. The other question may be an XCode bug so I presume this could be similar.
Scenario:
RootViewController has a tableView displaying a bunch of items
Selecting a cell presents a modal detailViewController containing another tableView
One of the table cells in detailViewController contains an MKMapView showing the location of the item
mapView.delegate = detailViewController
Dismiss the modal detailViewController
Soon after this, the app crashes b/c the MKMapView sends mapView:viewForAnnotation: to the now dealloc'ed detailViewController. This crash repro'ed on a users device with an ad-hoc distribution build so the issue has nothing to do with NSZombieEnabled.
I was able to resolve the crash by adding:
_mapView.delegate = nil;
to the dealloc method of the tableViewCell containing the mapView.
QUESTION: why is it necessary to nil the delegate when the cell is dealloc'ed? It seems like the mapView should be dealloc'ed by ARC when the cell is dealloc'ed leaving this unnecessary. It is good practice to nil delegates but I didn't think it would be required in this case.
EDIT: all subviews of both detailViewController and the UITableViewCells are declared as (nonatomic, strong) properties ala:
#property (nonatomic, strong) MKMapView * mapView;
EDIT 2: Guess I need to get better at reading the docs. #fluchtpunkt is correct. Here's the relevant info from the MKMapView documentation:
Before releasing an MKMapView object for which you have set a
delegate, remember to set that object’s delegate property to nil. One
place you can do this is in the dealloc method where you dispose of
the map view.
MKMapView is not compiled with ARC and because of that the property for delegate is still declared as assign instead of weak.
From the MKMapView documentation:
#property(nonatomic, assign) id<MKMapViewDelegate> delegate
And from the Transitioning to ARC Release Notes:
You may implement a dealloc method if you need to manage resources other than releasing instance variables. You do not have to (indeed you cannot) release instance variables, but you may need to invoke [systemClassInstance setDelegate:nil] on system classes and other code that isn’t compiled using ARC.
For delegates of system classes (NS*, UI*) you have to use the "old" rule of setting delegates to nil when you deallocate the delegate object.
so add a dealloc method to your detailViewController
- (void)dealloc {
self.mapView.delegate = nil;
}
While it's true that the delegates for such classes should be explicitly set to nil, doing it in dealloc is already too late. You already lost your reference to the mapview during viewDidUnload. You should do the self.mapView.delegate = nil BEFORE viewDidUnload (so probably viewWillDisappear or viewDidDisappear)
From my experience, only MKMapView and UIWebView behave this way.
I have declared a delegate for my cocoa application here :
MyAppDelegate.h
#interface MyAppDelegate : NSApplication {
}
- (void) applicationDidFinishLaunching:(NSNotification*) notice ;
#end
MyAppDelegate.m
#implementation MyAppDelegate
- (void) applicationDidFinishLaunching:(NSNotification*) notice {
NSLog(#"inside appdidfinishlaunching") ;
}
#end
I have linked the delegate outlet of File Owner to this object in IB.
Yet, this method is not getting called. I don't see any log messages from it.
Can you please suggest what is wrong ?
Your application delegate is not an application itself. It should inherit from NSObject, not NSApplication.
Why that matters
NSApplication is a singleton. Its init method always returns the first instance of NSApplication or any subclass, throwing away any subsequent objects you (or the nib loader) may be calling init on.
So you ended up setting your application object as its own delegate. The object you intended to make the delegate died in the second call to init, and the application object took its place.
Changing the application object to be an instance of your subclass would also have worked, but you'd still have the application as its own delegate, which is unclean and possibly dangerous (NSApplication may privately implement some of its delegate methods itself, as they're just notification handler methods). The only correct solution is to make your app delegate class not inherit from NSApplication.