类的内存结构
NSObject :绝大多数OC对象的基类(NSProxy除外)
在NSObject类中可以看到一个Class类型的isa 实例变量
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
在objc类中可以看到(isa)Class实际上是指向结构体为objc_class的指针
/// A pointer to an instance of a class.
typedef struct objc_object *id;(表示一个类的对象)
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
表示每一个类的对象都有一个指向他自己类的isa指针
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class; (表示一个类)
1.结构体objc_class定义
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;(instance指向自己类对象的指针,class指向自己的元类metaClass metaClass里有关Class对象的方法)
#if !__OBJC2__
Class super_class (指向自己父类的指针) OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size (实例的大小 最小为16个字节) 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;
问题:
@interface NSObject(mkCategory)
+(void)test;
@end
@implementation NSObject(mkCategory)
-(void)test{
NSLog(@"this mkcategory method %s",__func__);
}
[NSObject test]; [NSMutableArray test];运行情况
为什么Objective-C类不能动态添加成员变量
在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是文档中特别说明:
1 |
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported. |
意思是说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair之后才可以被使用,同样没有机会再添加成员变量。 为基类动态增加成员变量会导致所有已创建出的子类实例都无法使用。那为什么runtime允许动态添加方法和属性,而不会引发问题呢?因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。
类增加成员变量会增加实例对象的存在大小,代码如下:
BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *type)
{
bool result = YES;
if (!cls) return NO;
if (ISMETA(cls)) return NO;
if (!(cls->info & CLS_CONSTRUCTING)) return NO;
if (!type) type = "";
if (name && 0 == strcmp(name, "")) name = nil;
mutex_locker_t lock(classLock);
// Check for existing ivar with this name
// fixme check superclasses?
if (cls->ivars) {
int i;
for (i = 0; i < cls->ivars->ivar_count; i++) {
if (0 == strcmp(cls->ivars->ivar_list[i].ivar_name, name)) {
result = NO;
break;
}
}
}
if (result) {
old_ivar_list *old = cls->ivars;
size_t oldSize;
int newCount;
old_ivar *ivar;
size_t alignBytes;
size_t misalign;
if (old) {
oldSize = sizeof(old_ivar_list) +
(old->ivar_count - 1) * sizeof(old_ivar);
newCount = 1 + old->ivar_count;
} else {
oldSize = sizeof(old_ivar_list) - sizeof(old_ivar);
newCount = 1;
}
// allocate new ivar list
cls->ivars = (old_ivar_list *)
calloc(oldSize+sizeof(old_ivar), 1);//开辟空间
if (old) memcpy(cls->ivars, old, oldSize);//把old内存的资源拷贝到cls->ivars所指的地方
if (old && malloc_size(old)) free(old);
cls->ivars->ivar_count = newCount;
ivar = &cls->ivars->ivar_list[newCount-1];
// set ivar name and type
ivar->ivar_name = strdup(name);//把name拷贝到ivar->ivar_name
ivar->ivar_type = strdup(type);
// align if necessary
alignBytes = 1 << alignment;
misalign = cls->instance_size % alignBytes;
if (misalign) cls->instance_size += (long)(alignBytes - misalign);
// set ivar offset and increase instance size
ivar->ivar_offset = (int)cls->instance_size;
cls->instance_size += (long)size;
}
return result;
}