Why id is generic pointer?
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, ...);
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.