Weird inheritance bug iOS - objective-c

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.

Related

Why not enforce strict singleton application delegate object to use in NIBs?

I just ran myself round in circles, all coming down to having instantiated an app delegate object in a secondary NIB that wasn't the NSMainNibFile. Amazing how having two app delegates kicking around means you have separate managedObjectContexts.
Here's a thought-- could I make my application delegate class a singleton? And safely instantiate it in more XIBs? What would that break?
Also, there are some mentions on stackoverflow that [[UIApplication sharedApplication] delegate] is a "singleton" but it doesn't appear that UIApplicationDelegate protocol guarantees that, nor is the superclass UIResponder a singleton, either. So could I shoot myself in the foot in this regard on iOS as well?
[edit] Looks like you could nil out the delegateClassName in UIApplicationMain for iOS and have the main NIB load the delegate object, so you could create the App Delegate object pattern seen on OSX, if using a main NIB.
[edit2] Screenshot of what MainMenu.xib looks like for a new non-document application. The project gets created with this object, app delegate class gets created with a window property. The issue is getting that nice handy object in other NIBs, and that object being the same as [NSApp delegate]
Just do this in your existing App Delegate (There will only be one!)
// In the header file
+ (AppDelegate*) sharedInstance;
// In the body
+ (AppDelegate*) sharedInstance {
return (AppDelegate*) [[UIApplication sharedApplication] delegate];
}
Then anywhere you want to refer to your App Delegate, you can simply use [AppDelegate sharedInstance] followed by the property or instance method you want to call.
You shouldn't be using the app delegate for stuff to do with core data anyway. So making it an enforced singleton is pointless.
Ideally nothing should need to reference back to it at all.
Okay, after the question having been voted up, and then voted down to zero because of who-knows-why, I've continued to investigate my own answer. I think it's useful to make your app delegate classes true singletons so you can't cause headaches with NIBs. I can't see why it would be harmful. And I think if your app has a single user interface, it's not unreasonable to have the app delegate own the core data stack for all NIBs. However, the recommended design pattern would be to then have each window or view controller be passed the ManagedObjectContext pointer, and to access the MOC through the File's Owner placeholder rather than using an App Delegate object.
Yet on the other hand, things are different with the "Shared User Defaults Controller" singleton, which gets a special object in every NIB. We don't have to pass every controller a pointer to it so that every view can access it. It's just omnipresent. The app delegate is different. There's no "Shared App Delegate" object in every NIB. Yes, there are reasons to never talk to the app delegate in NIBs, but that's not an answer to the question.
So, an answer.
Singleton design patterns:
Covered long ago by Apple in this deprecated reference document-- Creating a Singleton Instance.
Turns out what I want my application delegate class to implement is the "strict" implementation, rather than having a factory method which could create other objects of the app delegate class. The one different feature here is having [NSApp delegate] be the master pointer rather than an app delegate class function.
The strict implementation has to override allocWithZone for my application delegate class (as alloc calls allocWithZone).
+ (MYAppDelegate*)allocWithZone:(NSZone *)zone
{
if ([NSApp delegate] == nil) return [super allocWithZone:zone];
return [NSApp delegate];
}
- (MYAppDelegate*)copyWithZone:(NSZone *)zone
{
return self;
}
Init just returning [super init] is fine, so it needs no override.
Seems to work. I'll update this if not.
[update] I have also been investigating NIB loading using NSBundle's loadNibNamed:owner:topLevelObjects: -- but it appears that I'd get an array back with a new app delegate object, even from that method. The method allows getting pointers to the top-level objects in the NIB without having otherwise created outlets for them. Still seems the best method to get an app delegate object in a XIB other than MainMenu is to use something like the code above.
[another update] Why it could be harmful: According to the the section "Top-level Objects in OS X May Need Special Handling" in this document, there's good reason for me to believe that, even with ARC, this answer of mine increases the retain count on [NSApp delegate], but heck if I feel okay doing a bridge and a release on the app delegate in dealloc for the window/view controllers that have a top-level object for the app delegate. Plus that means code outside the app delegate class.

The right way of setting up MapKit's delegate in a separate class

What is the proper way of setting up a separate delegate class for MapKit?
I have MapView class subclassing MKMapView and bare MapDelegate class conforming MKMapViewDelegate protocol having only one initializer method.
Here is the extract from MapView initialization method I use:
# MapView.m ...
#implementation MapView
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// [self setShowsUserLocation:YES];
[self setDelegate:[[MapDelegate alloc] initWithMapView:self]];
The only method MapDelegate class has is
# MapDelegate.m ...
- (id)initWithMapView:(MapView *)aMapView {
self = [super init];
self.mapView = aMapView;
return self;
}
Having [self setShowsUserLocation:YES]; commented, all works fine - I see the map. If I uncomment this line, my application begins to crash.
What my MapDelegate class is missing?
UPDATE 1: if I don't use a separate class MapDelegate and set just setDelegate:self - all works.
UPDATE 2: Now I understand, that the problem with [self setDelegate:[[MapDelegate alloc] initWithMapView:self]]; string is that I need MapDelegate class to live longer than it does now (delegate property has weak attribute). If I do the following:
#property (strong) id delegateContainer;
....
[self setDelegateContainer:[[MapDelegate alloc] init]];
[self setDelegate:self.delegateContainer];
...it works! Is there a better way of retaining MapDelegate life cycle along with the one of MKMapView?
Thanks!
After waiting enough for any answers that could appear here and ensuring original problematic behavior twice more times, I am posting my own answer based on the second update from the question:
The problem with [self setDelegate:[[MapDelegate alloc] initWithMapView:self]]; string is that MapDelegate class should be able to be kept alive outside of the scope of question's initWithFrame method because delegate property has weak attribute. The possible solution is to create an instance variable serving as a container for a delegate class, for example:
#property (strong) id delegateClass;
....
[self setDelegateClass:[[MapDelegate alloc] init]];
[self setDelegate:self.delegateClass];
This solves the original problem.
LATER UPDATE
Though it is possible to set MKMapView's delegate in a separate class, I now realize that such model should not be used:
Currently I always prefer to use my controllers (i.e. controller layer in MVC in general) as delegates for all of my View layer classes (map view, scroll view, text fields): controller level is the place where all the delegates of different views can meet - all situated in controller layer, they can easily interact with each other and share their logic with the general logic of your controller.
On the other hand, if you setup your delegate in a separate class, you will need to take additional steps to connect your separate delegate with some controller, so it could interact with a rest part of your logic - this work have always led me to adding additional and messy pieces of code.
Shortly: do not use separate classes for delegates (at least view classes delegates provided by Apple), use some common places like controllers (fx for views like UIScrollView, MKMapView, UITableView or models like NSURLConnection).
I think viewDidLoad would be a better place to set up the map view. It's just a guess, but perhaps the crash is due to the view not being loaded yet.
Of course subclassing MKMapView isn't recommended at all. You would generally put your map as a subview, and set the main view to be the delegate. From the docs:
Although you should not subclass the MKMapView class itself, you can get information about the map view’s behavior by providing a delegate object.
Finally, if you really want to have a separate delegate class, you don't need to set its mapView, as all delegate methods pass the map as an argument.

ObjC: using 'self' in init and/or initWithFrame:

I heard from someone that the state of a class is not yet stable inside -(id)init, so using 'self.something' is not recommended inside init, but I have a UIView subclass that need to add some subviews to itself when the class is created, so I have to use [self addSubview: subview] in init, or I have to have another initialisation method and after I create the view using:
MyView *myView = [[MyView alloc] initWithFrame:frame];
I need to call that method. Is this correct? Does initWithFrame has the same situation with init that the class is not yet stable(as it is called after alloc)? If it is, then how should I initialise subviews?
Thanks!
There is nothing wrong with using self in init. The one point of fragility is that if you use a setter method [self setFoo:...] or self.foo = ..., then you might trigger any setter logic in the class or subclass that may not be prepared for the partially initialized state.
[self addView:someView]; is not a problem. However, you might want to consider lazy loading the views instead to offload from the instantiation. It might make it easier to refactor later.
Emphasis because if your view really does always need those other views, there is no point to trying to lazy load.

didReceiveMemoryWarning Crash

-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

UIViewController not loading my custom UIView

This should be straight forward for a guru. I don't have any code really written out, just a couple of controllers and a custom UIView. All connected through nibs. The app loads without crashing, yet I can't see my NSLog() hit from my custom UIView.
My application delegate has default template code which calls for a class of mine called TabAnimationController. TabAnimationViewController has its view set to TabView. I made sure that in TabAnimationViewController's NIB that File's owner is set to TabAnimationViewController and that my instance of UIView has its class set to TabView.
In TabView.m I'm trying to see how NSLog is going to hit, and it's not showing up at all.
- (void)loadView {
NSLog(#"calling loadView");
}
- (id)initWithFrame:(CGRect)frame {
NSLog(#"Calling initWithFrame:");
return self;
}
Strange. I'm not sure why even after proper IB connections that my NSLog will not show up. Only anything put into drawRect: will invoke. Why isn't initWithFrame or loadView ever get hit? What if I want to customize this view programmatically?
First of all, when a view is dehydrated from nib file, instead of initWithFrame, initWithCoder is invoked. So you need to implement your initialization in initWithCoder as well. (It may be a good idea to keep the initWithFrame initialization as well, if you anticipate programmatically creating your TabView instead of hooking up in the IB. Just refactor your initialization to another method and call it from both implementations.)
Also in your initialization code above you must always call the super class's initialization. There is a boiler plate pattern all custom classes use in their init implementation for that:
if (self = [super initXXX]) { do your initialization }
return self;
Second, loadView which is actually a UIViewController method and not a UIView method is invoked only if the view outlet of the controller is nil.
Unless you are composing your view yourself programmatically using your controller, you do not need to override loadView. Instead you should override viewDidLoad, which is called after the view is loaded, to do additional initialization.
The simplest way to get this up and running is simply to use the "View based Application" template when you create a new project. It sets up everything you need to start with.
But, in short, you're looking at the wrong methods. First, you shouldn't override loadView unless you're creating your view programatically. If it's loading from a XIB file look at the initWithNibName method.
You might also want to look at the viewDidLoad, viewWillAppear and viewDidAppear methods that are triggered, well, it's fairly obvious when!