views:

165

answers:

2

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!

A: 

Do you mean the keyword self?

-(void)canHazCheeseburger:(BOOL)canHaz
{
    if (canHaz) {
        self.cheeseBurger = [[[CheeseBurger alloc] init] autorelease];
        [cheeseBurger onNomNom];
    }       
}
Nick Bedford
No, he meant the instance of `LOLCat ` as in:`@implementation LOLCat- (void)meow { CrazyCatLady *crazyCatLady = [[[CrazyCatLady alloc] init] autorelease]; [crazyCatLady canHazCheeseburger:YES];}`
Johan Kool
`self` is the receiver of the message, not the sender of the message (a.k.a. the caller).
Peter Hosey
Ahh, my mistake. You mean, when the message `[a doSomething]` calls `[b doSomethingElse]` you want `[doSomethingElse]` to know about `a`? Only way that'll happen is via a message parameter.
Nick Bedford
+8  A: 

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!

Marc W
Thank You. Very Useful. I've posted a follow-up question below so that I could mark-up the code.
Andrew