枚举、结构体、类都可以定义方法。

一、方法

方法分为:实例方法、类型方法。

  • 实例方法: 通过实例调用
  • 类型方法: 通过类型调用,用static或者class关键字定义

示例代码:

class Car {
    static var count = 0
    init() {
        Car.count += 1
    }
    static func getCount() -> Int { count }
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.getCount()) // 输出:3

注意:self在实例方法中代表实例,在类型方法中代表类型。

在上面示例代码中,类型方法getCount返回的count等价于self.count、Car.self.count、Car.count

二、mutating

结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改。

func关键字前加mutating可以允许这种修改行为。

示例代码(结构体):

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(deltaX: Double, deltaY: Double) {
        x += deltaX
        y += deltaY
        // 等价下面的代码 
        // self = Point(x: x + deltaX, y: y + deltaY)
    }
}

如果不加mutating会报错:

init oc swift 调用 swift方法调用原理_实例方法

示例代码(枚举):

enum StateSwitch {
    case low, middle, high
    mutating func next() {
        switch self {
        case .low:
            self = .middle
        case .middle:
            self = .high
        case .high:
            self = .low
        }
    }
}

如果不加mutating会报错:

init oc swift 调用 swift方法调用原理_ios_02

三、@discardableResult

func前面加个@discardableResult,可以消除函数调用后返回值未被使用的警告。

示例代码:

struct Point {
    var x = 0.0, y = 0.0
    @discardableResult mutating func moveX(deltaX: Double) -> Double {
        x += deltaX
        return x
    }
}
var p = Point()
p.moveX(deltaX: 10)

如果不加@discardableResult会警告:

init oc swift 调用 swift方法调用原理_init oc swift 调用_03

四、下标(subscript)

html中有标签元素:上标<sup></sup>下标<sub></sub>,经常用来表示数学符号。

Swift中使用subscript可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为下标脚本

subscript的语法类似于实例方法和计算属性的结合体,本质就是方法(函数)。

示例代码:

class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}
var p = Point()
p[0] = 11.1
p[1] = 22.2
print(p.x)  // 输出:11.1
print(p.y)  // 输出:22.2
print(p[0]) // 输出:11.1
print(p[1]) // 输出:22.2

如果不写subscript会报错:

init oc swift 调用 swift方法调用原理_swift_04

特点:

  • subscript中定义的返回值类型决定了:
  • get方法的返回值类型;
  • set方法中newValue的类型
  • subscript可以接受多个参数,并且类型是任意的

细节:

  1. subscript可以没有set方法,但必须要有get方法。
class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}
  1. 如果只有get方法,可以省略get
class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        if index == 0 {
            return x
        } else if index == 1 {
            return y
        }
        return 0
    }
}
  1. 可以设置参数标签(设置标签后,标签名一定要带上,不能省略)。
class Point {
    var x = 0.0, y = 0.0
    subscript(index i: Int) -> Double {
        if i == 0 {
            return x
        } else if i == 1 {
            return y
        }
        return 0
    }
}
print(p[index: 1])
  1. 下标可以是类型方法。
class Point {
    static subscript(row: Int, column: Int) -> String {
        return "\(row)-\(column)"
    }
}
print(Point[2, 3]) // 输出:2-3

结构体、类作为返回值对比:

struct Point {
    var x = 10, y = 10
}

class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        get { point }
    }
}
var pm = PointManager()
print(pm[0].x) // 输出:10
print(pm.point) // 输出:Point(x: 10, y: 10)

上面的代码,如果要对下标赋值,则需要添加set方法:

class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        set { point = newValue }
        get { point }
    }
}
var pm = PointManager()
print(pm[0].x) // 输出:10
print(pm.point) // 输出:Point(x: 10, y: 10)
pm[0].x = 11
print(pm[0].x) // 输出:11
print(pm.point) // 输出:Point(x: 11, y: 10)

注意:pm[0].x = 11等价于pm[0] = Point(x: 11, y: pm[0].y)

如果把上面的Point由结构体(Struct)修改为类(Class),并且不写set方法,会发生什么变化呢?

class Point {
    var x = 10, y = 10
}

class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        get { point }
    }
}
var pm = PointManager()
pm[0].x = 11

编译完美通过,为什么呢?

  • 如果是值类型,pm[0]返回的是临时变量,内部肯定无法修改外面的变量,所以如果要修改值就需要加上set方法。
  • 如果是引用类型,pm[0]返回的就是point指针变量,pm[0].x变为point.x,所以可以直接修改值。