前言
iOS开发中,runtime库作为底层基础设施,发挥着重要的作用,可以说,runtime赋予了OC这门语言灵魂,让OC这门语言变得动态化和极具灵活性,让开发者可以对代码本身进行编程,俗称元编程。万丈高楼平地起,只有明白了runtime底层的原理,才能在软件开发中游刃有余,避免隐秘的错误的发生。iOS开发中的很多知识点都和runtime息息相关,比如内存管理,弱引用,关联对象,分类,方法调用,继承等等。
(1)资源和工具:
runtime是一个开源的C++库,官方源码地址是:objc4
可编译调试版本地址:debug-objc
(2)将OC类重写为C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 *.m
为什么我们在分析OC代码的时候,总喜欢用clang将OC代码重写为C++呢?因为C++或者C语言中的结构体,直接就对应了内存的真实情况。比如OC的类,block等。
一 OC对象的底层数据结构
通过浏览runtime源代码,我们知道,有这么几个关键数据结构:
struct objc_object {
private:
isa_t isa;
}
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
explicit_atomic<uintptr_t> ro_or_rw_ext;
}
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
const uint8_t * ivarLayout;
const char * name;
WrappedPtr<method_list_t, PtrauthStrip> baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
其中struct objc_object就表示OC对象的底层数据结构,所有OC对象,都是在堆空间开辟一块内存,在其中依次存放isa指针和其他成员变量。下面,我们通过debug来验证这个结论:
@interface Person : NSObject
@property(nonatomic,assign)int age;
@property(nonatomic,strong)NSObject *obj;
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
Person *person = [Person new];
person.age = 20;
NSObject *obj = [NSObject new];
person.obj = obj;
//断点
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

上图中,我们看到,$0代表person的内存地址,$1代表person的成员变量NSObject的内存地址,x/8g $0 表示打印person对象内存地址64字节的内存信息,我们可以看到,第一个8字节0x0100000100e31601为isa指针,第二个8字节是0x0000000000000014,对应十进制数据20,第三个8字节是0x0000600002168020,正好就是$1,即obj对象的内存地址。至此,验证了上述OC对象内存结构的结论。
二 OC对象的创建:
了解了OC对象的内存结构之后,我们追溯源码看一下OC对象的创建过程:[[Person alloc]init]
在objc4下载到本地,在NSObject.mm中可以看到alloc的具体实现:
+ (id)alloc {
return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
}
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
研读上述调用栈,我们知道,最终调用的是_class_createInstanceFromZone函数中的malloc_zone_calloc或calloc函数,熟悉C语言的都知道,这两个函数是分配堆内存空间的,且需要向它们传入size参数,这个size表示要分配多大的堆内存。size来自于哪里呢?来自于参数cls,这个cls来自于+alloc中的self,alloc是一个类方法,self自然指代类。所以size来自于类信息中,至此,我们可以这么理解,创建OC对象的时候,就是去类信息中拿到要给对象分配的内存空间的大小,然后分配内存空间,这块内存空间,就是OC对象。
由上面的追溯过程,我们引出了类信息的概念,那么这个类信息反映到代码或者内存结构上,是什么样的呢?下篇文章将介绍有关OC类的结构。
1621

被折叠的 条评论
为什么被折叠?



