How to cast Class object to conformance witih protocol - objective-c

I'm trying to cast a Class object to a certain protocol, which defines class methods (+) that that class implements.
I know how to do this with (id< protocol>), as outlined in this question, but I can't seem to figure out the right way for Class objects.
The basic scenario is as follows.
I have a protocol:
#protocol Protocol <NSObject>
+ (id)classMethod:(id)arg;
#end
I then have a function which accepts a Class object, which it knows sometimes conforms to the protocol based on another argument (this is obviously very simplified):
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if (arg != nil) {
instance = [(Class<Protocol>)cls classMethod:arg];
}
}
Now I don't get any warnings on this, and it looks right to me. (I'm not going to see any errors in any case, because I can guarantee that if arg != nil then the class conforms.)
However, I'm not getting autocompletion in Xcode, which makes me wonder if this is the right way to do it. Any thoughts? (Note that I am not interested in instance being id< Protocol>.)

If you want to determine whether cls conforms to a particular protocol (and assuming that classMethod: is a required class method of that protocol), you can simply:
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if ([cls conformsToProtocol:#protocol(Protocol)]) {
instance = [cls classMethod:arg];
}
return instance;
}
Alternatively, just see if it responds to a particular class method selector:
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if ([cls respondsToSelector:#selector(classMethod:)]) {
instance = [cls classMethod:arg];
}
return instance;
}

The question is 11 years old and there is nothing wrong with the Rob's answer, but I find it unfortunate that the centrepiece part of it (whether type-casting a Class object with a protocol is a correct syntax) never got proper attention.
First of all static typing in Objective-C is very artificial thing, and it exists solely for the compiler to emit a warning (not even an error). Let's start with what Class objects really is - if you take a look at the documentation, you will find that the Class type is actually an alias for objc_class * type:
typedef struct objc_class *Class;
You can find definition of objc_class type in the source codes of Apple's objc runtime library:
// inherits objc_object with some adjustments
struct objc_class : objc_object { ... }
As you can see, objc_class is just an extension to a objc_object. Any Objective-C class is in fact instance of this objc_object. E.g. here is how NSObject or id aliases look like:
// "translation" of an Objective-C class declaration
typedef struct objc_object NSObject;
// the same for `id` type but with the pointer type included
typedef struct objc_object *id;
It means that "static typing" doesn't exist in Objective-C, the "typing" of an instance happens via introspection of a given instance (different kind of meta-information objc_object stores). It makes all Objective-C classes compatible with each other (first - because it's a pointer, second - because it's a pointer to the same structure). E.g. you can write code like this:
Class obj = [NSObject new];
..and it will happily compile.
However this purely dynamic nature of the language makes it very error-prone, exposing all kinds of mistakes a programmer can make. In order to avoid that clang in fact does compile time checking of the specified types, but it purely relies on the programmer to provide correct data for a type of an instance, and if the types are incompatible from Objective-C perspective, the compiler can emit a warning for you. This works for instance objects, but unfortunately there is no syntax in Objective-C to type a class object other than with the Class alias. It means that for the compiler all such objects are indistinguishable during compile time.
And all of this is true for protocols typing. Here I mean that when you add a protocol conformance token to a variable type (id<TDWLoadable> var) you merely ask the compiler to check whether the assigned to the variable object conforms to the given protocol:
#protocol TDWLoadable
+ (void)classMethod;
- (void)instanceMethod;
#end
#interface TDWObject : NSObject
#end
// Initializing '__strong id<TDWLoadable>' with an expression of incompatible type 'TDWObject *'
id<TDWLoadable> loadable = [TDWObject new];
For a class object, however, the same check is just ignored, because Class objects cannot be typed:
Class<TDWLoadable> loadable = [[TDWObject new] class];
This behavior is described in the Type Checking section of Protocols part in The Objective-C Programming Language (emphasis mine):
...the declaration
id <Formatting> anObject;
groups all objects that conform to the Formatting protocol into a type, regardless of their positions in the class hierarchy. The compiler can make sure only objects that conform to the protocol are assigned to the type.
In each case, the type groups similar objects—either because they share a common inheritance, or because they converge on a common set of methods.
The two types can be combined in a single declaration:
Formatter <Formatting> *anObject;
Protocols can’t be used to type class objects. Only instances can be statically typed to a protocol, just as only instances can be statically typed to a class. (However, at runtime, both classes and instances respond to a conformsToProtocol: message.)
Also, if we take into account that objc_class is in fact just an extension to objc_object then two expressions of kind:
Class<TDWLoadable> classObj;
id<TDWLoadable> obj;
Should follow the same contract (i.e. + (void)classMethod has to refer to metaclass of classObj and - (void)instanceMethod to the class object itself).
Having that said, since the syntax essentially has no effect and just ignored by the compiler, you are free to come up with your own convention to the Class<Protocol> typing.

Related

ObjC: restrict id to some types

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>.

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

Why id is generic pointer?

I want to know why id is a weak reference pointer,how it is able to handle any class type pointer and at run time how can we detect that which type of class pointer is assigned to id.
Why is id a weak reference pointer?
id is not a weak reference pointer, at least not in the ARC ownership sense. Whether an id-typed reference to an object is weak or not depends on the reference having been declared __weak (and variations) and the object’s class actually supporting weak references.
However, you could say that id provides weak typing, although I think that dynamic/duck typing is a more accurate description. Since an id- typed reference contains no compile-time class-type information, the compiler isn’t able to, for example, determine if the underlying object can respond to a given selector, which could lead to runtime errors.
How is it able to handle any class type pointer?
That’s part of the definition of the Objective-C language. The compiler recognises id as being the supertype of every Objective-C class, and it treats id differently. See the answer below as well.
At runtime, how can we detect that which type of class pointer is assigned to id?
In Apple’s Objective-C runtime, the first bytes in the memory allocated to an object must point to that object’s class. You might see this referenced elsewhere as the isa pointer, and that’s how Apple’s runtime finds out the class of every1 object. The id type is defined to have this information as well. In fact, its only attribute is the isa pointer, which means that all1 Objective-C objects conform to this definition.
If you have an id reference and want to discover the class of the referenced object, you can send it -class:
id someObject;
// Assign something to someObject
// Log the corresponding class
Class c = [someObject class];
NSLog(#"class = %#", c);
// Test whether the object is of type NSString (or a subclass of NSString)
if ([someObject isKindOfClass:[NSString class]]) {
NSLog(#"it's a string");
}
1Tagged pointers are a notable deviation of this structure, and (partly) because of them one shouldn’t access the isa pointer directly.
It's nice to have a generic object type, so you can define collection types that can hold any kind of object, and other generic services that work with any object without knowing what kind of object it is.
There is no trick to make id work. At a binary level all pointers are interchangeable. They just represent a memory address as a numerical value. To make id accept any type of pointer, it's only necessary to disable the rules of the compiler that normally require pointer types to match.
You can find out information about the class of an id type variable in these kinds of ways:
id theObject = // ... something
Class theClass = [theObject class];
NSString *className = NSStringFromClass(theClass);
NSClassDescription *classDescription = [NSClassDescription classDescriptionForClass:theClass];
But it's rarely necessary to do those kinds of things in code. More often, you want to test if your id variable is an instance of a particular class, and if so cast it to that class and start treating it as that type.
if ([theObject isKindOfClass:[MySpecializedClass class]]) {
MySpecializedClass *specialObject = (MySpecializedClass *)theObject;
[specialObject doSomethingSpecial];
}
If you were to use -class to find out the class, but it returned a class you know nothing about, then there's nothing special you can do with the object based on its class anyway. So there is no reason to do anything but check if it matches classes you know about, and only if you intend to do special handling for those classes anyway.
You can sometimes use isMemberOfClass instead of isKindOfClass. It depends whether you want an exact match or to include subclasses.
It may be worth to take a look on header file objc/objc.h to find internals of id.
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_selector *SEL;
typedef id (*IMP)(id, SEL, ...);

Get the address of an Objective-c property (which is a C struct)

I have an Objective-C class which contains a C-style struct. I need to call a C function passing a pointer to this object member (a.k.a. property). For the life of me, I can't figure out how to get the address of this C struct. Using the traditional & operator to get the address, I'm getting an LValue compiler error.
typedef struct _myStruct
{
int aNumber;
}MyStruct, *pMyStruct;
#interface MyClass : NSObject {
MyStruct mystruct;
}
#property (readwrite) MyStruct myStruct;
#end
The following code results in a compiler error:
MyClass* myClass = [[MyClass alloc] init];
MyStruct* p = &(myClass.myStruct);
How do I get a pointer to the myStruct member of the myClass object?
There are often pretty good reasons to do what the original post is asking, given that Objective-C apps often have to work with C/C++ API's that take pointers to structs and similar types, but in a Cocoa app you'll often want to store such data in Objective-C classes for data management, collection in arrays and dictionaries, etc.
Though this question has been up for awhile I don't see the clear answer, which is: you can have a method that returns the address of the data that's backing your property, but in that method don't use "self" or it will go through the accessor and still not work.
- (const MyStruct*) getMyStructPtr
{
return &mystruct;
}
Note that I'm using the declared property from the OP, but not referencing it as self.mystruct, which would generate a compiler error (because that invokes the synthesized getter method).
MyStruct mystruct is private in MyClass, I assume when you do myClass.myStruct you are only refering to generated accessor method not the actual structure.
I don't think you can access the instance variable (structure in this case) from outside because it is private.
To get a pointer to the myStruct instance variable, you need to write a method that returns a pointer to that instance variable.
- (void)getMyStructPointer:(MyStruct **)outStruct {
*outstruct = &myStruct;
}
I don't really think this is a good idea, though. Other objects should not be mutating that object's ivar out from under it, and that's the only thing you can do with a pointer to the struct that you can't do with a copy of the struct returned by value.
The question itself demostrates a lack of understanding of at least the terminology.
A property is an interface consisting of two (or one for readonly) methods made public by the object, namely the getter and setter methods, in this case:
- (MyStruct) myStruct;
- (void) setMyStruct: (MyStruct) newMyStruct;
It makes no sense to talk about "taking the address of a property".
You can take the address of an instance variable (ivar). In this case you have an ivar named mystruct, and you can take the address of it with &mystruct in a method of MyClass. Since it is marked #protected (by default), you can take the address of it in a subclass using &self->mystruct. If you mark it #public, then you could take the address of it using &myobj->mystruct. This is a terrible idea, and you should really really rethink this, but you could do it.
If you just want the address of the ivar for some short lived purpose (for example, if MyStruct was large) you could do this, but it would be very unusual, and you'd be better off writing an explicitly named method like:
- (MyStruct*) getAddressForSettingMyStruct;
and if it is just read only, even better would be to use const MyStruct*.

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