窥探App启动到main函数前的过程(通过符号断点来看调用栈)
_dyld_start => libSystem_initializer => libdispatch_init => _objc_init 在_objc_init中会对类进行初始化。
BEPerson* p = [BEPerson alloc]init];
那么对象的创建中,alloc做了啥,init又做了啥呢?
image.png
苹果开源的部分源码下载网站(https://opensource.apple.com/tarballs/)
从苹果开源的libobjc.A.dylib的动态库的源码上看,init方法啥都没做,就是返回了自己。其作用就是给开发者初始化类的时候去复写这个方法。
那么很显然类的初始化工作是alloc这个函数来做的,那么alloc做了啥呢?
从源码上看来揭晓alloc函数调用的流程:
alloc函数的流程图.png注:其中分为了有重写了alloc或allocWithZone方法的情况和没有重写的情况
关于class_getInstanceSize 和malloc_size的区别(内存对齐)
class_getInstanceSize:可以理解为是获取了ivar(成员变量)所占据的空间(和属性排列无关),且该函数里有做了一次8字节的对齐。所以该函数返回的结果肯定是8的倍数。(class_getInstanceSize这个函数没有最小16字节的硬编码)
malloc_size:是系统为这个类所开辟的真正的空间,那么它存在的内存对齐的情况:
(内存中一个对象真正的内存大小,是指malloc_size返回的值。这种就有内存优化了)
1、oc的硬编码里规定了<16的话,开辟的空间就为16
2、内存对齐,结果须为16的倍数。calloc里面做的。具体代码在malloc源码里,做了一次>>4再<<4(先左移四位再右移4位)
3、属性的位置排列ios系统是做了优化的(malloc_size),会尽可能的利用内存,不一定是按顺序排序属性。
isa是一个联合体union,里面加了个结构体位域结构。一个isa占8个字节,总共64位,其中位域的每一位都有其含义的。
(这里通过位域而不会属性的形式来表达各种信息是为了节省空间、优化内存的结构等等作用)
uintptr_t nonpointer : 1; /// 后面的1表示占了1位
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
每一位代表的意思如下:
- nonpointer:表示是否对 isa 指针开启指针优化。0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等
- has_assoc:关联对象标志位,0没有,1存在
- has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
- shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
- magic:用于调试器判断当前对象是真的对象还是没有初始化的空间
- weakly_referenced:志对象是否被指向或者曾经指向一个 ARC 的弱变量,
没有弱引用的对象可以更快释放。 - deallocating:标志对象是否正在释放内存
- has_sidetable_rc:当对象引用技术大于 10 时,则需要借用该变量存储进位
注:类和元类是系统在编译期自动生成的
通过lldb来探索类结构
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache;
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
.......//其他的方法
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
......//其他的方法
}
image.png
image.png
image.png
通过runtimeAPI来获取类结构的内容
#import <objc/runtime.h>
void testObjc_copyIvar_copyProperies(Class pClass){
// 获取某个类的所有实例变量和属性
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
void testObjc_copyMethodList(Class pClass){
// 获取某个类的所有方法(入参为类则得到实例方法,入参为元类则得到类方法)
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
NSLog(@"Method, name: %@", key);
}
free(methods);
}
void testInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
void testClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); // ?
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
void testIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
testObjc_copyIvar_copyProperies(pClass);
testObjc_copyMethodList(pClass);
testInstanceMethod_classToMetaclass(pClass);
testClassMethod_classToMetaclass(pClass);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
最后我还想说
我们在探索oc底层的时候,免不了要去看源码,可是苹果是封闭的生态系统,它不是开源的,那么我们怎么去研究它呢?基于分享的态度,苹果baba还是开源了部分代码的,我们可以在苹果官方去找到这些源码。(但仅仅只是部分,像UIKit,Foundation等核心的库也是没有开源的)
那对于没有开源的,我们怎么去研究学习呢?
这里我介绍一个叫GnuStep的项目,它是GNU计划下的一个项目,它是将苹果的Cocoa的oc库重新开源实现了一遍(就是GNU这个组织自己根据对苹果iOS的探究,自己实现了一遍苹果Cocoa的OC库。换句话说某个大神自己凭经验实现的源码,嘻嘻嘻!)。虽然GnuStep不是苹果官方的源码,但还是具有一定的参考价值。
网友评论