首页 / Swift 编程语言教程 / 结构体的属性与方法

Swift 编程语言教程

结构体的属性与方法

本教程共 93 篇 · 第 52 篇 · 更新于 2026-08-08 · 约 8 分钟阅读

SwiftSwift 编程语言教程结构体struct实例方法类型方法mutatingself

本节目标:读完你能在结构体里定义实例属性、实例方法、类型属性和类型方法,并理解 self 与 mutating 的作用。

1-1 属性与方法都属于类型

结构体里能放两样东西:属性(存数据)和方法(做事的函数)。它们都写在结构体花括号内,通过点语法访问。这正是面向对象里”封装”的雏形:数据和行为绑在一起。和类不同,结构体(以及枚举)也能拥有方法,这是 Swift 相比某些老语言更灵活的地方。

1-2 实例属性

实例属性属于某个具体实例,每个实例各有一份:

struct Counter {
    var count = 0
    func describe() -> String {
        return "当前计数 \(count)"
    }
}

count 是存储属性,describe 是实例方法。每个 Counter 实例都有自己的 count,互不影响。

1-3 实例方法

实例方法是挂在类型上的函数,语法和普通函数一样。它默认能访问同类型的其他属性和方法:

var c = Counter()
print(c.describe())  // 当前计数 0

方法通过 self 隐式指向当前实例。多数时候不用写 self,编译器会自动识别属性名。只有当参数名和属性名冲突时,才用 self 区分:

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

这里 self.x 是属性,x 是参数,不写 self 编译器会误以为两个都是参数。self 就像在方法里说”我自己身上的那个 x”,用来消除二义性。

1-4 mutating 修改自身

结构体是值类型,方法默认不能改属性。要改,必须标 mutating

var p = Point(x: 1, y: 1)
p.moveBy(x: 2, y: 3)

mutating 方法还能整体替换 self:self = Point(x: ..., y: ...)。常量结构体不能调用 mutating 方法,因为属性不可改。

Tip

为什么值类型的方法默认不能改自己?因为结构体赋值即拷贝,如果方法偷偷改了自己,拷贝语义就乱了。mutating 等于明明白白告诉编译器:“这个方法会改值,请按拷贝语义处理。“枚举也能用 mutating,用来在方法里切换到另一个成员。

1-5 类型属性与类型方法

上面都是”实例”级别。还有”类型”级别——属于类型本身,所有实例共享一份,用 static 标记:

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1
    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }
    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }
}

类型属性/方法通过类型名调用:LevelTracker.unlock(2),不依赖任何实例。类型方法里的 self 指的是类型本身,所以它能直接访问别的类型属性和类型方法,不必加前缀。

1-6 存储属性 vs 计算属性(预告)

结构体属性分两类:存储属性真正存值(如 count、x);计算属性不存值,靠其他属性实时算(如矩形的中心点)。计算属性和属性观察器下一章专讲,这里先有个印象。记住一句话:方法描述”动作”,属性描述”状态”,两者配合才是一个完整的结构体。

1-7 方法也能返回新实例

实例方法除了改属性,也能返回一个新实例。比如给 Point 加一个 func scaled(by factor: Double) -> Point,返回放大后的新点,而不改动自己。这种”返回新值而非修改自身”的风格,在函数式写法里很常见,也天然契合值类型的不可变特性。

当你的方法既可能改自己、也可能返回新值时,记得区分:要改自己就标 mutating 并直接改属性;要保留原值就别标 mutating,返回一个新建的实例。两者语义不同,别混用。

1-8 类型方法里的 self 指类型

再强调一次:类型方法(static 标记的)里,self 指的是类型本身,不是某个实例。所以你能在类型方法里直接写 highestUnlockedLevel 访问类型属性,也能直接调用其他类型方法。如果类型方法里想访问实例属性,那是不行的——实例属性得通过某个具体实例才能用。

这条规则和方法里的 self 形成对照:实例方法里 self 是实例,类型方法里 self 是类型。搞清当前身处哪一层,就不会写出”在类型方法里访问实例属性”的编译错误。

1-9 一个综合视角

把前面几点串起来:结构体 = 一组存储属性(数据)+ 一组方法(行为)。实例级别处理”某一个具体对象”,类型级别(static)处理”全体共享的东西”。mutating 决定方法能不能改自身,self 决定”我指的是实例还是类型”。

理清这四组关系——属性/方法、实例/类型、mutating、self——结构体的方法部分就站住了。后面章节会在类里看到同样的机制,只是类多了引用语义和继承。

1-10 方法和属性的命名约定

写结构体的属性和方法时,命名也有讲究。属性用名词或形容词(width、isActive),方法用动词短语(moveBy、reset),一眼能区分”这是数据”还是”这是动作”。参数名外部标签要读起来像句子:moveBy(x:y:) 调用时像 p.moveBy(x: 2, y: 3),自解释。

好的命名让结构体用起来像在说人话,也减少注释需求。Swift 社区特别在意这点,初学时就养成习惯,后面读别人代码会轻松很多。

1-11 实例方法与类型方法的分工

一句话总结分工:实例方法回答”这个具体对象能做什么”,类型方法回答”这一类整体能做什么”。前者操作某个实例的状态,后者操作所有实例共享的状态(如最高记录、全局开关)。把逻辑放在正确的层级,代码职责才清晰,也避免为了改一个全局值而到处传实例。

当你写一个方法时,先问自己:它用到了 self 的具体状态吗?用到了,就是实例方法;只用类型自己的属性和其他方法,就是类型方法。这个判断简单但关键。

1-12 小结

结构体可放实例属性、实例方法、类型属性和类型方法。方法用 self 指当前实例,冲突时才显式写。改属性的实例方法要标 mutating。static 标记类型成员,通过类型名访问、全实例共享。下章深入计算属性与属性观察器。