上一章介绍了应用加载的基本流程,即函数void _objc_init(void)
内部的实现,其内部最主要的函数_dyld_objc_notify_register(&map_images, load_images, unmap_image);
只是说了注册方法指针回调,用于程序调用,那这个方法内部的map_images, load_images,unmap_image
又是做了一些什么呢?
map_images
我将选取部分源码中的函数进行说明,在函数中重要的位置会有相应的注释,后面不再一一解释
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_nolock()
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
...
...
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
_read_images源码简化后的代码
void _read_images {
// 1:第一次进来 - 开始创建表
// gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
// allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
if (!doneOnce) {
doneOnce = YES;
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
// 2:类处理
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
}
// 3: 方法编号处理
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
// 4: 协议处理
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
NXMapTable *protocol_map = protocols();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// 5: 非懒加载类处理
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
addClassTableEntry(cls);
realizeClassWithoutSwift(cls);
}
// 6: 待处理的类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 7:分类处理
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
}
}
}
_read_images 之 readClass()
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
if (missingWeakSuperclass(cls)) {
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
assert(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
readClass()方法中主要做了这几件事:
1.判断下是不是后期要处理的类,读取Class的data() - 设置ro/rw
- addNamedClass(cls, mangledName, replacing);
- addClassTableEntry(cls);
4.插入到表中
_read_images 之 static Class realizeClassWithoutSwift(Class cls)
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
methodizeClass(cls);
return cls;
}
- 读取class的data()
- ro/rw的创建
3.父类和元类的实现内部(递归)分别是:supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
和
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
4.配置父类和元类的归属关系:;cls->superclass = supercls;;
和
;cls->initClassIsa(metacls);;
5.将此类连接到其超类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
realizeClassWithoutSwift
之methodizeClass(cls);
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
if (PrintConnecting) {
if (cats) {
for (uint32_t i = 0; i < cats->count; i++) {
_objc_inform("CLASS: attached category %c%s(%s)",
isMeta ? '+' : '-',
cls->nameForLogging(), cats->list[i].cat->name);
}
}
}
if (cats) free(cats);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
assert(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
把ro数据写到rw中:method_list_t、property_list_t、protocol_list_t
methodizeClass 之 attachLists()
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// 多对多
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0对多
list = addedLists[0];
}
else {
// 1对多
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
1.贴的时候是从源内存地址的起始位置开始复制的,并且是二维数组的形式;
2.是一个个先move然后copy
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
3.其他地方也会用到attachLists()eg:
- 类的加载,处理方法-属性-协议-
methodizeClass
- 方法的添加
addMethod
- 添加属性
_class_addProperty
- 添加协议
class_addProtocol
- 分类的加载
attachCategories
为什么?因为都有相同二位数组结构:entsize_list_tt
map_images小节
map_images主要做了
- 初始化类:realiseClass 设置rw、ro
- 处理分类:把分类中的方法协议、属性添加到类中
- 加载到相应的哈希表中
懒加载类和非懒加载类
懒加载类和非懒加载类的表面区别在于是否实现了load
方法,如果
- 实现了load方法就是非懒加载类
- 未实现了load方法就是懒加载类
但内层区在于属非懒加载类直接走上节课的realizeClassWithoutSwift 去加载数据,懒加载类 在lookupimpOrforward
中 有
if (!cls->isRealized())
判断条件,如果用到时候没有实现过则进入实现流程,如果实现过则跳过判断,进行下面的流程。
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
主类和分类懒加载分析
上面只提到load方法造成了懒加载和非懒加载的区别,其实实际当中会有4种情况
-
- 懒加载类 + 非懒加载分类
主类在发送消息的时候才有 — 但是分类提前了 — 需要加载 —-read_image —addUnattacheCategoryForClass —但是没有实现类 ——prepare_load_methods —提前实现了主类realizeClassWithoutSwift
- 懒加载类 + 非懒加载分类
2.非懒加载类 + 非懒加载分类
readImages — realizeClassWithoutSwift —— methodLizeClass — addUnattacheCategoryForClass —判断主类是否实现了,但是这时候主类已经实现了,if(cls —> isRealize){ remethodizeClass
}
加载分类进来
cls —-> demangleName () 用这个,不能用Name
addUnattachedCategoryForClass(cat, cls, hi);
const char *cname = ro->name;
const char *oname = "LGTeacher";//LGTeacher
if (cname && (strcmp(cname, oname) == 0)) {
// printf("_read_images - _getObjc2ClassList 类名 :%s - %p\n",cname,cls);
}
if(cls —> isRealize)
{
remethodizeClass(cls);
classExists == YES;
}
分类没有实现load —— 编译处理 — data() —> ro3.非懒加载类 + 懒加载分类
readImages — realizeClassWithoutSwift —— methodLizeClass — 不需要添加表 ——直接 data() ——> ro4.懒加载类 + 懒加载分类
消息发送的时候 ——lookupImpOrForward —— realizeClassWithoutSwift —— methodLizeClass — addUnattacheCategoryForClass 不进来 ——直接走data()
该部分参考链接 https://juejin.im/post/5e1c36fcf265da3df47adc1f
面试题
1.runtime底层api使用?
#import
#import "LGTeacher.h"
void lgSetter(NSString *value){
printf("%s/n",__func__);
}
NSString *lgName(){
printf("%s/n",__func__);
return @"master NB";
}
void lg_class_addProperty(Class targetClass , const char *propertyName){
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
objc_property_attribute_t backingivar = { "V", [NSString stringWithFormat:@"_%@",[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]].UTF8String }; //variable name
objc_property_attribute_t attrs[] = {type, ownership0, ownership,backingivar};
class_addProperty(targetClass, propertyName, attrs, 4);
}
void lg_printerProperty(Class targetClass){
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(targetClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
}
// class_data_bits_t
int main(int argc, const char * argv[]) {
@autoreleasepool {
// ro - rw - 有没有面试题
// 1: 动态创建类
Class LGPerson = objc_allocateClassPair([NSObject class], "LGPerson", 0);
// 2: 添加成员变量 1<ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
// cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
// 3.1 添加property - rw
lg_class_addProperty(LGPerson, "subject");
lg_printerProperty([LGTeacher class]);
lg_printerProperty(LGPerson);
// 3.2 添加setter + getter 方法
class_addMethod(LGPerson, @selector(setSubject:), (IMP)lgSetter, "v@:@");
class_addMethod(LGPerson, @selector(subject), (IMP)lgName, "@@:");
// 开始使用
id person = [LGPerson alloc];
[person setValue:@"KC" forKey:@"lgName"];
NSLog(@"%@",[person valueForKey:@"lgName"]);
id teacher = [LGTeacher alloc];
[teacher setValue:@"iOS" forKey:@"subject"];
NSLog(@"%@",[teacher valueForKey:@"subject"]);
[person setValue:@"master" forKey:@"subject"];
NSLog(@"%@",[person valueForKey:@"subject"]);
}
return 0;
}
// 相关api 的注释
/**
* 创建类对
*superClass: 父类,传Nil会创建一个新的根类
*name: 类名
*extraBytes: 0
*return:返回新类,创建失败返回Nil,如果类名已经存在,则创建失败
objc_allocateClassPair(<#Class _Nullable __unsafe_unretained superclass#>, <#const char * _Nonnull name#>, <#size_t extraBytes#>)
*/
/**
*添加成员变量
*
*cls 往哪个类添加
*name 添加的名字
*size 大小
*alignment 对齐处理方式
*types 签名
*
*这个函数只能在objc_allocateClassPair和objc_registerClassPair之前调用。不支持向现有类添加一个实例变量。
*这个类不能是元类。不支持在元类中添加一个实例变量。
*实例变量的最小对齐为1 << align。实例变量的最小对齐依赖于ivar的类型和机器架构。对于任何指针类型的变量,请通过log2(sizeof(pointer_type))。
class_addIvar(<#Class _Nullable __unsafe_unretained cls#>, <#const char * _Nonnull name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char * _Nullable types#>)
*/
/**
*往内存注册类
*
* cls 要注册的类
*
* objc_registerClassPair(<#Class _Nonnull __unsafe_unretained cls#>)
*/
/**
*往类里面添加方法
*
*cls 要添加方法的类
*sel 方法编号
*imp 函数实现指针
*types 签名
*
*class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
*/
/**
*往类里面添加属性
*
*cls 要添加属性的类
*name 属性名字
*attributes 属性的属性数组。
*attriCount 属性中属性的数量。
*
*class_addProperty(<#Class _Nullable __unsafe_unretained cls#>, <#const char * _Nonnull name#>, <#const objc_property_attribute_t * _Nullable attributes#>, <#unsigned int attributeCount#>)
*/
2.类拓展&分类
类拓展:匿名分类,增加属性和方法;
在上一章我们知道分类是在read_images
中加载到内存段的,而类拓展会在编译时作为类的一部分编译到内存段中,所以可以直接读取ro
!!! 如果类拓展没有被import到.m文件中时,是不会被编译到当前类的内存段的
分类如何增加属性?
1.通过关联对象
3.当对象delloc的时候,又会去系统中查找当前对象有没有关联对象,有的话就先移除,之后再delloc
3.load方法加载顺序
先看源码:
load_images()
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
//准备非懒加载类load方法,将其放在一个数组中
prepare_load_methods((const headerType *)mh);
}
//遍历加载load方法
call_load_methods();
}
call_load_methods();
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1.先调用主类的load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2.再调用分类的load方法 Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
从上面的源码可以得到load方法:
- 主类的load方法会先于分类load方法
- 主类和分类拥有相同方法时,分类会被调用,因为每个分类的方法会通过attachlist方法以数组的形式插入到二维数组的前面,所以会造成分类覆盖主类的假象
- 如何修改分类load方法顺序 - target - Bulid Phases - Complies Sources,shuoebi 就是调整编译顺序
initialize在什么时候调用
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
...
...
if (initialize && !cls->isInitialized()) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
...
...
return imp;
}
可以看到initialize
方法在lookUpImpOrForward
在这个方法后调用;所以我们总结下
- 晚于
load方法
调用 -
+initialize
方法
通过消息机制调用(objc_msgSend)
,当子类没有实现+initialize
方法时,会调用父类的initialize
方法 - 先调用父类的
+initialize
方法,后调用子类的+initialize
方法 - 如果一个类有分类,那么会调用最后编译的分类实现的