1.类和对象
首先进到runtime.h中
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
可以看到,#if !OBJC2以及很多OBJC2_UNAVAILABLE,说明这些在OC2.0中以及废弃了,真正的实现要看opensource或者下载源码
下载源码进入project headers里面的objc-runtime-new.h中,找到objc_object
struct objc_class;
struct objc_object;
typedef struct objc_class *Class;
typedef struct objc_object *id;
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_NONPOINTER_ISA
/*
...
*/
}
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// initIsa() should be used to init the isa of new objects only.
// If this object already has an isa, use changeIsa() for correctness.
// initInstanceIsa(): objects with no custom RR/AWZ
// initClassIsa(): class objects
// initProtocolIsa(): protocol objects
// initIsa(): other objects
void initIsa(Class cls /*indexed=false*/);
void initClassIsa(Class cls /*indexed=maybe*/);
void initProtocolIsa(Class cls /*indexed=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
/*
...
*/
}
}
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
/*
...
*/
}
可以看到,Class是结构体objc_class,id是结构体objc_object,而objc_class继承自objc_object,
也就是说Class本身就是一个对象,对象都会有一个isa_t isa.
-
元类
对象的方法并不是存储于对象的结构体中的,因为每一个对象都存一份这个开销是很大的.当实例方法被调用时,它会通过自己的 isa 来查找自己的类,然后在类的 class_data_bits_t 结构体中查找方法的实现,每一个对象有一个指向自己的父类的指针 super_class 用来查找父类的方法.
由于类Class本身也是对象,因此也有一个isa,类的isa指向元类metaClass,类就是其元类的对象,实例方法调用时,通过对象的 isa 在类中获取方法的实现;类方法调用时,通过类的 isa 在元类中获取方法的实现.
对象,类与元类
图中,实线是父类superclass,虚线是isa,对象的isa指向类对象,类对象的isa指向元类,元类的isa指向基类NSObject.NSObject的元类的isa指向自己,并且NSObject的元类的父类指向自己的类对象,所以当NSObject类调用自己的实例方法时,是可以成功的.同理,任何一个类都可以通过类方法调用NSObject里的实例方法.
每个类只有一个类对象. -
block的内存结构
在main.m写一个block
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^ {
NSLog(@"Hello World!");
};
block();
}
return 0;
}
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
查看main.cpp,找到main函数
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_0xn052bn6dgb9z7pfk8bbg740000gn_T_main_88f00d_mi_0);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
block调用了一个结构体,__main_block_impl_0,传入两个参数, __main_block_func_0即函数,__main_block_desc_0_DATA是结构体的地址.
可以看到__main_block_impl_0也有isa,可以理解为block也是对象,是id类型,在这里isa是_NSConcreteStackBlock,栈类型,__main_block_impl_0的isa有三种类型,
&_NSConcreteStackBlock,&_NSConcreteMallocBlock和&_NSConcreteGlobalBlock;栈堆和全局.
不过在clang出的文件中看到的isa都是_NSConcreteStackBlock,真正的分布需要在运行时通过lldb查看.
在OC方法中定义block,默认会被copy到堆中,如果是内存修饰copy的property定义block,自然也是在堆中,当block作为函数参数时,如果不进行类型转换,是会默认分配在栈中的,其内存在后续的执行中很可能会被覆盖;定义全局位置的block则是全局类型,分配在全局内存,因为是全局位置因此不会捕获局部变量.
block声明的时候保存了__main_block_impl_0地址,__main_block_impl_0保存了函数体以及block的类型和blcok的结构体大小,当block回调的时候就是调用了函数__main_block_func_0.
blcok的结构
网友评论