iOS原理分析之从源码看load与initialize方法(二)

简介: iOS原理分析之从源码看load与initialize方法

四、initialize方法分析


   我们可以采用和分析load方法时一样的策略来对initialize方法的执行情况,进行测试,首先将测试工程中所有类中添加initialize方法的实现。此时如果直接运行工程,你会发现控制台没有任何输出,这是由于只有第一次调用类的方法时,才会执行initialize方法,在main函数中编写如下测试代码:


int main(int argc, const char * argv[]) {

   @autoreleasepool {

       [MySubObjectOne new];

       [MyObjectOne new];

       [MyObjectTwo new];

       NSLog(@"------------");

       [MySubObjectOne new];

       [MyObjectOne new];

       [MyObjectTwo new];

   }

   return 0;

}

运行代码控制台打印效果如下:


2021-02-18 21:29:55.761897+0800 KCObjc[43834:23521232] initialize-cateOne:MyObjectOne

2021-02-18 21:29:55.762526+0800 KCObjc[43834:23521232] initialize:MySubObjectOne

2021-02-18 21:29:55.762622+0800 KCObjc[43834:23521232] initialize-cate:MyObjectTwo

2021-02-18 21:29:55.762665+0800 KCObjc[43834:23521232] ------------

可以看到,打印数据都出现在分割线前,说明一旦一个类的initialize方法被调用后,后续再向这个类发送消息,也不会在调用initialize方法,还有一点需要注意,需要注意,如果对子类发送消息,父类的initialize会先调用,再调用子类的initialize,同时,分类中如果实现了initialize方法则会覆盖类本身的,并且分类的加载顺序靠后的会覆盖之前的。下面我们就通过源码来分析下initialize方法的这种调用特点。


   首先,在调用类的类方法时,会执行runtime中的class_getClassMethod方法来寻找实现函数,这个方法在源码中的实现如下:


Method class_getClassMethod(Class cls, SEL sel)

{

   if (!cls  ||  !sel) return nil;

   return class_getInstanceMethod(cls->getMeta(), sel);

}

通过源码可以看到,调用一个类的类方法,实际上是调用其元类的示例方法,getMeta函数用来获取类的元类,关于类和元类的相关组织原理,我们这里先不扩展。我们需要关注的是class_getInstanceMethod这个函数,这个函数的实现也非常简单,如下:


Method class_getInstanceMethod(Class cls, SEL sel)

{

   if (!cls  ||  !sel) return nil;


   // 做查询方法列表,尝试方法解析相关工作

   lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);


   // 从类对象中获取方法

   return _class_getMethod(cls, sel);

}

在class_getInstanceMethod方法的实现中,_class_getMethod是最终获取要调用的方法的函数,在这之前,lookUpImpOrForward函数会做一些前置操作,其中就有initialize函数的调用逻辑,我们去掉无关的逻辑,lookUpImpOrForward中核心的实现如下:


IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)

{

   IMP imp = nil;

   // 核心在于!cls->isInitialized() 如果当前类未初始化过,会执行initializeAndLeaveLocked函数

   if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {

       cls = initializeAndLeaveLocked(cls, inst, runtimeLock);

   }


   return imp;

}

initializeAndLeaveLocked会直接调用initializeAndMaybeRelock函数,如下:


static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)

{

   return initializeAndMaybeRelock(cls, obj, lock, true);

}

initializeAndMaybeRelock函数中会做类的初始化逻辑,这个过程是线程安全的,其核心相关代码如下:


static Class initializeAndMaybeRelock(Class cls, id inst,

                                     mutex_t& lock, bool leaveLocked)

{

   // 如果已经初始化过,直接返回

   if (cls->isInitialized()) {

       return cls;

   }

   // 找到当前类的非元类

   Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);

   // 进行初始化操作

   initializeNonMetaClass(nonmeta);


   return cls;

}

initializeNonMetaClass函数会采用递归的方式沿着继承链向上查询,找到所有未初始化过的父类进行初始化,核心实现简化如下:


void initializeNonMetaClass(Class cls)

{

   Class supercls;

   // 标记是否需要初始化

   bool reallyInitialize = NO;

   // 父类如果存在,并且没有初始化过,则递归进行父类的初始化

   supercls = cls->superclass;

   if (supercls  &&  !supercls->isInitialized()) {

       initializeNonMetaClass(supercls);

   }

 

   SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;

   {

       // 如果当前不是正在初始化,并且当前类没有初始化过

       if (!cls->isInitialized() && !cls->isInitializing()) {

           // 设置初始化标志,此类标记为初始化过

           cls->setInitializing();

           // 标记需要进行初始化

           reallyInitialize = YES;

       }

   }

   // 是否需要进行初始化

   if (reallyInitialize) {

       @try

       {

           // 调用初始化函数

           callInitialize(cls);

       }

       @catch (...) {

           @throw;

       }

       return;

   }

}

callInitialize函数最终会调用objc_msgSend函数来向类发送initialize初始化消息,如下:


void callInitialize(Class cls)

{

   ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));

   asm("");

}

需要注意,initialize方法与load方法最大的区别在于其最终是通过objc_msgSend来实现的,每个类如果未初始化过,都会通过objc_msgSend来向类发送一次initialize消息,因此,如果子类没有对initialize实现,按照objc_msgSend的消息机制,其是会沿着继承链一路向上找到父类的实现进行调用的,所有initialize方法并不是只会被调用一次,假如父类中实现了这个方法,并且它有多个未实现此方法的子类,则当每个子类第一次接受消息时,都会调用一遍父类的initialize方法,这点非常重要,在实际开发中一定要牢记。


五、结语


   load和initialize方法是iOS开发中非常简单也也非常常用的两个方法,然而其与普通的方法比起来,还有有一些特殊,通过对源码的解读,我们可以更加深刻的理解这些特殊之处的原因及原理,编程的过程就像修行,知其然也知其所以然,与大家共勉。

目录
相关文章
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1759 4
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
运维 iOS开发 Windows
windows电脑备案ios APP获取公钥和证书指纹Sha-1值的方法
在阿里云进行APP备案、在备案IOS端的环节的时候,发现需要我们将p12证书安装在电脑上,再用xcode或或钥匙串访问来获取这个证书的公钥和sha-1值。 但是大部分开发uniapp应用的同学们,或者进行发布的运维人员的电脑都是windows,无法按照阿里云的教程来获取ios的公钥和sha-1。备案就被卡主了。 这里介绍下另一个方法,就是使用香蕉云编来在线上传证书获取。如下图所示,打开香蕉云编后,找到下图这个功能
1576 0
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
355 2
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
1227 21
|
iOS开发 开发者 Windows
uniapp云打包ios应用证书的获取方法,生成指南
打包用到的一共两个文件,一个是p12格式的私钥证书,一个是证书profile文件。其中生成p12证书的时候,按照官网的教程,是需要MAC电脑来协助做的,主要是生成一些csr文件和导出p12证书等。其实这些步骤也可以借助一些其他的工具来实现,不一定使用mac电脑,用windows电脑也可以创建。
1789 0
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
394 8
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的大舞台上,安卓与iOS两大操作系统各占半壁江山。本文将深入浅出地比较两者的开发环境,从开发工具、编程语言到用户界面设计等多个维度进行剖析,旨在为初入行的开发者们提供一盏明灯,帮助他们选择适合自己的开发路径。通过实例分析和数据支持,我们将揭示这两个平台的独特优势和潜在挑战,以及它们如何影响应用的性能和用户体验。
373 33
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
648 2
|
iOS开发
App备案与iOS云管理式证书 ,公钥及证书SHA-1指纹的获取方法
App备案与iOS云管理式证书 ,公钥及证书SHA-1指纹的获取方法
3730 0
App备案与iOS云管理式证书 ,公钥及证书SHA-1指纹的获取方法