How does the "managedObjectContext" method work? - objective-c

I'm just beginning to learn how to use Core Data. The tutorial that I'm learning from feels very helpful so far. In the part where they hook up the view controller where we want to store data to core data, they didn't explain the process of the method that does this "magic".
It's an implementation of the method managedObjectContext, so I thought it could be helpful if I post it here and you can explain to me/us what is the process that is being done here:
- (NSManagedObjectContext *) managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Also for some reason when I'm returning the context object it's not in regular colour when I'm returning a class object, its just white:
Thanks a bunch!

id delegate = [[UIApplication sharedApplication] delegate];
Here, we acquire a reference to the application delegate. This is an instance of the class that XCode generated for you (which should have the name [Prefix of your project]AppDelegate).
[UIApplication sharedApplication] returns a singleton object. Singleton is a design pattern which can basically be summed up as "if you call this method, it will always return the same object." So what that means here is that we are always getting the same application delegate whenever we call this in the program.
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
I'm actually kind of at a loss for why the tutorial wants you to call performSelector: here, respondsToSelector: seems a lot more appropriate, and performSelector: will raise an exception if your delegate object isn't of the right class anyway, so there's no point in guarding the actual message with an if statement.
If you ignore what seems to me to be unnecessary boilerplate, all you're doing is calling a method on the app delegate singleton. You can look it up in your project and see what it does -- judging by the context, it returns an NSManagedObjectContext class.
return context;
Note that this is not actually returning a class object, but instead a instance of NSManagedObjectContext. Therefore, the color white is to be expected.

What that code is doing is
Looking up the app delegate object. This is probably called something like AppDelegate in your project. This code finds the singleton by asking the shared UIApplication instance for its delegate.
Checking that the app delegate has a method named managedObjectContext.
If so, calling that method, and returning the result.
This is a lousy way to do it. Much better would be to declare a property on the view controller:
#property (readwrite, strong) NSManagedObjectContext *managedObjectContext;
Then assign a value to it when creating the view controller
newViewController.managedObjectContext = self.managedObjectContext;
That is, pass the managed object context around, assigning it to whatever object needs to use it. Don't assume that you can always look it up from the app delegate, because that will break if you ever change that part of your app delegate.
Xcode uses that color because it's a local variable in that method. Whether it's an object or some other type doesn't matter.

Related

How to define a global variable in a Cocoa Framework

When you build a Cocoa App, you can add all the global methods and objects in AppDelegate class, and in any place of the code, you can get access to AppDelegate via
AppDelegate* appDlgt = (AppDelegate*)[[NSApplication sharedApplication] delegate];
But how can I do the same thing in a Cocoa Framework? It doesn't seem to have AppDelegete
Thanks
Like this
+ (instancetype)sharedMyClass
{
static id kSharedMyClass = nil;
static dispatch_once_t theOnceToken;
dispatch_once( &theOnceToken, ^{ kSharedMyClass = [[self alloc] init]; } );
return kSharedMyClass;
}
NSApp is the shortcut to your NSApplication instance, which normally have a delegate, or more archaically, a subclass where you could store data.
so if you followed the default project template:
((YourAppDelegate *)[NSApp delegate]).property
will get you where you want to go
don't abuse this, as you should only really store things that belong to the application in the delegate, and even then, those user defined properties should be NSUserDefaults or contained in some other controller class.
edit: sorry, I kind of missed the point....
if you need higher level application data to be passed down into a framework, then you can do so many things, with out knowing the specifics it would be hard to comment on which would be best...
some options:
define a protocol in the framework with the properties you are interested in, the adopt that protocol in your delegate.
then ((id <FrameworkProtocol>)NSApp.delegate).protoPropertywould work.
but that introduces a requirement that the delegate adopts that protocol, which you can check with the conformsToProtocol: method.

ARC - Delegate (assign) is released too early

We are subclassing UITabBarController as well as UITabBarControllerDelegate to handle certain events concerning tab switches.
Now in our custom tab bar controller we have:
- (id)initCustomTabBarController {
self = [super init];
if(self) {
[self setDelegate:[[CustomTabBarControllerDelegate alloc] init]];
// ...
}
return self;
}
Since we transitioned the project to ARC, the delegate is released to early which causes a tab switch run into a deallocated instance.
The property is defined as assign in UITabBarController.h - which I obviously have no influence on.
What can I do to make the delegate object "live" longer than for the init method?
The way you have done it, it is expected that the delegate will not outlive the object, because it is weak. Remember, you created the object, it's up to you to hold on to it.
However - the pattern that you are using is incorrect.
The point of a delegate, is that it provides method implementations to a class that a class can't add for itself, because it doesn't have enough information. For example, a table view delegate. A table view, in order to be generic, cannot know how many rows or sections to display, so it asks it's delegate to supply this information.
In your case, you have an object that is creating it's own delegate. In which case, why bother having a delegate at all? Just implement the methods in the class.
Yes this would be normal under ARC, since no reference to it is made (aka strong propteries) it should be released at the end of cycle.
Just make a property in the class where you assign the CustomTabBarControllerDelegate take make it strong. Then assign this property to the delegate.
In non ARC the way you have set it up you could have create a memory leak.

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

Category on NSObject to get instance of AppDelegate?

I need access to my core data managed object context often, and instead of getting an instance of the [[UIApplication sharedApplication] delegate] and storing it in a variable every time in every class, I was wondering if it would be ok to do this:
#interface NSObject(DelegateExtension)
- (AppDelegate*)appDelegate;
#end
#implementation
NSObject(DelegateExtension)
- (AppDelegate*)appDelegate
{
return (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
#end
so then I can just do self.appDelegate anywhere in my code.
Is anything wrong with doing this that might night be obvious? Is it bad programming practice?
Alternatively, you could add a preprocessor macro (or a static C function) to your Prefix.pch file:
#define AppDelegateInstance() (AppDelegate *)[[UIApplication sharedApplication] delegate]
This will make your app delegate accessible from anywhere in your code, and there is no chance of conflicting with any existing methods named appDelegate.
I usually use a global variable to point to the app delegate.
In MyAppDelegate.h:
extern MyAppDelegate* AppDelegate;
In MyAppDelegate.m:
MyAppDelegate* AppDelegate = nil;
- (id)init {
if (self = [super init]) {
AppDelegate = self;
…
}
}
Now anyone who imports "MyAppDelegate.h" can use the AppDelegate variable to access your app delegate.
NSObject doesn't have any meaningful connection to UIApplication or its delegate. From a design standpoint, I think doing this would be a hack in the bad sense. There are three other solutions that I can come up with off the top of my head that I think would be millions of times better:
Function, whose declaration is in a header which is imported by your prefix header.
Category on UIApplication, which is actually a class that has something to do with the action you're trying to take.
Global pointer, like NSApp on OS X*, that's set up as the first thing in your program to point to the UIApplication instance.
(Four solutions!) Global pointer to the app delegate.
See On lazy instantiation and convenience methods for info on implementing 3 or 4.
*Really don't understand why they didn't do this on iOS.
Of course it's safe, so long as you know that apple doesn't or won't have a method named appDelegate in the near future. Subclassing is the preferred method, especially because NSObject is a root class. But, that would require class posing, something deprecated completely in OSX 10.5+ and never even a viable option on iOS devices.

Where and how should I instantiate an object which will be used globally in an IOS app?

For simplicity's sake, lets say I am making a stopwatch application for ios5. I have a class called stopwatch, which represents the stopwatch's state. This class possesses members that store elapsed time, whether the stopwatch is running, etc.
To me, it seems this class should be instantiated once at the beginning of my apps lifetime, thus I declare it to be a member of the AppDelegate class and instantiate it in the didFinishLaunchingWithOptions: method of the AppDelegate class
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
Stopwatch *stopwatch = [[Stopwatch alloc] init];
return YES;
}
However, I now find myself needing to be able to reference this stopwatch object in the ViewController class but not knowing how to do so.
I would greatly appreciate any advice as to how to approach this problem, as it seems to be a fundamental concept for making ios applications. Thank you!
Don’t use a singleton. There are plenty of reasons why singletons are a bad design decision in most cases, I wrote a blog post about some of them.
Don’t store arbitrary data in the app delegate. That’s just misusing an existing singleton to do what you want. The app delegate should be concerned with the app’s lifecycle events, it should not be a place to stick whatever data you want accessible from everywhere.
One possible correct approach to solve this problem is to have a class that will create you application’s object graph for you and pass the instances to all interested classes. My app delegate’s application did finish launching method mostly look like this:
- (BOOL) applicationDidFinishWhatever
{
Factory *factory = [[Factory alloc] init];
[self setWindow:[factory buildMainWindow]];
[window makeKeyAndSomething];
return YES;
}
And all the object creation and assembly gets done in the Factory class. The Factory will create the timer (and keep the single instance in memory, if needed) and it will pass the timer to view controllers:
- (void) buildMainWindow
{
UIWindow *window = …;
[window setRootViewController:[self buildMainController]];
return window;
}
- (void) buildMainController
{
UIViewController *controller = …;
[controller setTimer:[self buildTimer]];
return controller;
}
I have created a sample Xcode project that shows how to wire an app without singletons. It’s very simple right now, I will add some common use cases later.
You can use Singleton pattern. look here for more info What should my Objective-C singleton look like?
If you "declare it to be a member of the AppDelegate class" then you should use it as a property:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
Stopwatch *aStopwatch = [[Stopwatch alloc] init];
self.stopwatch = aStopwatch;
[aStopwatch release];
...
}
Later, anywhere in your code:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate doSomethingWithStopwatchMethod];
someVariable = appDelegate.stopwatch.timeSinceStart;
...
Another approach would be to use a singleton (check SO for dozens of questions on the subject) or even a plain global variable (an integer holding a flag for instance) declared in main.m.