0

The Situation

My custom controller class has the following method:

- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id)object;

This causes object to receive this message:

- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;

The Problem

Right now, object is just an id so there's no guarantee that it actually implements that method, and I get a compiler warning when I send the message.


The Solution

I came up with two solutions myself:

  1. Use a protocol.
  2. Make id an NSObject and create a category for NSObject.

They are both fine solutions probably and I don't mind choosing one of them, but...


The Question

...I noticed Apple is doing something odd in their GameKit API. GKSession has the following method:

- (void)setDataReceiveHandler:(id)handler withContext:(void *)context

handler is just an id, but Apple actually requires it to implement this method:

- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context;

Without making use of any protocol or category! I'm wondering how and why would they do this? Why don't use a protocol? Do they enforce the method in some other way? If I were to do this, how can I suppress the compiler warning?

Rits
  • 5,105
  • 4
  • 42
  • 53

2 Answers2

2

You can assign a variable of type id to any object type. If you know that it must implement a given protocol you can assign it to a variable of that type within the method and invoke the method on that variable.

As a design point, I would say that it is better to make the protocol explicit and externalise it to the caller so that the compiler can do type checking properly. Some parts of Apple's code are better than others at this: from what you say GameKit is very much at the unhelpful end of things.

Defining a category is not what you want to do, because that tells the compiler that you will add the method to every NSObject.

If you have this protocol:

@protocol YourProtocol <NSObject>
- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;
@end

And define your method as:

- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id <YourProtocol>)object;

It will probably do what you want. It may not be strictly necessary in this case but it's usually a good idea to have your protocol extend the NSObject protocol (as above) so that you can call useful stuff like -retain, -release, -autorelease, and -respondsToSelector. The alternative of declaring the method as follows prevents the user from using an NSProxy-rooted object as the object parameter.

- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(NSObject <YourProtocol> *)object;

If it's just for your own use and you aren't using proxy objects this can be quite convenient, but you should avoid it in public APIs.

Phil Willoughby
  • 1,649
  • 12
  • 16
  • Defining a category actually *will* add the method to every object, with the implementation you provide for the category. – Anomie Feb 27 '11 at 17:31
  • You don't have to implement categories you define; I don't think Rits was suggesting that he would. – Phil Willoughby Feb 28 '11 at 08:32
0

I think the answer you seek is the difference between a formal and an informal protocol. This answer has a good explanation.

Community
  • 1
  • 1
fsaint
  • 8,759
  • 3
  • 36
  • 48