Using #DEFINE to share Managed Object Context - iphone

Just looking for a little feedback on if this is a bad idea for sharing the Managed Object Context.
In the MyApp-Prefix.pch file I have added following:
#import "AppDelegate.h"
#define MOC [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]
Then when I need to access the context I do the following (just an example):
[MOC deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
This seems to work great, but it also seems too easy, especially since I don't see it mentioned anywhere. Is this a bad design pattern?
Thanks for any feedback

Instead of a compiler macro (which can cause surprises sometimes) I usually define a class method on my app delegate, like this:
+ (AppDelegate *)sharedDelegate
{
return [[UIApplication sharedApplication] delegate];
}
Then, when I need a reference to some global state, I can call it like this:
[[[AppDelegate sharedDelegate] managedObjectContext] deleteObject:foo];
If your app is complex enough, it might be worth the trouble of passing around a reference to a managed object context, rather than using a global one. If you do, it becomes easier to refactor and use other techniques later, like child contexts to group changes.
To be specific, that means each view controller has it's own managedObjectContext instance variable. When you present a new view controller, you pass along a reference: maybe in a custom init method, maybe by setting a property. None of your classes (except maybe the root view controller) ever reference your global app delegate. This way, if you had a complicated editing view, you could give it a child context where it could save changes "temporarily" (to verify that all the objects are valid); if the user hits the cancel button, you just throw the whole context away.
Also, if you're always using the global context, you might have bugs that are hard to track down. For example, an edit view that doesn't clean up after itself leaves an invalid object behind. Then the next time you go to save something unrelated, you get an error! Happened to me, was no fun to debug.

Nothing wrong with that approach — but unless the delegate's managed object context is going to change during your program's lifetime, it seems more sensible and efficient to use a global variable. The Cocoa AppKit framework takes the second approach with with the NSApp variable, which is set to [NSApplication sharedApplication].

Related

Sharing an object throughout an app

I am currently developing an application for the iPhone. The appdelegate shows a splash-screen while I'm caching data (e.g. NSDictionary) for use in a certain view. What is the best way to call this data from the view I need it in? I don't think passing it along as a variable from view to view until it reaches the view is a correct way to do this.
App Delegate (with Splashscreen that should cache the data to NSDictionary)
|
View A
|
SubView
|
Final View (here I want to use the cached data)
Thanks :-)
If the NSDictionary that you're caching the data in is an ivar of your App Delegate you can access it from anywhere in your app using the following lines:
myAppDelegate *delegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
NSDictionary *myData = [delegate cachedData];
Hope that answers your question.
If you have an object that will never be released throughout the entire life of the app, and really want it to be accessible from absolutely anywhere in the app (say, so that a simple debug NSLog from absolutely anywhere in the code can print it's state), then that's what global variables are for. Just assign a global variable with a reference to the object. If you don't mind generating nearly equivalent but microscopically slower and larger code, then assign it to an instance variable in the app delegate with a suitable getter.
Note that using global variables is a violation of encapsulation that won't be very scalable, maintainable or suitable for unit testing, but is perfectly suitable for a small app which is not much larger than most objects would encapsulate anyway.

How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C

I'm coding a library (Obj-C for iPhone) that I want to package and sell, so I obviously need to work out any design kinks before listing it for sale. I am also utilizing this library to help me develop another app.
My library is heavily built on task delegation. The primary function I have is to launch a (potentially) long-running process, and when it's done, I call a Delegate Protocol method in the class's delegate.
An additional complicating factor here is that I will often schedule this task to fire off every 30 seconds or so. Usually, I do this with [self performSelector:#selector(someMethod:) withObject:nil afterDelay:30] rather than using an NSTimer. Then, when the delegate method successfully returns, I process the returned data and trigger the method to fire in another 30 seconds. This gives me 30 seconds BETWEEN method calls, rather than 30 seconds FROM THE START OF ONE CALL TO THE NEXT. (This is mainly just in case the call ever takes more than 30 seconds, which shouldn't happen.)
The error that I'm catching is that sometimes, the Delegate callback method is failing with an EXC_BAD_ACCESS error. Based upon my investigation, it appears that the delegate of my class library has disappeared (been released/dealloced) since the long-running process was initiated. Thus, when it calls [[self Delegate] doSomeDelegateMethod], it's accessing a released object.
I tried first checking [[self Delegate] respondsToSelector:#selector(doSomeDelegateMethod)], but even that access apparently also throws the EXC_BAD_ACCESS.
It doesn't yet seem that checking for [self Delegate] == nil is the right way to go, either.
One way I think I have solved the problem, in this specific instance, is when the view controller that instantiates my object is disappearing (and therefore on its way to the garbage dump), I call [NSObject cancelPreviousPerformRequestsWithTarget:self]. This apparently fixes the problem. (Does this "fix" also indicate that my object "knows" about the call to come and keeps itself in memory until it can successfully, desperately, fire off its final shot?)
This appears to put a band-aid on a bullet wound. Yes, it appears to stop my app from breaking this time, but my gut tells me that this is a poor solution.
I've also considered setting the custom object to nil in my viewWillDisappear:animated: method, which is probably the correct coding pattern, but it doesn't seem right that the customer has to be so precise in handling my objects.
What's really bugging me, though, is that I haven't yet found a way, as a library developer, to "box in" my code so that it won't throw an exception for the user if they don't do just the right things. Basically, I'd like a way to have my object:
Get a request.
Go look for the answer.
Find the answer.
Try to return the answer.
Realize that there's nothing on the other end.
Give up and die on its own. (OK, so "die on its own" probably won't happen, but you get the point.)
One interesting side point:
A main reason I have for preventing this type of error from occurring is this:
I did the following steps:
Built my library's .h/.m files.
Generated my library's .a output file.
Imported my library's .a/.h files into another project.
Had the error described above.
Got to peruse the code from one of the .m files that SHOULD have been hidden inside the .a file.
Am I missing something here? Am I really risking exposing my entire source code if it ever throws an error for a client? (This is just a side issue, but I'm fairly concerned here!)
Thanks for any help you can provide to help me be a better programmer!
---EDIT---
I have found another reason why this is important. In another view controller, where I am using this library, I implemented the NSTimer strategy. If the view is popped from the navigation stack (i.e., in the viewWillDisappear:animated: method), I invalidate said timer. So, no more calls will go to my library after the view disappears.
Here's the rub: what if the view disappears IN THE MIDDLE of the long-running call? Yes, it's tricky and unlikely to do, but I just had it happen on the simulator. In particular, THIS is why I'm looking for a workaround to let my code realize "hey, there's nothing on the other end of this pipe" and then fail gracefully. Anyone?
Thanks!
There are several approaches to this problem:
The traditional delegate approach (UITableViewDelegate) makes it a requirement to clear yourself as delegate before going away. This is traditionally done in dealloc of the delegate with otherObject.delegate = nil. Failure to do so is a programming error. That's basically what you're seeing. This is the common pattern when the delegate and the delegator have basically the same lifespan.
Another approach is how NSURLConnection handles it: retain your delegate until you're done. The key to this working well is that NSURLConnection has a lifespan of its own, so the retain loop will work itself out automatically. UITableView could not retain its delegate because this would almost always create a permanent retain loop. If your object lives for a while and then goes away, then this makes sense. Typically here the delegate has a much shorter lifespan than the delegator, so the retain loop doesn't hurt anything.
Any object that calls performSelector:withObject:afterDelay: should always call cancelPreviousPerformRequestsWithTarget:self in its own dealloc. This has nothing to do with your delegate, though. It should be self-contained to the object itself. (I don't know why I keep thinking this is true, and then proving to myself again that it isn't. When you call performSelector:...afterDelay:, you are retained, so you can't deallocate before it fires. My SIDE NOTE, while true, isn't relevant here.)
SIDE NOTE cancelPrevious... is really expensive in my experience. If you have to call cancelPrvious... very often, I recommend keeping your own one-shot NSTimer and just resetting it when it fires to get the same effect. performSelector:withObject:afterDelay: is just a wrapper around a one-shot timer.
I'm answering myself because the page warned me to not have extended discussions in the comments... :)
OK, so it appears that part of my answer is that [self performSelector:withObject:afterDelay:] automatically retains my object until it gets to "fire that shot", at which point I'm guessing the view controller dies.
So, now it makes sense why my custom class is trying to access a released object when it tries to return its answer to its delegate, which is an __unsafe_unretained object, meaning that it can die at will (I think).
What I'd like now is a way to prevent this from causing an error. In .NET, I've got all sorts of error handling options to do this, but I'm unable to think of a fail-safe "bail out" here.
I've tried [[self Delegate] isKindOfClass:..., but can't be sure what kind of class the delegate will be, so it won't work.
I've also tried [[self Delegate] respondsToSelector:#selector(...)]. I'm not sure why this fails, but I get the EXC_BAD_ACCESS here, too.
What I don't want is my customers to be able to crash my product with such a simple, innocent mistake.
As an aside, does anyone know why this sort of failure gives me such easy access to the contents of the .m file that should be hidden inside my .a file? Did I build my library incorrectly?
Thanks!
Try setting Delegates to nil in dealloc.
example:
self.fetchedResultsController.delegate = nil;
I've seen this problem a lot lately and usually fix the problem. Even though delegates are supposed to be weak references, sometimes some private implementation is using them as well.
If I release, I get bad access, if I retain, I leak
That's where I had a similar problem.
Edit: When using ARC, you can still override dealloc for cleanup, you just can't call [super dealloc] or release anything.

What's an effective CoreData context/coordinator strategy for this common application pattern?

I have a common application pattern: user enters data in the main view controller, then views it in a table in a modal view controller, where rows can be deleted or modified. I was following the general design strategy from the Stanford iPhone course, but somewhere things went off the rails and all I've been getting is SIGABRT's and exceptions like "Illegal attempt to establish a relationship 'xyz' between objects in different contexts."
As in the Stanford course, I use a singleton class called "Database" which should return the same context whenever requested. So the first commands in my viewDidLoad method on the main view controller are:
dbsingleton = [Database sharedInstance];
nmocontext = [dbsingleton managedObjectContext];
nmocontext is an ivar I use throughout the view controller. When the user wants to see the other view controller, with the table, I alloc-init it, then present it modally. (It has an NSFetchedResultsController that supplies the data from my store.) I've tried various strategies here:
I've made the NSFetchedResultsController a retained property set by the main view controller
I've made the NSManagedObjectContext a retained property set by the main view controller; and
I've used the singleton internally by repeating those two lines of code above at the beginning of the table view controller's viewDidLoad method.
Whichever I go with, the one problem I just can't solve is that after the user closes and deallocs the table view controller (and its NSFetchedResultsController), I start getting crashes in the main view controller when the store is accessed (like the "Illegal attempt" error mentioned above).
What are best practices for dealing with this common application pattern? I am still hoping to make this application iPhone SDK 3.x compatible, but I do seem to have fewer crashes when I'm using iOS 4 -- if there are underlying issues with 3.x that are causing me problems, please let me know and I may target iOS 4 only.
Thank you!
This is just a guess but I assume the following problem for your crashes after closing the tableview:
You declared a property
#property (retain, nonatomic) NSManagedObjectContext* nmocontext;
do you properly release the ivar nmocontext in dealloc?
If yes your problem is the assignment
nmocontext = [dbsingleton managedObjectContext];
This never retains nmocontext within your viewcontroller but you release it on dealloc.
Second:
"Illegal attempt to establish a relationship 'xyz' between objects in different contexts."
This is not a memory management issue, but you probably created another new context to add objects (like in the apple core data iphone samples) and tried to set a NSManagedObject as relationship from a different context.
It sounds like you don't have your singleton properly configured.
A singleton should override release to do nothing such that when it is sent a release message nothing happens. If you do not override release then any random piece of code anywhere in the app can kill the singleton and defeat the entire purpose of using a singleton. The next time you call singleton, you actually get another new object which in this case also returns a new managed object context.
See Cocoa Fundamentals Guide: Creating a Singleton Instance.
Singletons are powerful and flexible but very easy to do wrong. They are so easy to screw up that many experienced developers simply refuse to use them. If don't have experience with them, don't use them when you are just starting out.

Can Objects Call the Main Application?

Please bear with me, this is probably a very simple problem for an expert but I'm new to the language.
I'm trying to create an application that dynamically generates and releases a series of views, which are displayed in the main window. The problem I am having is in swapping between them. I want each view to contain buttons that cause the view to change. I can make a view close itself, but I don't know how to order the main program to change its own view to something new once that's been done (i.e. message the main application from within one of its child objects). I suppose I could make the main view of the application into a global, but I'd rather be able to contact the main application directly as this strikes me as being somewhat neater and less prone to other problems. Problem is, I don't know the main app's identifier. The convenient 'self' identifier gets used so often that I've never seen a code snippet that contains an identifier for the main application that could be called from outside it. It would also be useful to be able to link NIB file objects directly to attributes held in common by the main program.
Am I going about this in entirely the wrong way? Is there a generic 'main app' identifier? Is it possible to create one, if one isn't generated automatically?
-Ash
Assuming that by 'main application' you mean your app delegate, you can get it using this method :
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];

Best Application Delegate Practice

I have been making a few apps here and there, and I know my way around. What has always confused me is accessing global attributes, and where the best place to set them are. I have a few questions about accessing things and how to access them.
Do you have to include your application delegates header file into any other other file you want to access it in? Say I have a view controller, that I would like to use, do I need to include the .h inside my view controller's .h? Or can I set the:
#class AppDelegate;
Can you only access the delegate by typing out:
[UIApplication sharedApplication].delegate
EACH and every time? Is that something I just have to get used to? Or could I set the following in my implementation in each .h:
AppDelegate *delegate;
And inside the init function, put the singleton instance to that variable?
Sorry if this was off structure, but I think it's a logical question people have a problem encountering and solving.
Maybe you need to reconsider how you are using the App Delegate? It sounds to me like perhaps you are not making a very good class design.
Regardless, here's a way to make it easy. Don't put this in init just use it when you need it.
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
Naturally, replace MyAppDelegate with the actual class name of your app delegate.
Another possibility is to add the code to use a properly casted app delegate reference as a #define in the app delegate header file, so after including it you can do something like:
MYAPPDELEGATE.customProperty = blah;
However I tend to favor just writing out the line that John presented, as use of #defines confuses code completion which I find more annoying than just typing the line.
As also mentioned, if you have a ton of references to the app delegate you may want to restructure to keep some of those references closer to home.