为什么我们应该避免在结构体使用闭包

我们所有人都喜欢闭包,你难道不喜欢吗? Closure能够让iOS开发者生活更轻松。如果它让我们更轻松了,那为啥我还要说不在在结构体中使用闭包了,原因就是:“内存泄露和发生不可预料的事”,你会不会有为啥Swift结构体会发生内存泄露问题的疑问?

Swift 结构体是值类型,按道理不会发生内存泄露,事实真的如此吗?我们已经有很多疑问了,接下来我们看看Swift基本内存管理吧。

基本类型

在解决主要问题之前,我要强调基本类型。
Swfit 主要有两个基本类型:引用类型和值类型,比如类就是引用类型,而结构体和枚举都是值类型。

值类型

值类型数据直接存储在内存,每个实例有唯一的复制数据,当变量赋值给存在的变量时,数据就会被复制。值类型的内存分配是在堆栈(stack)中完成。当值类型变量超出作用范围,内存分配就会发生。

struct Person {var name : String
}
var oldPerson = Person(name: "Rizwan")
var newPerson = oldPerson
newPerson.name = "Oh my Swift"
print(oldPerson.name)
print(newPerson.name)-------
Output:
Rizwan
Oh my Swift
-------

我们可以看到,newPerson变量的改变不会引起oldPerson的改变。这就是值类型的特性。

引用类型

引用类型在初始化时保留数据的引用(指针)。无论这个变量被分配到哪个已存在的引用类型上,这个引用共享变量值,引用类型的内存分配是在堆(heap)中完成.由自动引用计数(ARC)管理引用类型变量的内存。

class Person {var name: Stringinit(withName name: String){self.name = name}
}
var oldPerson = Person(withName: "Rizwan")
var newPerson = oldPerson
newPerson.name = "Oh my Swift"
print(oldPerson.name)
print(newPerson.name)------
Output
Oh my Swift
Oh my Swift
------

我们可以看到, olaPerson变量受newPerson的改变而改变,这就是引用类型的特性。

通常,内存泄露发生在引用类型。大多数情况发生在循环引用,想知道循环引用,可以阅读这篇博客
如果引用类型引起循环引用,我们可以尝试使用值类型去解决这个问题。

但是,这个也不是方法,有时候枚举和结构体能作为引用类型的参考,有时候循环引用也会在枚举和结构体中发生。

闭包-是结构体中的反面教材

当你在闭包中使用结构体,就像在引用类型中使用闭包时,就会发生问题。闭包时拥有外部环境的引用,以便在执行闭包主体时修改引用外部环境的值。

有这样一个例子,我们使用weak self来解决循环引用。如果我们尝试在结构体中去这样做,我们就会得到如下编译错误:'weak self' many noly be applied to class add class-bound protocol type, not '{struct name}'(意思就是weak self只能用于类或者类协议)

struct Car {var speed: Float = 0.0var increaseSpeed: (() -> ())?
}
var myCar = Car()
myCar.increaseSpeed = {myCar.speed += 30 // The retain cycle occurs here. We cannot use [weak myCar] as myCar is a value type.
}
myCar.increaseSpeed?()
print("My car's speed :")
print(myCar.speed) // Prints 30var myNewCar = myCar
myNewCar.increaseSpeed?()
myNewCar.increaseSpeed?()
print("My new car's speed :")
print(myNewCar.speed) // Prints 30 still!

你大概期待myNewCar结果是90.0,但是数据为30(而且引起循环引用了)

为什么

原因就是 myNewCar 只是 newCar 的部分复制,然而闭包和他的外部环境不能被完成复制。speed值被复制,但是myNewCar的属性increaseSpeed持有的是myCar的increaseSpeed的环境,即myCar的speed环境。所有myCar的increaseSpeed被调用。

这就是为什么在swift中定义闭包使用是危险的。

那我们应该怎么解决

最直接的办法是避免在结构体内定义闭包使用。如果你想使用,你应该理解非常小心,可能会出现一些预料之外的问题。对于循环引用问题,唯一的方法是设置myCar和myNewCar为nil,听起来不好,但是这也是无赖之举。

当我知道闭包在值类型中使用会有如此危险时,是如此令人深思。我希望你也有这种感受。

参考

[1] https://forums.swift.org/t/avoiding-unbreakable-reference-cycle-with-value-types-and-closures/18757/6

[2] https://github.com/Wolox/ios-style-guide/blob/master/rules/avoid-struct-closure-self.md

[3] https://www.objc.io/issues/16-swift/swift-classes-vs-structs/

[4] https://marcosantadev.com/capturing-values-swift-closures/

避免在Swift Struct中使用闭包相关推荐

  1. 在 Swift 中使用闭包实现懒加载

    本文讲的是在 Swift 中使用闭包实现懒加载, 原文地址:Swift Lazy Initialization with Closures 原文作者:Bob Lee 译文出自:掘金翻译计划 译者:ls ...

  2. Swift 3 中的新特性

    原文:What's New in Swift 3? 作者: Ben Morrow 译者:kmyhy Swift 3 将在下半年推出,对于 Swift 程序员来说,它带来了许多改变. 如果你没有密切关注 ...

  3. 如何在Swift 3中创建调度队列

    在Swift 2中,我能够使用以下代码创建队列: let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue&quo ...

  4. Swift之深入解析闭包Closures的使用和捕获变量的原理

    一.Closures 简介 ① 什么是闭包? 闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值.Swift 中的闭包与 C 和 Objective-C 中的代码块(b ...

  5. Swift 的函数和闭包

    函数的关键字是 func ,函数定义的格式是: func funcName(para:paraType) -> returnType{// code } 复制代码 函数的参数标签 其中参数的那部 ...

  6. Swift应用案例 2.闭包入门到精通

      本文主要介绍Swift的闭包的使用并与OC的Block做比较.学习Swift是绕不过闭包的,因为无论是全局函数还是嵌套函数都是闭包的一种,本文主要介绍闭包表达式. 1.闭包表达式的使用 // 1. ...

  7. Go中被闭包捕获的变量何时会被回收

    1. Go函数闭包 Go语言原生提供了对闭包(closure)的支持.在Go语言中,闭包就是函数字面值[2].Go规范中是这样诠释闭包的: 函数字面值(function literals)是闭包:它们 ...

  8. Swift Struct结构体

    一天一步,快乐前进: 结构体: 结构体就是结构化程序的产物.结构体是由另个或多个类型相同或者不同的数据组成的数据集合,其中的数据或者方法被称为它的成员或成员方法.结构体的成员可以包括属性,类型别名.数 ...

  9. iPad Swift Playgrounds中实现AR 3D物体识别

    在Xcode中我们可以通过创建ARResourceGroup, 添加.arobject的文件到ReferenceObject, 以及相关模型来到达3D物体检测识别的效果.同样的效果我们在Swift P ...

最新文章

  1. 【重大更新】DevExpress v17.1新版亮点(DevExtreme HTML5/JS篇)
  2. 参数处理:#{}与${}取值的区别
  3. 01-插件推荐:RTImageAssets 自动生成 AppIcon 和 @2x @1x 比例图片
  4. SQL Server 2008新特性——SSMS增强
  5. python值函数名的使用以及闭包,迭代器详解
  6. 云视睿博流媒体服务器Lit,《云视睿博流媒体服务器参数表.docx
  7. 帧间差分法_OpenCV_详解
  8. Linux 音频驱动
  9. 功能暴强的页面验证js代码
  10. 理解AOP思想(面向切面编程)
  11. 阿里天池大赛实战记录之菜鸟-需求预测与分仓规划
  12. 高职高专教材c语言,高职《C语言程序设计》教材建设研究
  13. python批量处理text_【RhinoPython】Rhino如何批量替换text 和Dot
  14. mark制图软件_mac自带的画图工具在哪?如何使用苹果电脑自带的预览工具进行画图操作...
  15. vue 批量下载图片并打包成压缩包
  16. 2022应届校招面试总结
  17. 定时任务的corn表达式
  18. HTML+CSS静态——绿色的餐饮美食网站(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
  19. 电力系统的延时功率流 (CPF)的计算【 IEEE-14节点】(Matlab代码实现)
  20. 搭建简单的Netty开发环境

热门文章

  1. 在标签使用onclick(this)来传递参数
  2. linux下jdk/maven/tomcat
  3. Vaughn Vernon谈当今软件开发所面临的挑战
  4. 在MyEclipse启动或者是tomcat启动的时候出现:Address already in use: JVM_Bindnull:8080
  5. HTML基础笔记-02
  6. 杭电1754--I Hate It(线段树)
  7. JSON支持什么对象/类型?
  8. ViewPager+Fragment实现TabHost(可复用的类)
  9. Node.js 究竟是什么?(zz)
  10. 一个恶心的无法同步服务器与pda时间的龌龊问题