懒加载类和非懒加载类
懒加载类:当需要实现才进行加载的类成为懒加载类,反之称为非懒加载类(无论是否使用到,都会进行加载)
我们也知道+(void)load
方法,会在main()之前调用,自定义的类中实现此方法,此类就会从懒加载类变为非懒加载类
前面了解到dyld的流程,pre-main()函数,中初始化主程序时,libObjc会在_objc_init()
到项目中所有类结构进行初始化。
libObjc分析
void _objc_init(void)
{
static bool initialized = false;
/*
如果已经被初始化,直接返回,保证仅仅初始化一次
*/
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();//环境变量初始化
tls_init();//设置objc的预定义的线程特定键和键的析构函数,来存储objc的私有数据
static_init();//运行C++静态构造函数
runtime_init();//
exception_init();//初始化异常操作系统
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
//在dyld初始化主程序时,通过指针回调实现images的map,load,unmap操作
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
map_images
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
map_images()内部通过map_images_nolock()
,此方法内部会对头文件信息遍历,当文件类型为mh_execute
时,获取mach-o下的方法段和消息段,为注册方法做准备
- 注册部分系统方法选择器
- 自动释放池初始化,全局散列表初始化
map_images
最后函数执行read_imags
获取类的初始化信息
read_images
获取类的初始化信息,
在这里面会进行以下处理
- 类处理
- 方法编号处理
- 非懒加载类实现
- 分类处理
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
//绝大多数情况都执行Normal class,这里是真正开始设置rw的地方,但是需要注意,rw只赋值了ro和flgs,其他的methods,protocol还未赋值。
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
//递归实现父类和元类,递归出口是cls
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
//对类的结构体内部的isa,supercls复制,类的结构体包含isa,supercls,bits,cache_t
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
//如果存在父类,反向将cls添加其子类,否则设置root
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
methodizeClass(cls, previously);
}
methodizeClass
static void methodizeClass(Class cls, Class previously)
{
method_list_t *list = ro->baseMethods;
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
}
向rw
方法列表,属性列表,协议列表
- 在确定个数且为多个的情况下,旧数据整体向后移动新数据的个数,把新数据插入到列表的前面
- 在确定个数且为一个情况下,新数据直接插在第一个
- 不确定个数的情况下,旧数据往后移动一个,新数据插入一个在前面,反复执行到添加结束
总之:新数据在旧数据前面
问题
- 类和分类谁先被加入rw
先类后分类, 分类的数据是需要到rw中才能生效,先加载类,是其拥有rw。即使有分类先被执行,也是被保存了起来。
- 有ro之后为何还有rw
oc是动态的,除了编译期数据,还需要运行时添加
- ro和rw的区别
ro存储类在编译期就确定的属性,方法,协议
rw运行时确定,会将ro的内容拷贝
- macho里面的数据怎么到内存的
读取macho对应字段下的内容,用哈希表存储,根据表初始化
网友评论