关于“幽灵架构”的补充说明2:Struct以及Copy - on -Write

简介: 在“幽灵架构”Demo中我把两个数据模型声明成了Struct,苹果WWDC2015的414号视频讲解了非常多关于Struct的优势,其实也是所有值类型的优势。

在“幽灵架构”Demo中我把两个数据模型声明成了Struct,苹果WWDC2015的414号视频讲解了非常多关于Struct的优势,其实也是所有值类型的优势。首先Swift标准库中绝大部分是值类型的,值类型的值传递是通过copy的,而作为一门静态语言,Swift要求所有的对象都有明确的类型,明确的类型代表了固定的内存分配,而414号视频也指出在内存中进行定长对象的copy是时间常数的,也就所谓的“cheap”。另外如果值类型的对象中包含引用类型的属性的话,会破坏值类型的特性,出现共享(详情请参考414号视频),因此在Swift中,对于包含引用类型属性的值类型对象间的Copy使用了Copy - on - Write这项技术,最基本的示例如下:

struct BezierPath {
    private var _path = UIBezierPath()

    var pathForReading: UIBezierPath {
        return _path  }

    var pathForWriting: UIBezierPath {
        mutating get {
            _path = _path.copy() as! UIBezierPath
            return _path
        }  
    } 
}

把引用类型的属性声明成private属性,然后向外界暴露两个公开的计算属性,供读取的计算属性返回私有属性本身,供写入的计算属性返回一个copy后的复本。这个版本的代码的问题是虽然保证了整个struct的值类型特性,但是每次写入都需要copy性能并不高,因此在Copy - on - Write中的pathForWriting加入了一个方法:

struct MyWrapper {
    var _object: SomeSwiftObject
    var objectForWriting: SomeSwiftObject {
        mutating get {
            if !isUniquelyReferencedNonObjC(&_object)) {
                _object = _object.copy()
            }
            return _object
        }
    }
}

这里的isUniquelyReferencedNonObjC方法会判断你当前访问的MyWrapper的_object属性是否只有一个引用,如果是通过 let b = a 这样的方法创建的话,那么a的_object和b的_object的引用会指向同一个地址,在读取值的时候不关心_object对象的引用,如果只进行读取的话是不需要进行copy的,如果需要写入b的_objc话需要访问objectForWriting,此时objectForWriting会检查_object的引用,如果如let b = a 的方式创建的b对象,那么b中_object的引用是2,此时会将b的_object对象赋成_object拷贝后的副本,这样在之后继续访问objectForWriting的时候由于b中的_object已经是新的对象了,引用只有1,所以_object会被直接返回。Swift中的很多会包含引用对象的值对象都采用了Copy-on-Write技术,比如我们常用的数组。需要注意的是这里的引用类型的对象必须是Swift对象,如果你使用了一个OC中的引用类型,那么你需要对其进行一个封装。
用法如下:

 final class Box<A> { 
     var unbox: A
    init (_ value: A) { unbox = value } 
}

其中A是非Swift原生对象,在类中声明某个对象的时候使用”Box”,在取值的时候需要调用Box实例的unbox属性获得原始的对象。除了使用“=”会发生copy外,向一个方法中传入值类型时也会发生copy,所以可以安全地操作传入的参数。通常情况下Swift中不会直接操作一个指针,所有的指针都会被标注为“unsafe”,如果想要方法改变传入的参数,可以把该参数声明成inout,然后在传入时参数前加&,&代表传入的是一个地址,inout的形式与直接操作某个对象的指针看起来是相同的,但是inout其实依旧使用了copy,不同的是在方法体结束的时候会把处理后的copy对象的值再赋回给原始的对象,请看下面的例子:

let clo:Int -> Void = { i in print("形参的值\(i)")
}
func method(inout num:Int){
    clo(num)
    num += 1
}
method(&a)
print("a的值:\(a)")

我们知道闭包可以捕获对象,闭包中捕获的是形参对象num,如果闭包num和a指向同一个地址的话,那么在method方法体中clo捕获的应该是a的引用,但其实clo捕获的只是copy后的副本,而已,在a发生变化之前就已经将副本捕获了,导致最后的打印结果不同。

下面来聊聊Struct,所有属性的类型都确定的Struct会被保存在栈上,Swift中每个Struct类型的长度是固定的,好比每个指针都是8字节,所以指针会被保存在栈上。在使用引用类型的时候,每次从栈上取到对应的指针需要根据指针提供的地址信息去堆上寻找引用类型的真实值,定长的Struct的值会被直接保存在栈上,这就省去了寻址的开销,栈的空间是有限的,如果一组值类型的长度超过了栈的空间,那么会被自动转移到堆上。
让我们在playground上写一些示例:

struct StructDemo{
    var a = 1
    var str = ""
}
let strDemo = StructDemo()
sizeofValue(strDemo)
sizeof(StructDemo)

这里Int的长度是8字节,String的长度是24字节,最终StructDemo的长度是32,完全取决于其属性的长度,sizeof和sizeofValue分别打印类型和实例的长度,这两个方法显示的都是栈上的长度,结果是相同的:
这里写图片描述

如果换成用class声明,长度是8。

目录
相关文章
|
前端开发 iOS开发
关于“幽灵架构”的总结:适用场景与方法重载
前几篇博文对“幽灵架构”做了用法的介绍和相关技术点的补充,本文是一篇总结性质的文章,分析该架构的适用场景和限制,首先让我们回顾一下iOS开发的MVC模式,参考斯坦福公开课里Paul老爷子的讲解...
992 0
|
编译器 Swift
关于“幽灵架构”的补充说明4:协议的应用场景与局限性
再次解释一下协议的意义:定义某个功能模块的最小粒度,因为Swift是单继承,而无论是值类型还是引用类型都可以遵守多个协议,因此协议是比父类的粒度还要小的功能模块。
929 0
|
编译器 数据安全/隐私保护 Swift
关于“幽灵架构”的补充说明5:改造控制器
Swift中的泛型有非常多的用处,除了我在之前介绍的方法中作为占位符之外,还可以被用在协议中,构成一个泛型协议,那么遵守这个泛型协议的成员就会变成泛型成员。
903 0
|
安全 编译器 Swift
关于“幽灵架构”的补充说明3:为什么不会产生“循环引用”
承接上文,已经简明阐述了使用Struct代替Class的好处,使用Class会使我们的程序出现“意外的共享”以及“循环引用”之类的危险,传统面向对象开发中对Class的依赖主要来自于我们对“继承”的依赖。
1047 0
|
Swift
关于“幽灵架构”的补充说明1:协议中的方法定义
承接上一篇博文,上一篇的篇幅有点太长了,我觉得有一些相关的技术点需要说明,所以重新写几篇博文。
1042 0
|
前端开发 iOS开发
Swift黑科技:还在争论MVC和MVVM?博主独创幽灵架构MV!
本人原创,长文慎入,但此文可能会改写你的编程风格。我认为数据和模型交互的关键问题是如何处理数据源和视图源本身的异构性。
1176 0
|
1月前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
2月前
|
Cloud Native Devops 云计算
云计算的未来:云原生架构与微服务的革命####
【10月更文挑战第21天】 随着企业数字化转型的加速,云原生技术正迅速成为IT行业的新宠。本文深入探讨了云原生架构的核心理念、关键技术如容器化和微服务的优势,以及如何通过这些技术实现高效、灵活且可扩展的现代应用开发。我们将揭示云原生如何重塑软件开发流程,提升业务敏捷性,并探索其对企业IT架构的深远影响。 ####
66 3
|
2月前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
1月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
226 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型

热门文章

最新文章