My understanding is that objects and classes in Objective-C are just structs.
Respectively they are just:
struct objc_class *
and:
struct objc_object *
Question #1:
objc_msgSend(id self, SEL _cmd);
id
as far as I know is of type struct objc_object *
But when we call a class method, a class, which is of type struct objc_class *
,
I would expect it to cause problem or shout out some kind of warning such as „Hey, wrong type here, my friend“.
But there isn’t.
This only serves to fulfill my curiosity, because even without fully understanding this, it doesn’t seem to cause me any trouble (so far). But I would like to dig deep and learn the fundamentals / „peculiarities".
Question #2:
Since there is no warning according to my experience (relates to Question #1), hence my not-so-sure assumption that they could perhaps be used interchangeably.
Can struct objc_class *
and struct objc_object *
really be used interchangeably?
If yes:
Can you show me a scenario and a sample when / how / why we would need to use these interchangeably?
What would be the benefit (if any), disadvantages (if any) and „gotchas“ (things to watch out for; if any) doing so?
My understanding is that objects and classes in Objective-C are just structs.
This isn't really true, particularly since ObjC2, and definitely isn't a good way of thinking about it. The "struct" in question has a single field (isa
) defined. That's it. It's a "struct" in name only.
Note that under ARC objects and structs are treated differently by the compiler. Objects pointers will get special handling (including nil-initialization) that a struct pointer will not. The compiler will also apply ->
differently to an object than it does to a struct. A struct pointer must have a field with the name given after the ->
. objc_object
only has one field (isa
). Since class hierarchies can be defined and redefined at runtime, the compiler cannot evaluate ->
for an object at compile time as it does for a struct.
I make this point because in C++, objects and structs are just slightly different versions of the same thing, and you can easily swap between them. You cannot safely do that in ObjC. They're not nearly as similar.
But when we call a class method, a class, which is of type
struct objc_class *
, I would expect it to cause problem…
This is because classes are objects. Objects are not defined in terms of being of type objc_object
. They're defined in terms of having an isa
field (it used to be a pointer, but now it might be a pointer, or it might not). This is part of the problem of assuming that the typedef is the important thing. It isn't. What matters is whether it acts like an object. Objective-C is a mostly duck-typed language. If it acts like an object, it's an object.
BTW, NSProxy
also lives in this weird almost-an-object world. If you note its definition, it does not inherit from anything, but it does declare an isa
field as its first ivar. That's the critical part of making it act like an object.
Can struct objc_class * and struct objc_object * really be used interchangeably?
No. You can generally pass a class to anything that wants an object (since a class is an object), but you cannot pass an object to things that want a class.
I find this in runtime.h file.
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
Both objc_class and objc_object has isa variable, isa in objc_object is point to Class which describes the instance and stores instance method list. Isa in objc_class is point to Class too but it is meta-class which describes the class and stores class method list.
The link is useful to you.
I have simulate the condition.
struct A {
int a ;
} ;
typedef struct A *PA ;
struct B {
int a ;
int b ;
} ;
void dosomething(PA pa)
{
printf("%d", (*pa).a) ;
}
+ (void)hei
{
struct B b ;
b.a = 10 ;
b.b = 20 ;
// without forced cast, i will get a warning but work well
dosomething((PA)&b) ;
}
You can use struct B *
instead of struct A *
because compiler and linker place int a
in the same relative address.
Why compiler doesn't complain when we call class method which pass struct objc_class *
to the objc_msgSend(id self, SEL _cmd);
, in my opinion the compiler takes account of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With