Swift开发基础07-内存布局
了解Swift的内存布局和底层原理对于编写高性能和内存高效的应用非常重要。接下来,我将更详细地介绍Swift的内存管理机制和一些底层实现细节,包括内存布局、ARC(自动引用计数)、引用类型和值类型的区别,及其在底层的实现。
内存布局(Memory Layout)
栈(Stack)
栈内存用于存储函数调用帧(Call Stack)、局部变量和函数参数。栈的特点是LIFO(Last In, First Out),即最后进入的最先出去。栈内存分配和释放速度非常快,但栈的空间是有限的。
堆(Heap)
堆内存用于存储动态分配的内存,比如对象、闭包等。堆的内存管理相对复杂,需要显式管理分配和释放。Swift通过ARC来自动管理堆内存。
值类型与引用类型的内存布局
- 值类型(Value Type):
- 存储在栈上或嵌入到引用类型中的对象内部。
- 包括基本类型(整数、浮点数、布尔值)、结构体、枚举等。
- 每次赋值会拷贝一份独立的数据。
- 引用类型(Reference Type):
- 存储在堆上,并通过指针引用管理。
- 包括类(Class)、闭包(Closure)等。
- 赋值操作只会复制指针,而不会复制对象本身。
自动引用计数(ARC)
ARC在运行时管理引用类型实例的内存生命周期。每个引用类型实例有一个引用计数(Reference Count),当引用计数为零时,ARC会自动释放该实例的内存。
ARC的底层机制
引用计数增加(retain):
- 每当有新的引用指向一个对象,其引用计数加一。
- 底层实现为调用
objc_retain。
引用计数减少(release):
- 每当一个引用不再指向该对象,其引用计数减一。
- 底层实现为调用
objc_release。
内存释放:
- 当引用计数为零时,对象的
deinit方法被调用,然后释放对象占用的内存。
- 当引用计数为零时,对象的
ARC示例
class ExampleClass {
let name: String
init(name: String) {
self.name = name
print("\(name) is initialized")
}
deinit {
print("\(name) is being deallocated")
}
}
var object1: ExampleClass? = ExampleClass(name: "Object1") // 引用计数+1
var object2 = object1 // 引用计数+1,此时引用计数为2
object1 = nil // 引用计数-1,此时引用计数为1
object2 = nil // 引用计数-1,此时引用计数为0,对象被释放
上下文捕获(Context Capture)
捕获机制
闭包可以捕获并存储其上下文中的变量和常量,即使这些变量和常量在闭包创建时已经作用域结束。Swift通过将被捕获的变量存储在堆上来实现这种捕获。
func makeIncrementer(incrementAmount: Int) -> () -> Int {
var total = 0
let incrementer: () -> Int = {
total += incrementAmount
return total
}
return incrementer
}
let incrementByTwo = makeIncrementer(incrementAmount: 2)
print(incrementByTwo()) // 输出: 2
print(incrementByTwo()) // 输出: 4
在这个例子中,total和incrementAmount被捕获到堆中,并在闭包中引用。
Swift底层对象模型
Swift类的内存布局
Swift类的内存布局包含一个头部和实例数据部分。头部通常包含:
- 引用计数:用于ARC。
- 类元数据指针:指向类型元数据(Class Metadata)。
方法派发(Method Dispatch)
Swift支持多种方法派发方式:
- Direct Dispatch(直接派发):主要用于值类型或没有多态性的类。
- 编译时决定,性能最高。
- VTable Dispatch(虚表派发):用于引用类型的多态方法调用。
- 通过虚表(VTable)找到方法的具体实现。
- Message Dispatch(消息派发):主要用于兼容Objective-C的类。
- 通过运行时的消息机制进行方法调用。
class BaseClass {
func printMessage() {
print("BaseClass")
}
}
class SubClass: BaseClass {
override func printMessage() {
print("SubClass")
}
}
let instance: BaseClass = SubClass()
instance.printMessage() // 运行时通过VTable找到SubClass的方法,输出: "SubClass"
内存安全(Memory Safety)
变量初始化:
- Swift强制在使用变量之前进行初始化,避免未初始化变量的使用。
数组越界检查:
- Swift在访问数组元素时,会进行边界检查,防止越界访问。
原子性:
- Swift对内存的读写操作是原子的,可以避免数据竞争。
组合的安全示例
综合上述内容,以下是一个更复杂的示例,展示了闭包捕获、ARC、VTable派发以及内存安全性:
class Counter {
var count = 0
func increment() -> Int {
count += 1
return count
}
deinit {
print("Counter is being deallocated")
}
}
func makeCounter() -> () -> Int {
let counter = Counter()
let increment: () -> Int = { [weak counter] in
guard let counter = counter else { return 0 }
return counter.increment()
}
return increment
}
let incrementer = makeCounter()
print(incrementer()) // 输出: 1
print(incrementer()) // 输出: 2
上面的示例展示了如何在Swift中效率和安全地处理内存。通过理解这些底层细节,能更好地编写性能高效、内存安全的Swift代码。
Swift开发基础07-内存布局的更多相关文章
- eclipse Android 开发基础 Activity 窗体 界面
eclipse Android 开发基础 新建工程 新建布局layout,new Android Activity就相当于窗体Form. 新建Activity自动生成src下同名的java代码. pu ...
- swift开发多线程篇 - 多线程基础
swift开发多线程篇 - 多线程基础 iOS 的三种多线程技术 (1)NSThread 使用NSThread对象建立一个线程非常方便 但是!要使用NSThread管理多个线程非常困难,不推荐使用 ...
- Swift开发必备技巧:内存管理、weak和unowned
因为 Playground 本身会持有所有声明在其中的东西,因此本节中的示例代码需要在 Xcode 项目环境中运行.在 Playground 中可能无法得到正确的结果. 不管在什么语言里,内存管理的内 ...
- swift的类型系统及类型(内存)信息获取:接口、编译运行时、反射、内存布局
swift是静态语言,没有在运行时保存类型的结构信息(isa.class). 一.self.Self.Type.typeof extension Collection where Self.Eleme ...
- Swift语法基础入门一(适合有C, OC开发人员)
Swift开发体验 /*: 创建对象 * OC: alloc initWithXXX 方法 * Swift: (xxx:) */ /*: 调用方法 * OC: [UIColor redColor]; ...
- Swift零基础教程2019最新版(一)搭建开发环境
Swift简单介绍 Swift是苹果强力推荐的新型开发语言,能开发苹果下属所有软件平台(iOS,iPadOS,macOS,watchOS,tvOS)初学者如果想进入苹果的开发体系,从Swift开始学习 ...
- 基础篇:JVM运行时内存布局
目录 1 JVM的内存区域布局 2 JVM五大数据区域介绍 3 JVM运行时内存布局和JMM内存模型区别 4 JMM内存模型交互操作 欢迎指正文中错误 关注公众号,一起交流 参考文章 1 JVM的内存 ...
- C# Xamarin移动开发基础进修篇
一.课程介绍 英文原文:C# is the best language for mobile app development. Anything you can do in Objective-C, ...
- javaSE基础07
javaSE基础07 一.static静态修饰符 用了static修饰的变量就会变成共享的属性,只会初始化一次,在内存中只存在一个,并且每个对象都可以访问,存放在方法区(数据共享区) 1.1 stat ...
- 《Swift开发指南》
<Swift开发指南> 基本信息 作者: 关东升 赵志荣 丛书名: 图灵原创 出版社:人民邮电出版社 ISBN:9787115366245 上架时间:2014-8-5 出版日期:20 ...
随机推荐
- 4G EPS 中的各种唯一标识
目录 文章目录 目录 电信运营商的唯一标识:PLMN.MCC 与 MNC 移动用户的唯一标识:IMSI.MSIN 与 MSISDN/MDN 移动用户的唯一临时标识:TMSI.GUTI 与 GUMMEI ...
- NumPy 分割与搜索数组详解
NumPy 分割数组 NumPy 提供了 np.array_split() 函数来分割数组,将一个数组拆分成多个较小的子数组. 基本用法 语法: np.array_split(array, indic ...
- Spring Boot中的 6 种API请求参数读取方式
使用Spring Boot开发API的时候,读取请求参数是服务端编码中最基本的一项操作,Spring Boot中也提供了多种机制来满足不同的API设计要求. 接下来,就通过本文,为大家总结6种常用的请 ...
- linux上使用webdav
webdav 干什么用的? 对于我来说,主要是用来同步文件的,n年以前,那时候还啥都不懂,要分享一个文件都是用qq/或者微信发,那时候就一个手机一个电脑,而且文件大部分是分享给认识的人. qq分享完全 ...
- itest work 开源接口测试&敏捷测试管理平台 9.5.0 GA_u3,优化及修复关键 BUG
(一)itest work 简介 itest work (爱测试) 一站式工作站让测试变得简单.敏捷,"好用.好看,好敏捷" ,是itest wrok 追求的目标.itest w ...
- 算法金 | Python 中有没有所谓的 main 函数?为什么?
大侠幸会,在下全网同名[算法金] 0 基础转 AI 上岸,多个算法赛 Top [日更万日,让更多人享受智能乐趣] 定义和背景 在讨论Python为何没有像C或Java那样的明确的main函数之前,让 ...
- [经验分享] VPS安装爱快
前言:本人是作VPN服务端用,配合域名分流,蛮好用.参考1.送一个阿里云腾讯云安装爱快3.X的文档https://bbs.ikuai8.com/thread-97314-1-1.htmlVPS存在的问 ...
- 机器学习算法(一):1. numpy从零实现线性回归
系列文章目录 机器学习算法(一):1. numpy从零实现线性回归 机器学习算法(一):2. 线性回归之多项式回归(特征选取) @ 目录 系列文章目录 前言 一.理论介绍 二.代码实现 1.导入库 2 ...
- 【Socket】解决UDP丢包问题
一.介绍 UDP是一种不可靠的.无连接的.基于数据报的传输层协议.相比于TCP就比较简单,像写信一样,直接打包丢过去,就不用管了,而不用TCP这样的反复确认.所以UDP的优势就是速度快,开销小.但是随 ...
- LeetCode 72. Edit Distance 编辑距离 (C++/Java)
题目: Given two words word1 and word2, find the minimum number of operations required to convert word1 ...