2.1 变量、常量与数据类型


2.1 变量、常量与数据类型

本节摘要:养分的最小单位是"一个有名字的值"。本节讲 let 与 var 的选择策略、类型推断与类型注解的分工、五类基础类型的取用场景,以及字符串插值——UI 代码里每行文案几乎都靠它。结论先行:默认用 let,编译器逼你改 var 时才改;类型让编译器推断,歧义处才写注解。

养分的最小单位:声明一个名字

第 1 章结束时种子已经下地,现在开始配养分。第一课不是语法罗列,而是一个工程习惯:在 Swift 里,"不变"是默认态,"可变"是需要理由的。这与很多语言相反,却是 Swift 安全性的第一道门——一个 let 声明的值,任何一行代码都改不了它,读代码的人也因此少背一份"这个值后来变没变"的心智负担。

// 阳台花园的第一批养分 let plantName = "龟背竹" // 常量:名字定了就是它,推断为 String var daysSinceWater = 0 // 变量:天数每天在涨,必须可变 let wateringIntervalDays = 3 // 常量:品种定了周期就定了 daysSinceWater += 1 // 合法:var 可以改 // plantName = "绿萝" // 取消注释会编译错误: // Cannot assign to value: 'plantName' is a 'let' constant print("\(plantName) 每 \(wateringIntervalDays) 天浇水一次,已过 \(daysSinceWater) 天") // 控制台输出:龟背竹 每 3 天浇水一次,已过 1 天

这段代码里的双引号插值 \(...) 就是字符串插值:把任何值嵌进文案。界面上的每一行动态文案——倒计时、计数、状态——都是它的产出。

推断与注解的分工

Swift 的编译器会从初值反推类型,多数时候不需要手写类型;但两种情况必须写注解:没有初值时,以及初值有歧义时(最典型的是数字)。

// 类型推断:编译器从右边推左边 let nickname = "小阳台" // 推断为 String let isThirsty = false // 推断为 Bool let score = 4.8 // 推断为 Double(浮点默认 Double,不是 Float) // 类型注解:初值缺失或歧义时,把类型写在冒号后 var potDiameter: Double // 先声明后赋值,必须注解 potDiameter = 15.5 let humidity: Float = 0.62 // 要 Float 就明说,否则推断成 Double let maxPlants: Int = 30 // 经典歧义:不注解时 16 会推断成 Int,算除法直接截断 let potCount = 16 let shelves = 3 print(Double(potCount) / Double(shelves)) // 5.333333333333333 print(potCount / shelves) // 5(整数除法,小数被扔掉)

💡 关键直觉:整数除法扔小数不是 bug,是类型系统的诚实——Int 里本来就没地方放小数。想要小数,先让值变成 Double。

五类基础类型与取用场景

类型 存什么 阳台花园里的例子 注意点
Int 整数 植物数量、天数计数 除法截断、溢出会崩
Double / Float 小数 湿度、进度比例 默认 Double;判等别用 ==(有误差)
Bool 真/假 该浇水吗、是否收藏 非 Bool 值不会隐式转成 Bool
String 文本 名字、备注 插值 \(...) 随处可用
Character 单个字符 品种缩写代号 双引号声明,与 String 靠类型区分

判等那条值得展开:进度算出来 0.1 + 0.2 与 0.3 用 == 比较结果是 false,浮点误差是所有语言的共同陷阱。界面代码里判断"进度是否到满",用 >= 0.999 或乘成整数再比,别硬碰硬。

图 2-1 一个值的构成:名字、类型、值、可变性

图 2-1 一个值的构成:名字、类型、值、可变性

案例:植物卡片文案的完整过程

背景:详情页顶部要一句摘要文案,形如"龟背竹 · 每 3 天浇水 · 已过 2 天"。操作:先用几个零散变量拼字符串,跑通后再把不变的收进 let、可变的留给 var。

let name = "龟背竹" let interval = 3 var passed = 2 var summary = "\(name) · 每 \(interval) 天浇水 · 已过 \(passed) 天" print(summary) // 龟背竹 · 每 3 天浇水 · 已过 2 天 passed = 3 // 到了浇水日,状态推进 summary = "\(name) · 每 \(interval) 天浇水 · 已过 \(passed) 天" print(summary) // 龟背竹 · 每 3 天浇水 · 已过 3 天 print(summary.count) // 20(字符串长度,含分隔符,count 是字符数不是字节数)

结果:文案随状态更新。解读:注意 passed 改了之后 summary 没有自动变——普通字符串拼好就是死文本,状态变了要重拼。这个"值不会自己更新"的痛点,正是第 5 章 SwiftUI 状态驱动要解决的问题,先在这里埋个记号。变式:把两行拼接收进一个函数(下一节 2.3 的主角),调用处只传状态,文案格式改一处即可全局生效。

类型别名与可读性小灶

两个能让代码更可读的小工具收进工具箱。类型别名给复杂类型起短名:

// 一长串类型起个语义化短名,界面层只认业务词汇 typealias PlantIndex = [String: Int] // 名字到序号的索引表 var aisleOrder: PlantIndex = ["龟背竹": 1, "多肉": 2, "绿萝": 3] print(aisleOrder["多肉"] ?? 0) // 2(字典取值可选,?? 兜底,2.7 详说) // 元组:临时打包几个值,用完即弃,不值得造结构体时用它 let potSpec = (size: 15, color: "陶土", hasDrainHole: true) print("盆:\(potSpec.size) 厘米 \(potSpec.color) 排水孔\(potSpec.hasDrainHole ? "有" : "无")") // 输出:盆:15 厘米 陶土 排水孔有

元组适合函数内部与临时传递,一旦要在多个界面间流转,就升级成结构体(2.4)——"临时用元组、正式用结构体"这条线,和便签纸与笔记本的关系一样。

本节要点回顾

  • let 优先:可变是需要理由的,被编译器拒绝时再改 var;
  • 推断为主、注解补位:无初值或歧义(数字、Float)时才写类型;
  • 无隐式转换:Int 与 Double 混算先显式转换,整数除法会截断;
  • 字符串插值是 UI 文案引擎:任何值都能进 \(...)
  • 浮点判等禁用 ==:用容差或转整数比较。

名字有了、类型有了,下一步要让养分按条件流动——控制流。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U