Question

In ObjC we can use protocols to restrict an id behavior, so we can declare something like -(void)aMethod:(id<aProtocol>)aVar which works very well until we provide a value or a non-id variable as aVar, but this gets completely broken since we can pass a generic id variable delcared without protocols specifiers... Is this normal? Is there any workaround? Am I missing something?

Was it helpful?

Solution 3

I (FINALLY) found out that using Objective-C++ is the way to go. Let's suppose I want to be able to pass NSString or NSNumber (instead of a too much generic id and instead of using protocols which become useless passing id values): well, I can create a C++ class having two distinct constructors, one for each ObjC class, so passing id values cannot be done anymore (almost directly). For example, let's take a look at

class NSStringOrNSNumber{
    public:
        NSStringOrNSNumber(NSString *);
        NSStringOrNSNumber(NSNumber *);
};

The great advantage is that methods/functions taking a NSStringOrNSNumber parameter can get NSString/NSNumber values DIRECTLY, since the constructor acts as an implicit cast. In other words, if we have

void aFunction(NSStringOrNSNumber param);

the following calls are perfectly valid:

aFunction(@"Hello!");
aFunction(@25);

The only (little) downside is that we need the class to implement a function if we want to get back the value passed to the constructor.

Using a C++ class constructor to get something like id<NSCoding> is still better the using id<NSCoding> directly: in fact, if we do the following

@class classOne, classTwo;

class NSCodingClass{
    private:
        NSCodingClass(classOne *);
        NSCodingClass(classTwo *);
    public:
        NSCodingClass(id<NSCoding>);
}

we won't be able to pass a generic id as a parameter (since it would be ambiguous: the compiler cannot know which constructor to call among the two private ones)

OTHER TIPS

Just use id less, and declare variables and parameters using the correct types, where possible. That is to say: don't pass ids around. If you are implementing a collections class (for example), then id's often useful.

My approach is to specify types, and introduce that type as local as possible in the source. So I omit id and add the type, and when (for instance) I take a reference from a collection, I create a variable:

MONType<MONProtocol>* thing = [array objectAtIndex:idx];
// now thing is correctly typed. use thing.

Similarly, if I have an id parameter, I declare a new variable:

- (IBAction)someAction:(id)sender
{
  NSButton * button = sender;
  // now use button, not sender

Protocols are extremely useful. Very often, better/cleaner than subclassing.

You're missing the understanding that types in Objective-C are determined at runtime, not compile time. Just because you say that an object will be of type id<aProtocol> does not mean that at runtime it is guaranteed to be so.

The idea of specifying something as id<aProtocol> is to aid you as a developer and people using your code. It aids you as a developer because the compiler will warn (or error under ARC) if you attempt to call a method on something that the compiler can determine it doesn't think exists on instances of its supposed type (excluding forwarding which could mean an instance responds to something the compiler cannot determine). It aids people using your code as it tells them the contract that they should adhere to when interfacing with your code.

So, in your question you say that:

but this gets completely broken if we pass a generic id variable delcared without protocols specifiers

Well, the compiler would warn and tell you that you're trying to pass something that does not conform to that protocol, except for the case of passing id. That's why you generally should try to type things more precisely than just id.

If you have a method defined like so:

- (void)aMethod:(id<aProtocol>)aVar

Then aVar could be of type SomeSubclass where that is defined like so:

@interface SomeSubclass : NSObject <aProtocol>

And you could then use aMethod like this:

SomeSubclass *obj = [SomeSubclass new];
[other aMethod:obj];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top