An introduction to Objective-C Meta Class
An introduction to Objective-C Meta Class
First, let's have a look at the base class NSObject.
@interface NSObject <NSObject> { //implement protocol NSObject
Class isa; //point to meta class, all instances of NSObject share the same meta class.
}
+ (void)load;
+ (void)initialize;
- (id)init;
+ (id)new;
+ (id)allocWithZone:(NSZone *)zone;
+ (id)alloc;
- (void)dealloc;
+ (Class)superclass;
+ (Class)class;
...
@end
Class is defined as:
typedef struct objc_class *Class;
struct objc_class {
Class isa;
#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;
objc_class is the meta class.
id is defined as:
typedef struct objc_object {
Class isa;
} *id;
id is an pointer pointed to struct obj_object very similar with the definition of NSObject.
//class TestA
@interface TestA : NSObject {
int a;
}
+ (id)alloc;
+ (void)initialize;
@end
@implementation TestA
+ (void)load
{
printf("load TestA. %s %#x\n", [[self description] UTF8String], self);
}
+ (id)alloc
{
self = [super alloc];
printf("alloc TestA. %s %#x\n", [[self description] UTF8String], self);
return self;
}
+ (void)initialize
{
printf("initialize TestA. %s %#x\n", [[self description] UTF8String], self);
}
- (id)init
{
self = [super init];
if(self)
{
printf("init TestA. %s %#x isa=%#x\n", [[self description] UTF8String], self, isa);
}
return self;
}
@end
//End of TestA
//class TestB
@interface TestB : TestA {
int b;
}
+ (id)alloc;
+ (void)initialize;
@end
@implementation TestB
+ (void)load
{
printf("load TestB. %s %#x\n", [[self description] UTF8String], self);
}
+ (id)alloc
{
self = [super alloc];
printf("alloc TestB. %s %#x\n", [[self description] UTF8String], self);
return self;
}
+ (void)initialize
{
printf("initialize TestB. %s %#x\n", [[self description] UTF8String], self);
}
- (id)init
{
self = [super init];
if(self)
{
printf("init TestB. %s %#x isa=%#x\n", [[self description] UTF8String], self, isa);
}
return self;
}
@end
//End of TestB
//Testing example
TestB *b1 = [[TestB alloc] init];
TestB *b2 = [[TestB alloc] init];
TestA *a1 = [[TestA alloc] init];
Output:
initialize TestA. TestA 0xd634
load TestA. TestA 0xd634
initialize TestB. TestB 0xd65c
load TestB. TestB 0xd65c
alloc TestA. <TestB: 0x4e317f0> 0x4e317f0
alloc TestB. <TestB: 0x4e317f0> 0x4e317f0
init TestA. <TestB: 0x4e317f0> 0x4e317f0 isa=0xd65c
init TestB. <TestB: 0x4e317f0> 0x4e317f0 isa=0xd65c
alloc TestA. <TestB: 0x4e369e0> 0x4e369e0
alloc TestB. <TestB: 0x4e369e0> 0x4e369e0
init TestA. <TestB: 0x4e369e0> 0x4e369e0 isa=0xd65c
init TestB. <TestB: 0x4e369e0> 0x4e369e0 isa=0xd65c
alloc TestA. <TestA: 0x4e34930> 0x4e34930
init TestA. <TestA: 0x4e34930> 0x4e34930 isa=0xd634
From the outputs, some rules can be concluded:
1. In class method, such as initialize, load, alloc. self is the address of meta class, in another word, a pointer to struct objc_class.
2. The first time a class is referenced, its meta class will only be initialized and loaded once.
3. All instances of class objects share a meta class.
4. TestB inherits from TestA. The meta class address of TestB is 0xd65c. The meta class address of TestA is 0xd634. So TestB.isa->super_class = 0xd634. so TestB can call super's methods. All methods and meta infomations are stored in isa.
First, let's have a look at the base class NSObject.
@interface NSObject <NSObject> { //implement protocol NSObject
Class isa; //point to meta class, all instances of NSObject share the same meta class.
}
+ (void)load;
+ (void)initialize;
- (id)init;
+ (id)new;
+ (id)allocWithZone:(NSZone *)zone;
+ (id)alloc;
- (void)dealloc;
+ (Class)superclass;
+ (Class)class;
...
@end
Class is defined as:
typedef struct objc_class *Class;
struct objc_class {
Class isa;
#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;
objc_class is the meta class.
id is defined as:
typedef struct objc_object {
Class isa;
} *id;
id is an pointer pointed to struct obj_object very similar with the definition of NSObject.
//class TestA
@interface TestA : NSObject {
int a;
}
+ (id)alloc;
+ (void)initialize;
@end
@implementation TestA
+ (void)load
{
printf("load TestA. %s %#x\n", [[self description] UTF8String], self);
}
+ (id)alloc
{
self = [super alloc];
printf("alloc TestA. %s %#x\n", [[self description] UTF8String], self);
return self;
}
+ (void)initialize
{
printf("initialize TestA. %s %#x\n", [[self description] UTF8String], self);
}
- (id)init
{
self = [super init];
if(self)
{
printf("init TestA. %s %#x isa=%#x\n", [[self description] UTF8String], self, isa);
}
return self;
}
@end
//End of TestA
//class TestB
@interface TestB : TestA {
int b;
}
+ (id)alloc;
+ (void)initialize;
@end
@implementation TestB
+ (void)load
{
printf("load TestB. %s %#x\n", [[self description] UTF8String], self);
}
+ (id)alloc
{
self = [super alloc];
printf("alloc TestB. %s %#x\n", [[self description] UTF8String], self);
return self;
}
+ (void)initialize
{
printf("initialize TestB. %s %#x\n", [[self description] UTF8String], self);
}
- (id)init
{
self = [super init];
if(self)
{
printf("init TestB. %s %#x isa=%#x\n", [[self description] UTF8String], self, isa);
}
return self;
}
@end
//End of TestB
//Testing example
TestB *b1 = [[TestB alloc] init];
TestB *b2 = [[TestB alloc] init];
TestA *a1 = [[TestA alloc] init];
Output:
initialize TestA. TestA 0xd634
load TestA. TestA 0xd634
initialize TestB. TestB 0xd65c
load TestB. TestB 0xd65c
alloc TestA. <TestB: 0x4e317f0> 0x4e317f0
alloc TestB. <TestB: 0x4e317f0> 0x4e317f0
init TestA. <TestB: 0x4e317f0> 0x4e317f0 isa=0xd65c
init TestB. <TestB: 0x4e317f0> 0x4e317f0 isa=0xd65c
alloc TestA. <TestB: 0x4e369e0> 0x4e369e0
alloc TestB. <TestB: 0x4e369e0> 0x4e369e0
init TestA. <TestB: 0x4e369e0> 0x4e369e0 isa=0xd65c
init TestB. <TestB: 0x4e369e0> 0x4e369e0 isa=0xd65c
alloc TestA. <TestA: 0x4e34930> 0x4e34930
init TestA. <TestA: 0x4e34930> 0x4e34930 isa=0xd634
From the outputs, some rules can be concluded:
1. In class method, such as initialize, load, alloc. self is the address of meta class, in another word, a pointer to struct objc_class.
2. The first time a class is referenced, its meta class will only be initialized and loaded once.
3. All instances of class objects share a meta class.
4. TestB inherits from TestA. The meta class address of TestB is 0xd65c. The meta class address of TestA is 0xd634. So TestB.isa->super_class = 0xd634. so TestB can call super's methods. All methods and meta infomations are stored in isa.