ObjC: restrict id to some types - objective-c

In Objective-C, is it possible to restrict id to just a few types? I want to write a method having an id parameter, but this method applies only to some ObjC types: using id could lead to runtime errors. Is there any LLVM convention or something like that?

id is a generic Objective-C object pointer, ie it means any object.
The only way you could restrict the type would be using protocols:
id <myProtocol>
Therefore, in this way, you point to any object which adopts the myProtocol protocol.

As long as you're dealing with objects, you can ask for it's class:
id anId;
if ([anId isKindOfClass:[NSNumber class]]) {
...
}

"restricting" id is not something Objective-C has. Anyways, if you pass an object of which the type doesn't match the type specified in the method declaration, you would only get a warning and not a compiler error (unless you compile using -Werror), so the compiler can't really prevent you from doing this.
Yes, this is runtime-error-prone, but that's how Objective-C works. One thing you should do is documenting which types are accepted.
One thing you can do is checking the type at runtime, either by using the isKindOfClass: or isMemeberOfClass: methods of NSObject. Also, if there are a common set of messages the object should respond to, you can wrap them into a protocol and require an expression of type id <MyProtocol>.

Related

Objective-C id & NSObject

I understand that id is for any Object type even objects that do not inherit NSObject such as things from Cocoa. I have been told to almost always use id but what if I were making an API and had a method that I wanted to make it clear that it should only take a certain type of object such an object called Animal, would I still use
(id) animal
or would I do
(Animal) animal
Thanks so much!
id is a generic pointer to an object -- it's like void *, except that the pointer must point to an Objective-C object. So yes, you could use id in most situations where a more specific object pointer type would work, but it's usually better to use the more specific type:
- (id)animal; // OK if 'animal' could be any type of object
- (Animal*)animal; // much better if you know that 'animal' points to an object of type 'Animal'
You'll find plenty of examples if you look at any Cocoa or Cocoa Touch class. Let's look at a little bit of UIView:
- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
- (UIView *)viewWithTag:(NSInteger)tag; // recursive search. includes self
As you can see, the first method takes a UIView* as a parameter. If you try to pass something other than a pointer to an instance of UIView, the compiler will complain.
The second method returns a UIView*, and you can use the result directly as the receiver of other messages that UIView understands:
[[topView viewWithTag:someTag] removeFromSuperview];
Being specific about the types of parameters and return values lets the compiler help you make sure that you're sending appropriate messages to your objects and getting appropriate values back.
You can use any type starting from Animal and then up through inheritance chain to NSObject and id. Any would be valid. But in most cases you need to use just Animal because this is the very type you need to work with

ObjC protocols potentially useless

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?
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];
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)

Would it be beneficial to begin using instancetype instead of id?

Clang adds a keyword instancetype that, as far as I can see, replaces id as a return type in -alloc and init.
Is there a benefit to using instancetype instead of id?
Yes, there are benefits to using instancetype in all cases where it applies. I'll explain in more detail, but let me start with this bold statement: Use instancetype whenever it's appropriate, which is whenever a class returns an instance of that same class.
In fact, here's what Apple now says on the subject:
In your code, replace occurrences of id as a return value with instancetype where appropriate. This is typically the case for init methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of id to return instancetype, it doesn’t convert other methods. Objective-C convention is to write instancetype explicitly for all methods.
Emphasis mine. Source: Adopting Modern Objective-C
With that out of the way, let's move on and explain why it's a good idea.
First, some definitions:
#interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
#end
For a class factory, you should always use instancetype. The compiler does not automatically convert id to instancetype. That id is a generic object. But if you make it an instancetype the compiler knows what type of object the method returns.
This is not an academic problem. For instance, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] will generate an error on Mac OS X (only) Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes. The reason is that both NSFileHandle and NSURLHandle provide a writeData:. Since [NSFileHandle fileHandleWithStandardOutput] returns an id, the compiler is not certain what class writeData: is being called on.
You need to work around this, using either:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
or:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Of course, the better solution is to declare fileHandleWithStandardOutput as returning an instancetype. Then the cast or assignment isn't necessary.
(Note that on iOS, this example won't produce an error as only NSFileHandle provides a writeData: there. Other examples exist, such as length, which returns a CGFloat from UILayoutSupport but a NSUInteger from NSString.)
Note: Since I wrote this, the macOS headers have been modified to return a NSFileHandle instead of an id.
For initializers, it's more complicated. When you type this:
- (id)initWithBar:(NSInteger)bar
…the compiler will pretend you typed this instead:
- (instancetype)initWithBar:(NSInteger)bar
This was necessary for ARC. This is described in Clang Language Extensions Related result types. This is why people will tell you it isn't necessary to use instancetype, though I contend you should. The rest of this answer deals with this.
There's three advantages:
Explicit. Your code is doing what it says, rather than something else.
Pattern. You're building good habits for times it does matter, which do exist.
Consistency. You've established some consistency to your code, which makes it more readable.
Explicit
It's true that there's no technical benefit to returning instancetype from an init. But this is because the compiler automatically converts the id to instancetype. You are relying on this quirk; while you're writing that the init returns an id, the compiler is interpreting it as if it returns an instancetype.
These are equivalent to the compiler:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
These are not equivalent to your eyes. At best, you will learn to ignore the difference and skim over it. This is not something you should learn to ignore.
Pattern
While there's no difference with init and other methods, there is a difference as soon as you define a class factory.
These two are not equivalent:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
You want the second form. If you are used to typing instancetype as the return type of a constructor, you'll get it right every time.
Consistency
Finally, imagine if you put it all together: you want an init function and also a class factory.
If you use id for init, you end up with code like this:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
But if you use instancetype, you get this:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
It's more consistent and more readable. They return the same thing, and now that's obvious.
Conclusion
Unless you're intentionally writing code for old compilers, you should use instancetype when appropriate.
You should hesitate before writing a message that returns id. Ask yourself: Is this returning an instance of this class? If so, it's an instancetype.
There are certainly cases where you need to return id, but you'll probably use instancetype much more frequently.
There definitely is a benefit. When you use 'id', you get essentially no type checking at all. With instancetype, the compiler and IDE know what type of thing is being returned, and can check your code better and autocomplete better.
Only use it where it makes sense of course (i.e. a method that is returning an instance of that class); id is still useful.
Above answers are more than enough to explain this question. I would just like to add an example for the readers to understand it in terms of coding.
ClassA
#interface ClassA : NSObject
- (id)methodA;
- (instancetype)methodB;
#end
Class B
#interface ClassB : NSObject
- (id)methodX;
#end
TestViewController.m
#import "ClassA.h"
#import "ClassB.h"
- (void)viewDidLoad {
[[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime
[[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible #interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}
You also can get detail at The Designated Initializer
**
INSTANCETYPE
**
This keyword can only be used for return type, that it matches with return type of receiver. init method always declared to return instancetype.
Why not make the return type Party for party instance, for example?
That would cause a problem if the Party class was ever subclassed. The subclass would inherit all of the methods from Party, including initializer and its return type. If an instance of the subclass was sent this initializer message, that would be return? Not a pointer to a Party instance, but a pointer to an instance of subclass. You might think that is No problem, I will override the initializer in the subclass to change the return type. But in Objective-C, you cannot have two methods with the same selector and different return types (or arguments). By specifying that an initialization method return "an instance of the receiving object," you would never have to worry what happens in this situation.
**
ID
**
Before the instancetype has been introduced in Objective-C, initializers return id (eye-dee). This type is defined as "a pointer to any object". (id is a lot like void * in C.) As of this writing, XCode class templates still use id as the return type of initializers added in boilerplate code.
Unlike instancetype, id can be used as more than just a return type. You can declare variables or method parameters of type id when you are unsure what type of object the variable will end up pointing to.
You can use id when using fast enumeration to iterate over an array of multiple or unknow types of objects. Note that because id is undefined as "a pointer to any object," you do not include an * when declaring a variable or object parameter of this type.
The special type instancetype indicates that the return type from the init method will be the same class as the type of object it is initializing (that is, the receiver of the init message). This is an aid for the compiler so that it can check your program and flag potential
type mismatches—it determines the class of the returned object based on context; that is, if you’re sending the init message to a newly alloc’ed Fraction object, the compiler will infer that the value returned from that init method (whose return type has been declared as type instancetype) will be a Fraction object. In the past the return type from an initialization method was declared as type id. This new type makes more sense when you consider subclassing, as the inherited initialization methods cannot explicitly define the type of object they will return.
Initializing Objects, Stephen G. Kochan, Programming in Objective-C, 6th Edition

Using (id) in Objective-C

I have a function that I want to operate on two different custom objects. My first thought was to accept the argument as an (id) and operate on the id object. I can't quite seem to figure out how to do that, however.
Both classes (say apples and oranges) have interface variables:
NSDecimalNumber *count;
I want to do something similar to this:
-(NSDecimalNumber*)addCount:(id)addObject{
return [count decimalNumberByAdding:addObject.count];
}
I can't seem to figure out the syntax to make that happen. Is this the proper approach, or would it be better to subclass (from say a fruit class) and operate on the parent class?
-(NSDecimalNumber*)addCount:(Fruit*)addFruit{
return [count decimalNumberByAdding:addFruit.count];
}
While you can send a message to any object (id) - property accessors require that the compiler be aware of the type you are dealing with - this is because property accessors are syntactic sugar around calling specific getter and setter methods.
You have a few of ways of working around this:
Instead of accessing the count property, call the corresponding [getCount] methods.
If the different classes have different versions of this method, you can use a runtime type check:
Provide a base class for both types so that you can pass in something more specific than (id).
Define and implement a Protocol that both objects implement that defines a count property (or method).
Example of a dynamic type check:
if( [object isKindOfClass:[Apple Class] )
// call one overload of getCount
else if( [object isKindOfClass:[Orange Class] )
// call another overload of getCount
Personally, I favor strong typing in my code because it makes it easier to understand the intent. It also allows the IDE to support your coding effort with intellisense, static analysis, and refactoring features. So, in your case, I would use either #3 or #4 as an approach - depending on whether inheritance is really appropriate for the problem.
You should try not to access instance variables from another class.
In Objective-C it's enough that the two objects respond to the same selector (say count), however that would give you a compiler warning.
There are two ways you can get rid of this warning: either by subclassing from a common Fruit class or by having your two classes conform to a protocol. I'd go with the protocol:
#protocol FruitProtocol
- (NSDecimalNumber *)count;
#end
#interface Orange : NSObject<FruitProtocol>
#end
#interface Apple : NSObject<FruitProtocol>
#end
Then your method can look like this:
-(NSDecimalNumber*)addCount:(id<FruitProtocol>)addFruit {
return [count decimalNumberByAdding:[addFruit count]];
}
Here you are saying that your addCount expects any object that conforms to the FruitProtocol protocol, and hence can respond to the count selector, so the compiler will accept it.
The fact that you are trying to access 'addFruit.count' is the problem. The dot syntax is only for properties declared with #property (or for structs). If you change it to
[addFruit count]
and add
-(NSDecimalNumber*)count
{
return [[count retain] autorelease];
}
to each class, then it would work. However, you will notice you'll get a warning saying 'id' may not respond to the 'count' message, and unless you can be absolutely sure the items sent to this method implement a 'count' method, this is a problematic approach.
I agree with pgb's approach. You should define a protocol, and declare both classes to implement that protocol. This eliminates the problem of not knowing whether the object will respond to 'count' or not, as you now have a 'contract' of sorts.
If you want to keep the dot syntax with a property, you can declare it in the protocol:
#protocol FruitProtocol
#property(readonly) NSDecimalNumber * count;
- (NSDecimalNumber *)count
#end
and then, your function would be:
-(NSDecimalNumber*)addCount:(id<FruitProtocol>)addObject{
return [count decimalNumberByAdding:addObject.count];
}
You're sending the message to count, what is count? id is a pointer to any type of object. If you expect the object to have a count property, then you should only be able to pass in an Array (or some other type restriction).
-(NSDecimalNumber*)addCount:(NSArray*) Object{
return [count decimalNumberByAdding: [Object count]];
}
As I understand it, id does not have any methods or variables associated with it because it is a generic pointer that does not refer to any specific class. This page has some good info on ids if you scroll down a bit.
anObject this will not have a count variable, which is why your first attempt won't work. Creating a base class and using that as a parameter to the method seems like the best idea to me.

What's the difference between declaring a variable "id" and "NSObject *"?

In Objective-C, what's the difference between declaring a variable id versus declaring it NSObject *?
With a variable typed id, you can send it any known message and the compiler will not complain. With a variable typed NSObject *, you can only send it messages declared by NSObject (not methods of any subclass) or else it will generate a warning. In general, id is what you want.
Further explanation: All objects are essentially of type id. The point of declaring a static type is to tell the compiler, "Assume that this object is a member of this class." So if you send it a message that the class doesn't declare, the compiler can tell you, "Wait, that object isn't supposed to get that message!" Also, if two classes have methods with the same name but different signatures (that is, argument or return types), it can guess which method you mean by the class you've declared for the variable. If it's declared as id, the compiler will just throw its hands up and tell you, "OK, I don't have enough information here. I'm picking a method signature at random." (This generally won't be helped by declaring NSObject*, though. Usually the conflict is between two more specific classes.)
id means "an object", NSObject * means "an instance of NSObject or one of its subclasses". There are objects in Objective-C which are not NSObjects (the ones you'll meet in Cocoa at the moment are NSProxy, Protocol and Class). If some code expects an object of a particular class, declaring that helps the compiler check that you're using it properly. If you really can take "any object" - for instance you are declaring a delegate and will test all method sends with respondsToSelector: calls - you can use an id.
Another way to declare an object variable is like "id <NSObject>", which means "any object which implements the NSObject protocol.
From my limited understanding of Objective-C, not all objects are derived from NSObject (unlike Java where all objects derive from Object). You can theoretically have other root objects. id could apply to any of those non-NSObject derived objects.
I would like to add another difference. When you add a protocol to id, it does not longer mean that it will be of type NSObject *, it just means that it will be any class that confirms to that protocol.
So, for example, this code will not throw any error, since NSObject's category NSDelayedPerforming has that method:
id testId;
[testId performSelector:#selector(isKindOfClass:) withObject:[NSObject class] afterDelay:.5];
However, this code will show the error No known instance method for selector "performSelector:withObject:afterDelay:":
id<NSMutableCopying> testId;
[testId performSelector:#selector(isKindOfClass:) withObject:[NSObject class] afterDelay:.5];