Files
knowledge-kit/Chapter1 - iOS/1.123.md
2024-04-27 13:01:58 +08:00

3.7 KiB
Raw Blame History

Swift 模式匹配

模式匹配的底层实现

为了去除其他语句对汇编研究造成的干扰,所以 case 和 default 里面都很简单,聚焦于研究 case 的模式匹配实现。测试代码如下:

var age = 28
switch age {
case 0...30: 
    break
default:
    break
}

case 0...30: 处加断点可以看到汇编,然后看到可疑方法 Swift.RangeExpression.~= infix(τ_0_0, τ_0_0.Bound) -> Swift.Bool

LLDB 输入 si 进去窥探下

看样子,函数还没到底,看到地址 0x00007ff81a86f530 很大很大,猜测应该是一个系统动态库方法地址,继续跟进去研究 si

可以看到内部还有函数调用

继续跟进 si

看上去是在做继续 clasedRange 区间符合的判断,继续跟进 si,里面确实是在判断是否命中区间的判断。不一一研究了,这次目的是判断 switch...case pattern 的实现。

结论switch case pattern 模式匹配,系统底层实现是依赖

模式匹配的应用

自定义模式匹配。根据上面通过汇编进行分析,我们知道

  • static func ~=(pattern: case 后面的值, value: switch 后面的值) pattern 代表的是 case 后面的值value 代表 switch 后面的值。

  • 当 case 有不同类型的时候,如果编译报错,则需要重写 static func ~=(pattern: ,value: ) 方法,调整 pattern 的数据类型,数据类型应该和 case 后的数据类型一致(可以是函数等)

struct Student {
    var score = 0
    var name = ""
    
    
    /// Student 和 Int 模式匹配的方法
    /// - Parameters:
    ///   - pattern: case 后面的内容
    ///   - value: switch 后面的内容
    /// - Returns: 是否命中
    static func ~= (pattern: Int, value: Student) -> Bool {
        value.score >= pattern
    }
    static func ~= (pattern: Range<Int>, value: Student) -> Bool {
        pattern.contains(value.score)
    }
    static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool {
        pattern.contains(value.score)
    }
    static func ~= (pattern: String, value: Student) -> Bool {
        Int(pattern) == value.score
    }
}

var student: Student = Student(score: 55, name: "杭城小刘")
switch student {
case 100:
    print(">=100")
case 90:
    print(">=90")
case 80..<90:
    print("[80, 90)")
case 60...79:
    print("[60, 79]")
case "55":
    print("斯国一")
default:
    print("just so so")
}
// console
斯国一

修改 var student: Student = Student(score: 78, name: "杭城小刘") 则输出 [60, 79]

extension String {
    static func ~=(pattern: (String) -> Bool ,value: String) -> Bool {
        pattern(value)
    }
}

func hasPrefix(_ prefix: String) -> (String) -> Bool {
    {$0.hasPrefix(prefix)}
}

func hasSuffix(_ prefix: String) -> (String) -> Bool {
    {$0.hasSuffix(prefix)}
}

var name = "城小刘"
switch name {
case hasPrefix("杭"):
    print("有杭哦")
case hasSuffix("刘"):
    print("有刘哦")
default:
    print("just a name")
}
// console
有刘哦