#Swift Two-Phase Initialiaztion and 4 Safety check

在Swift中,Two-Phase Initialization(二阶段初始化)是一种确保类对象在完全初始化之前不会被使用的机制。这个机制主要用于类的初始化,尤其是在继承体系中,以确保子类和父类的属性都正确地初始化。Two-Phase Initialization的设计避免了部分初始化的情况,确保对象总是处于一致的状态。

Two-Phase Initialization 的过程

第一阶段:确保所有存储属性都有初始值。

  1. 从最底层的子类开始,逐层向上初始化每一个类层级的存储属性,直到基类。
  2. 每个类初始化自己的属性后,调用其父类的初始化方法。
  3. 直到基类的存储属性全部初始化完成,第一阶段才算结束。
    在这里插入图片描述

第二阶段:完成初始化,配置自定义设置。
5. 在所有属性都完成初始化后,开始从基类向下的逐层配置每一个类的自定义设置。
6. 在这个阶段,可以访问self、调用实例方法和读取属性。

这种初始化顺序可以确保即使在复杂的类继承关系中,子类和父类都能得到正确的初始化,避免属性被访问时处于未初始化状态。

在这里插入图片描述

Swift编译器在Two-Phase Initialization过程中执行了四项安全检查,以确保初始化的完整性和正确性。这些安全检查帮助开发者避免常见的初始化错误,从而保证类对象的内存安全和一致性。

安全检查 1

指定初始化器(Designated Initializer)必须在向上代理到父类初始化器之前,确保它自身类中引入的所有属性都已初始化

这项检查的原因是,只有当对象的所有存储属性都有了已知的初始值后,内存才算是完全初始化。指定初始化器需要确保它自己声明的所有属性都已经初始化,然后才能调用父类的初始化器。这可以避免在向上初始化过程中,父类的方法依赖于未初始化的子类属性,导致程序出错。

安全检查 2

指定初始化器必须在分配继承属性的值之前代理到父类初始化器

这一点确保了指定初始化器不能先给继承的属性赋值后再调用父类初始化器,否则,父类的初始化过程可能会覆盖这个新赋的值。Swift通过这种检查避免了意外的属性覆盖问题,确保继承链中的初始化顺序和预期一致。

安全检查 3

便利初始化器(Convenience Initializer)必须在给任何属性赋值之前,调用另一初始化器

便利初始化器的目的是简化指定初始化器的调用,但它不能直接初始化任何属性,包括本类中的属性。否则,便利初始化器赋的值会被指定初始化器的调用所覆盖。这样,Swift强制便利初始化器只能代理指定初始化器,以确保指定初始化器拥有对属性赋值的最终控制权。

安全检查 4

初始化器在第一阶段完成之前,不能调用实例方法、读取实例属性的值,或将self作为值引用

因为在第一阶段结束前,对象尚未完全初始化,因此调用实例方法、访问实例属性,或使用self可能会导致未定义行为。Swift的这个检查确保只有在所有属性都被赋值、对象的内存状态完全确定之后,才允许访问对象自身,从而避免了潜在的运行时错误。

示例代码

以下是一个示例,展示了如何在这些安全检查下正确初始化:

class Vehicle {var make: Stringinit(make: String) {self.make = make}
}class Car: Vehicle {var model: String// 安全检查 1 和 2init(make: String, model: String) {self.model = model // 自身属性 model 已初始化super.init(make: make) // 然后调用父类的初始化器}// 安全检查 3convenience init(model: String) {self.init(make: "DefaultMake", model: model) // 调用指定初始化器}// 安全检查 4:初始化完成后,才可以访问实例方法和属性func startEngine() {print("Engine started for \(make) \(model)")}
}let car = Car(model: "Sedan")
car.startEngine() // Engine started for DefaultMake Sedan

在这个例子中:

  • Carinit(make:model:)初始化器遵循了安全检查1和2。
  • Car的便利初始化器convenience init(model:)遵循了安全检查3。
  • startEngine()方法在初始化完成后才能被调用,确保了安全检查4。

通过这些安全检查,Swift确保Two-Phase Initialization的过程安全且一致,使代码在复杂的继承体系中也能避免初始化顺序错误。


补充:关于安全检查第三条

在Swift中,初始化器的第一个阶段指的是所有存储属性的初始化,包括类自身定义的属性以及从父类继承的属性。编译器禁止在第一阶段完成之前调用实例方法、读取实例属性的值,或者将self作为值引用,这个规则的目的是为了确保对象在完全初始化之前处于安全的状态。

原因与背景

在初始化的第一阶段,类的实例还没有完全初始化,某些属性的值可能尚未设置。因此,访问这些属性或使用self可能会导致访问未初始化或不稳定的对象状态。为了避免这种风险,Swift强制要求初始化器在完成第一阶段(即所有属性都已初始化)之前,不能执行以下操作:

  1. 调用实例方法
  2. 读取实例属性的值
  3. self作为值

这些限制确保在类实例化的过程中,self始终处于一个一致且已知的状态。

示例:访问未初始化的属性

假设我们有一个类,里面有一个初始化器,它在初始化阶段调用了一个实例方法:

class MyClass {var value: Intinit(value: Int) {self.value = valueself.printValue()  // 错误:在第一阶段完成之前调用实例方法}func printValue() {print("Value is \(value)")}
}

这段代码会报错,原因是在init(value:)初始化器中,尝试调用self.printValue(),而此时selfvalue属性可能尚未初始化,因此编译器会禁止这种操作。

为什么禁止这种操作?

在第一阶段完成之前,类的存储属性并未完全初始化,可能会导致以下问题:

  • 访问一个未初始化的属性会导致运行时错误,因为这个属性的值是未知的。
  • 调用实例方法时,实例方法可能会访问尚未初始化的属性,从而触发不可预期的行为。
  • 由于self指向的是一个未完全初始化的对象,尝试将self作为值传递或引用也会导致不确定的状态。

什么时候可以使用self

一旦所有存储属性都被初始化,Swift会认为初始化器的第一阶段已经完成,初始化器进入第二阶段。这时,实例方法可以被调用,实例属性的值可以安全地访问,self也可以作为值传递或引用。

例子:正确的初始化顺序

下面是一个正确的使用方式,初始化器不在第一阶段调用方法,直到所有属性都被初始化:

class MyClass {var value: Intvar description: Stringinit(value: Int, description: String) {self.value = valueself.description = descriptionself.printValue()  // 正确:在初始化完成后调用实例方法}func printValue() {print("Value is \(value), Description is \(description)")}
}let myObject = MyClass(value: 10, description: "This is a test")

在这个例子中,printValue()方法是在初始化阶段完成后调用的,此时valuedescription都已经被正确初始化。因此,这段代码是合法的。

总结

“An initializer can’t call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete” 这条规则的核心目的是为了确保对象在初始化过程中始终处于安全和一致的状态。在初始化的第一阶段,类的存储属性尚未初始化,无法安全地访问实例的其他部分。只有在初始化器的第一阶段完成后,才可以安全地执行这些操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/3115.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Python毕业设计选题:基于Python的无人超市管理系统-flask+vue

开发语言:Python框架:flaskPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 系统首页 超市商品详情 购物车 我的订单 管理员登录界面 管理员功能界面 用户界面 员…

JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习

先简单记录下简单使用跟测试,后续再补充具体,最近有用到,简单来说就是后端(服务端)编写个发射器,实现一次请求,一直向前端客户端发射数据,直到发射器执行完毕,模拟ai一句一句回复的效果 Respon…

cdp(Chrome DevTools Protocol)检测分析

如需转载请注明出处.欢迎小伙伴一起讨论技术. 逆向网站:aHR0cHM6Ly93d3cuYnJvd3NlcnNjYW4ubmV0L2JvdC1kZXRlY3Rpb24 首先,打开devtools后访问网址,检测结果网页显示红色Robot,标签插入位置,确定断点位置可以hook该方法,也可以使用插件等方式找到这个位置,本篇不讨论. Robot标…

HUGE滤波器分析、matlab例程

文章目录 1. 动态噪声方差2. 更新卡尔曼增益时考虑实时噪声3. 更灵活的误差模型4. 实时调整和自适应能力代码示例中的体现总结 HUGE滤波器专注于处理异方差性,主要体现在以下几个方面: 1. 动态噪声方差 在传统的卡尔曼滤波中,观测噪声和过程…

[OS] Assignment3_Prerequisite_mmap()_1

专业解释 这张图片展示了 mmap() 函数的作用和其在内存映射中的应用。在操作系统中,mmap() 是一个系统调用,它的主要作用是将文件或设备的内容映射到进程的虚拟地址空间中,使得文件内容可以像内存一样直接访问。 mmap() 函数的核心功能&…

部署istio应用未能产生Envoy sidecar代理

1. 问题描述及原因分析 在部署Prometheus、Grafana、Zipkin、Kiali监控度量Istio的第2.2章节,部署nginx应用,创建的pod并没有产生Envoy sidecar代理,仅有一个应用容器运行中 故在随后的prometheus中也没有产生指标istio_requests_total。通…

HBuilderX自定义Vue3页面模版

HBuilderX自定义Vue3页面模版 首先在HBuilderX工具下的任意一个项目添加新建自定义页面模版 新建模版文件&#xff0c;并打开进行编辑 vue3-setup-js.vue文件里填写样式模版&#xff08;根据自己的需要进行修改&#xff09; <template><view class"">&…

PAT甲级-1084 Broken Keyboard

题目 题目大意 坏的键盘不能打出字符。给出2个字符串&#xff0c;一个字符串是期望打出的字符&#xff0c;另一个字符串是实际打出的字符。要求输出坏掉的字符&#xff0c;字母键用大写字母。字符范围是_以及大小写字母和数字。 思路 因为大小写字母是同一个键&#xff0c;所…

【天线&其他】大疆无人机热成像人员目标检测系统源码&数据集全套:改进yolo11-bifpn-SDI

改进yolo11-ASF等200全套创新点大全&#xff1a;大疆无人机热成像人员目标检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.11.03 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系…

gitee推送代码remote rejected

其原因是码云邮箱设置怕暴露邮箱账号&#xff0c;所以禁止了代码推送 直接把这个选项关掉就行了

英特尔股价分析,财报超出预期,英特尔股票该买入还是卖出?

猛兽财经核心观点&#xff1a; &#xff08;1&#xff09;虽然英特尔第三季度的营收超出预期&#xff0c;但盈利却不及预期。 &#xff08;2&#xff09;华尔街分析虽然仍对英特尔持谨慎态度&#xff1b;但预计其2025年后有望取得重大进展。 &#xff08;3&#xff09;猛兽财经…

靠谱的零代码产平台开发— 应用创建与设置

在当今数字化快速发展的时代&#xff0c;企业对于快速构建和部署应用的需求日益增长。然而&#xff0c;传统的应用开发方式往往耗时耗力&#xff0c;且需要专业的编程技能。为了应对这一挑战&#xff0c;靠谱的零代码平台应运而生&#xff0c;它们通过直观的拖拽式界面和丰富的…

如何优雅处理异常?处理异常的原则

前言 在我们日常工作中&#xff0c;经常会遇到一些异常&#xff0c;比如&#xff1a;NullPointerException、NumberFormatException、ClassCastException等等。 那么问题来了&#xff0c;我们该如何处理异常&#xff0c;让代码变得更优雅呢&#xff1f; 1 不要忽略异常 不知…

Scrapy入门

Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。 安装scrapy pip install scrapy2.5.0 1.新建 Scrapy项目 scrapy startproject mySpider # 项目名为mySpider 2.进入到spiders目录 cd mySpider/mySpider/spiders 3.创建爬虫 scrapy gensp…

从0开始linux(20)——文件(1)文件描述符

欢迎来到博主的专栏&#xff1a;从0开始linux 博主ID&#xff1a;代码小豪 文章目录 文件打开文件文件修饰符从linux源码了解文件描述符 文件 首先我们先来搞清楚文件是什么&#xff1f;文件其实就是一段数据&#xff0c;是一段存储在磁盘当中的数据&#xff0c;文件是由文件内…

【汇编语言】[BX]和loop指令(二)——在Debug中跟踪用loop指令实现的循环程序

文章目录 前言1. 题目引入1.1 问题一1.2 问题二1.3 问题三1.4 代码实现 2. 程序跟踪2.1 加载程序&#xff0c;用r命令查看寄存器内容2.2 用U命令查看内存中的程序2.3 用T命令进行程序跟踪2.4 用P命令使得程序返回 3. 循环次数更多的程序3.1 代码实现3.2 一个新的命令——g3.3 如…

Unity照片墙效果

Unity照片墙效果&#xff0c;如下效果展示 。 工程源码

鸿蒙HarmonyOS开发生日选择弹框

鸿蒙HarmonyOS开发生日选择弹框 生日选择弹框和城市选择弹框差不多&#xff0c;都是通过观察上一个数据变化来设置自己的数据 一、思路&#xff1a; 一个弹框上建三个compoent&#xff0c;一个年&#xff0c;一个月&#xff0c;一个日。日的数据是根据年和月进行变化的 二、…

浏览器内核版本更新:Chrome 130✔

SunBrowser 内核版本更新至 Chrome 130&#xff0c;UA 同步更新至 130。 如何更新浏览器内核版本&#xff1f; 本地设置更新 在 AdsPower 客户端点击右上角的[设置]&#xff0c;再点击[本地设置]&#xff0c;下滑找到版本信息&#xff0c;选中需要的内核版本立即下载。 新建浏…

【实践】某央企研究院如何打造IT监控告警平台?

01客户简介&#xff1a; 案例客户为某央企下属研究院。 02痛点分析&#xff1a; 随着信创国产化持续推进&#xff0c;案例客户已完成部分IT核心系统的替代&#xff0c;部署了一系列国产软硬件设施&#xff0c;如Kylinv10操作系统、融智通网络设备等。由于信创生态不够成熟&a…