Can you refer to the sender of a message without passing the sender as a parameter?
This is simplified code for the sake of discussion:
// mainTableViewController.m
[dataModel loadData]; //Table is requesting data based on user input
// dataModel.m
-(void) loadData{
// I want to store the sender for later reference
sendingTableViewController = ???? ;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
// Web data is loaded. Ask the sending tableViewController to
// reload it's data.
[sendingTableViewController.tableView reloadData];
}
I'm still getting used to how to refer to methods and properties that are the responsibility of another object. I want to send a message to dataModel to load some data using NSURLConnection. But I don't just want to return the data because I don't want to sit around waiting for the data to load. I want to send a message to the mainTableViewController once connectionDidFinishLoading is called.
Since the loadData method may be called from any number of tableViewControllers I can't just say [mainTableViewController reloadData].
Follow-Up Question
Great Information! I love the no-judgement nature of StackOverflow.
So the mainTableViewController would be the Delegate of the dataModel?
Would it be correct to say that the dataModel class defines the informal protocol?
I currently instantiate my dataModel class from within my mainTableViewController. So I could change my code like this:
// mainTableViewController.m
dataModel *myDataModel = [[dataModel alloc] initWithDelegate:self ];
// Does this method need to be defined in the mainTableViewController header file
// since I will already have defined it in the dataModel header file?
-(void) dataDidFinishLoading {
[self.tableView reloadData];
}
// dataModel.m
-(id) initWithDelegate:(id)aDelegate{
self.delegate = aDelegate;
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self.delegate dataDidFinishLoading];
}
Is it bad that my TableViewController is instantiating my dataModel, cause then my dataModel is owned by the TableViewController? Should I really instantiate the dataModel from the AppDelegate instead?
Thank You!
I daresay this isn't the correct way to think about the problem. Architecturally, by giving the data model knowledge of the table view controller, you are coupling your model layer to your controller layer. Violating separation of concerns is a bad thing.
In Cocoa, the use of delegate objects is used all over the place. A delegate object is an object that implements a particular protocol with callback methods that can be called when things or events (such as data loading from a remote location, in your case) occur. I recommend that you create a delegate property in your data model, have an interface that mainTableViewController (or any other class, really) implements, and assign that class as the delegate. Then, when the data is finished loading, call the appropriate method on self.delegate. In that callback method, you could then call [tableView reloadData].
Again, you do not want your data model to be coupled (meaning aware of) the existence of your controller classes.
Edit
I just re-read the last part of your question, about having multiple table controllers needing to listen for notification of the data being finished loading. For that, I suggest you use the Observer pattern in Cocoa by using NSNotificationCenter. You use use the notification center in the data model to send notifications to observers (you don't care who is observing; the notification center handles those details) and you'd also use it in your table controllers to subscribe to the notification. Delegates are a nice, simple solution if you only need one object to be directly called when something happens. Notifications are more complex and have more overhead, but give you the flexibility to have an arbitrary number of objects "listening" for a notification to be posted.
Follow-Up Response
A class doesn't define an informal protocol; the developer does. You could also define a formal protocol in a separate .h file and have the controller implement it if you want an enforceable contract. With a formal protocol, you can also use #optional on methods that don't have to be implemented by a class conforming to the protocol.
It is also not at all bad to instantiate the data model from within the table view controller. In fact, this is one very correct way to do it. Since the data model exists to encapsulate data that (presumably) a controller will want to display later, you can think of the controller as owning the data model. You may even consider making an instance variable (and perhaps a property, too) to store your data model. Besides that, your rewritten code looks good to me!
Do you mean the keyword self?
-(void)canHazCheeseburger:(BOOL)canHaz
{
if (canHaz) {
self.cheeseBurger = [[[CheeseBurger alloc] init] autorelease];
[cheeseBurger onNomNom];
}
}
Related
I have been wondering why is NSProxy class so important. Why does an object need to keep its instance variables inside other objects? I need examples to understand when to use it. Thanks!
NSProxy is useful when there is a need for delegate interception, let's say you have some styled UISearchBar across your app, in which you remove the search icon when user starts typing, it means you need to listen UISearchBarDelegate method -searchBar:textDidChange: but this method is already listened by ViewController which performs searching, to avoid code duplications you don't want to copy-paste hiding icon logic in every your ViewController. To solve this problem you can create NSProxy which will have reference to your ViewController as originalDelegate and your hiding search icon helper as middleMan, then in your NSProxy instance you need implement following methods:
- (void)forwardInvocation:(NSInvocation *)invocation
{
if ([self.middleMan respondsToSelector:invocation.selector])
{
//Note: probably it's better to provide a copy invocation
[invocation invokeWithTarget:self.middleMan];
}
if ([self.originalDelegate respondsToSelector:invocation.selector])
{
[invocation invokeWithTarget:self.originalDelegate];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
id result = [self.originalDelegate methodSignatureForSelector:sel];
if (!result) {
result = [self.middleMan methodSignatureForSelector:sel];
}
return result;
}
And set your proxy instance to searchBar delegate: searchBar.delegate = proxy
Example A: Imagine you'd be writing an object persistence layer (like CoreData, but much better of course ;) ).
Let's say you can fulfill a query for thousands of items in your database really quick by just looking at the index-tree, without the cost of reading and initializing the complete item.
You could use NSProxy to implement lazy-loading. Use your index table to locate the primary key of the object, but instead of creating that object, return an NSProxy that knows the primary key of the real object.
Only when another database lookup is required, the proxy object creates the item and redirect all future messages to it. The calling code would only deal with the NSProxy item, and never now about the lazy-loading performed under the hood.
Example B (this is OS X, sorry): NSOutlineView behaves really odd, when you have the same item in the outline hierarchy twice. Very common problem when you have a smart group feature in your app. The solution: use different proxies in the outline view, pointing to the same object.
I am wondering if the NSUserDefaults object is shared and can be accessed from within the app delegate as well as within several of my view controllers. Basically I need to pass data from the app delegate back and forth to the view controllers. I don't want to use a singleton. I was wondering if the NSUserDefauflts object was a way to do this.
If this is possible, how would I initialize the object so that is possible?
Thanks!
If you just use [NSUserDefaults standardUserDefaults], the same instance will be returned every time. Different classes can then use it to store data that is persistent across sessions.
If you're just trying to pass data between parts of the app, but not store it, user defaults are not the appropriate way to do so. You should expose methods or properties on your classes that take as input the data you need to pass.
Well, it is but that's not really what it's designed for. The normal design pattern is to pass the objects back and forth between your view controllers "manually." You want your view controllers to be as independent -- reusable -- from the rest of your application as possible. Tying them to NSUserDefaults isn't a good way to do that!
You should not be doing any processing in your app delegate. Ideally, you should initialise your window, root view controller (if not doing it by storyboard) and model and that's it. All processing should be done elsewhere (mostly in view controllers talking to the model classes).
Make your root model class a singleton so that all your view controllers can talk to it via an interface of your choosing.
Making a singleton is not hard:
#interface MyModel: NSObject
+ (MyModel *)sharedModel;
#end
and the implementation:
#implementation MyModel
+ (MyModel *)sharedModel
{
static MyModel* modelSingleton = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
modelSingleton = [[MyModel alloc] init];
});
return modelSingleton;
}
#end
And then you just use:
[MyModel sharedModel]
to access it.
I currently have a UIViewController and a NSObject class.
What i want to do is to tell the NSObject class to perform an action and then tell the UIViewController when it has finished it's action.
I'm calling the object to perform it's action like so:
[fooObject performActionWithDelegate:self];
The performActionWithDelegate function basically only takes the UIViewcontroller's delegate to perform a callback.
-(void)performActionWithDelegate:(id)d{
// bar is declared in the fooObject header file
// id bar;
[bar setDelegate:d];
[bar performCallback];
}
Where performCallback is a simple NSLog()-statement in the UIViewController:
-(void)performCallback{
NSLog(#"Successfully performed a callback");
{
Now, i'd like this to work. My first guess is that this is not the best approach to this problem.
The full scope of the problem is that the fooObject is supposed to perform a httppost to a webservice to update one of it's properties and then inform the uiviewcontroller if the operation was successful or not.
How do i achieve this?
Any tips and/or pointers will be highly appreciated!
Thanks in advance.
Edit:
The actual problem is that the fooObject is not performing the callback.
Its not clear exactly what you are trying to accomplish. What you are doing seems related to two different design patterns:
Delegate
Asynchronous callback
Delegate: You would use a delegate if there is some reason to separate out some of the functionality of your UIViewController into another object. Instead of the UIViewController doing something it asks another object to do it. This is commonly used for code reuse so you can have the same UIViewController serve in different cases and just change the delegate to change some of its behavior.
Asynchronous callback: This allows an operation to occur in the background while you are doing other things and then be notified by calling a method of your object when the operation completes. You can do this without involving other objects.
In your case, why do you want to perform an HTTP post to a web service outside of our UIViewController? Do you just want to separate the network code from UI code? In this case, you don't really need a delegate, just call the method on the other object from your UIViewController and when it returns, its done. It can pass back any result you need in other parameters. Returning values by setting properties on the calling object is not generally a very good design. Even if you do this the UIViewController isn't really a "delegate".
On the other hand if you are concerned about blocking the main thread while the HTTP post is in process then you will want to use something like asynchronous callback. The easiest way to do this is to use Grand Central Dispatch. Conceptually you could do something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doLongHTTPPost];
dispatch_async(dispatch_get_main_queue(), ^{ [self longHTTPPostDone]; });
});
This will call doLongHTTPPost in the background and then at some later time after it is complete it will call longHTTPPostDone on the main thread where it is safe to take UI actions.
I am using CoreData in iOS.
I have a modal AddViewController, with a RootViewController as the delegate.
Should I do the saving of the managed object context in the root controller, or in the actual modal controller itself?
I am seeing examples of passing the data back to the rootViewController, but can't see how this will work with data validation failures, or more complex scenarios that need the managed object context.
// AddViewController.m
- (void)save
{
[[self delegate] controller:(id)controller didFinishWithSave:(BOOL)save withName(NSString *)name;
}
// RootViewController.m
- (void)controller:(id)controller didFinishWithSave:(BOOL)save
{
if (save)
{
// save context
}
[self dismissModalViewControllerAnimated:YES];
}
As at the moment I am dismissing the modal using the delegate protocol.
Any advice on the best practices?
Thanks
I know this is a little late, but for others who come across this, I'd add a little more to Levend's answer.
Yes, you should have a separate manager class to handle the operations (possibly the class itself).
I would think that if your modal needed to handle validations, before calling on the delegate, it would validate the object. If it had an error, you could handle it right there. If it passed validations, then you could pass the valid object to the delegate. Thus the delegate can make the assumption that any object that is getting passed to it is a valid one.
As to which class you should save it, I think that is just a preference thing. But I would suggest you have a save method in the core data model itself.
Something I came across recently is the mogenerator which is supposed to help with managing CoreData classes. I haven't tired it yet, but it sounds good.
From an architecture point of view, you should create a separate manager class responsible for core data operations. From technical point of view, it isnt matter where you save(root, or modal VC) as long as you do it on the same thread where the data origins.
With MVC in mind I would let the M(odel) perform saving of the context as well as fetching data from it.
I'm working with a Navigation Controller based iOS app. There are multiple tableView screens that pull and save data from a Core Data persistent store. Most of the data for the different table views comes from NSFetchedResultsController instances or NSFetchRequests.
The app works as intended but I have been getting a few random crashes and glitches that seem to be related to Core Data. For example sometimes when I save the context the app will crash but not always. Another thing I've been seeing is the very first tableView doesn't always update the reflect the data that was modified in it's detail view.
Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers by setting the context property of the view controller just before I push it onto the navigation stack.
This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?
I noticed in one of the WWDC sessions using delegation but I've never used creating my own delegates before and haven't been able to puzzle it out of the WWDC session.
Thanks.
=)
Use singleton NSManagedObjectContext for all Controllers isn't a best practice.
Each Controller should have your own Context to manage specific, sometimes atomic, operations at document store.
Think if you can edit a NSManagedObject attached to Controller that pass the same Context to other Controller that will select another instance to delete or edit.. you can lost the controll about modified states.
When you create a view controller, you pass it a context. You pass an
existing context, or (in a situation where you want the new controller
to manage a discrete set of edits) a new context that you create for
it. It’s typically the responsibility of the application delegate to
create a context to pass to the first view controller that’s
displayed.
http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
1)
Use a singleton for your CoreData setup (NSPesistentStoreCoordinator, NSManagedObjectModel & NSManagedObjectContext). You can use this singleton to execute the fetch requests you created in your Models and to add or delete Entities to your Context.
2)
Delegates are not that hard. Following is a sample:
#class SomeClass
#protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value
#optional
- (NSString *) thisMethodIsOptional;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate> delegate;
//Other instance variables omitted.
}
#property (assign) id<SomeClassDelegate> delegate;
#end
#implementation SomeClass
#synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate {
NSString *hello = #"Hello";
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(someClassInstance:givesAStringObject:)]) {
[self.delegate someClassInstance:self givesAStringObject:hello];
}
}
#end
Option 1 could be something like this, you will have to setup the variables in the init of the object (and implement the singleton ofcourse):
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataUtility : NSObject {
#private
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;
#end
Currently I'm passing around a single Managed Object Context that was
created in the app delegate to each of the different view
controllers...This seems like a clunky, hacky way of getting the job
done. Is there a better design pattern to use?
There's nothing particularly special about a managed object context in this respect, it's just another object that your view controller may need to do its job. Whenever you're setting up an object to perform a task, there are at least three strategies that you can use:
Give the object everything it needs to get the job done.
Give the object a helper that it can use to make decisions or get additional information.
Build enough knowledge about other parts of the application into the object that it can go get the information it needs.
What you're doing right now sounds like the first strategy, and I'd argue that it's often the best because it makes your view controllers more flexible, less dependant on other parts of the app. By providing the MOC to your view controllers, you leave open the possibility that you might someday use that same view controller with a different context.
Jayallengator makes the helpful observation that every managed object has a reference to its context, and if you're passing around specific managed objects you don't also need to pass along the context. I'd take that a step further: if you're passing specific managed objects to your view controller, the view controller often won't need to know about the context at all. For example, you might keep Game objects in your data store, but a GameBoardViewController will probably only care about the one Game that's being played, and can use that object's interface to get any related objects (Player, Level, etc.). Perhaps these observations can help you streamline your code.
The second strategy is delegation. You'll usually use a protocol when you use delegation, so that your object knows what messages it can send its helper without knowing anything else about the helper. Delegation is a way to introduce a necessary dependency into your code in a limited, well-defined way. For example, UITableView knows that it can send any of the messages defined in the UITableViewDelegate protocol to its delegate, but it doesn't need to know anything else about the delegate. The delegate could be a view controller, or it could be some other kind of object; the table doesn't care. The table's delegate and data source are often the same object, but they don't have to be; again, the table doesn't care.
The third strategy is to use global variables or shared objects (which is what people usually mean when they talk about singletons). Having a shared object that you can access from anywhere in your code is certainly easy, and you don't have that "klunky" extra line of code that configures your object, but it generally means that you're locking your view controllers in to using that shared object and no other. It's a lot like gluing a hammer to your hand because you know for certain that that hammer is the tool you need. Works great for pounding nails, but it can be painful if you later discover that you'd like to use the same hand for driving screws or eating dinner.
The singleton approach seems to be best-practice, but another trick I found useful was that in cases where you're passing a NSManagedObject from one view controller to the next anyway (usually as an instance variable), you don't need to also pass the NSManagedObjectContext since you can get the context from the object you passed in by invoking [myManagedObject managedObjectContext]. This can be a handy shortcut when there's maybe only one or two methods where you need the context and you don't want the overhead of creating yet another NSManagedObjectContext ivar/property.