[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fzG5msSeUo0dlQG56j2IOqPVQ1wRijzHG0l5gfGFLc8s":3},{"code":4,"message":5,"data":6},200,"成功",{"id":7,"createdAt":8,"title":9,"content":10,"summary":11,"image":12,"uid":13,"user":14,"categoryId":21,"category":22,"subCategoryId":24,"subCategory":25,"comments":27,"status":17,"reason":28,"notice":28,"visitCount":29,"commentCount":30,"keywords":31},99,"2024-03-21T17:20:17.357Z","Swift知识一览（一）","## 一、概览\nSwift是苹果主推的Apple系列产品的开发编程语言，相较于早期的Objective-C，Swift是一门类型安全的且具有可扩展性的编程语言。\n\nSwift是开源的，apple在github上的开源Swift地址：[https://github.com/apple/swift](https://github.com/apple/swift)。\n\nSwift主要是使用C++编写。\n\nswift的官网：[https://www.swift.org/](https://www.swift.org/)\n\n如官网所说，Swift是一个对入门者友好，也对资深专家强有力支持的具有多用途的编程语言。它快捷、现代、安全且便于编写。\n\n使用领域：\n1. Apple系列平台：比如iOS、macOS、ipadOS等其他平台；\n2. 跨平台命令行：因其语法简洁、安全且轻量；\n3. 服务端和网络：因其内存占用小，启动快、高性能。\n\n目前由于Swift的ABI（Application Binary Interface，应用程序二进制接口）稳定，所以近期并没有新版本频繁更新。\n\n目前最新的稳定版本是5.10。\n\n解释：\nABI是应用程序与操作系统之间的底层接口，相关内容是目标文件格式、数据类型的大小/布局/对齐、函数调用约定等。\nAPI是应用程序编程接口。\n\n## 二、安装\n### 2.1 MacOS系统\n直接安装新版的Xcode，Xcode内置了新版的Swift。\n\n### 2.2 Linux系统\n使用Docker容器就可以获取到官方的Swift镜像。\nDocker容器的基本知识可以在官网学习，也可以看我的博客：https://www.xinwei.ltd/article/83 查看基本命令。\n\n### 2.3 Windows系统\n使用包管理器，比如WinGet。\n\n当在电脑中安装了Swift环境后，就可以在终端通过以下类似指令执行swift的操作。\n![swift -help.jpg](https://image.xinwei.ltd/17110381485881711038168386.jpg)\n\n\n## 三、如何针对不同领域方向学习\n### 3.1 命令行\n可以参考官方教程：[https://www.swift.org/getting-started/cli-swiftpm/](https://www.swift.org/getting-started/cli-swiftpm/)\n### 3.2 跨平台库\n可以参考官方教程：[https://www.swift.org/getting-started/library-swiftpm/](https://www.swift.org/getting-started/library-swiftpm/)\n### 3.3 Web服务\n可以参考官方教程：[https://www.swift.org/getting-started/vapor-web-server/](https://www.swift.org/getting-started/vapor-web-server/)\n### 3.4 iOS和macOS\n可以参考官方教程：[https://www.swift.org/getting-started/swiftui/](https://www.swift.org/getting-started/swiftui/)\n\n## 四、Swift的编译过程\n可以通过在Xcode的Swift代码断点，查看汇编代码，Swift的编译前端是swiftc。以下图片可以揭示编译流程和编译前后端的概况。（以下图片来自于网络）\n![swift编译流程1](https://image.xinwei.ltd/v2-c2ac0057a8b6e26b5dbd8d573253c317_r1711038218911.png)\n![swift编译流程2](https://image.xinwei.ltd/202007252211019341711038244403.png)\n\n\n从图中可以看出，代码编译的过程主要分为以下几步：\n1.生成语法树：`swiftc -dump-ast main.swift`\n2.生成简洁的SIL代码：`swiftc -emit-sil main.swift`\n3.生成LLVM IR代码：`swiftc -emit-ir main.swift -o main.ll`\n4.生成汇编代码：`swiftc -emit-assembly main.swift -9 main.s`\n\n## 五、基本语法和约定\n### 5.1 类型声明\n- let 声明不可变变量\n- var 声明可变变量\n#### 5.1.1 常量\n只能赋值一次，不要求在编译时期确定，但是使用之前必须赋值一次。\n```swift\nlet num = 1\n\nlet num1: Int\nnum1 = 20\n\n// 以下编译会报错\nlet a: Int\nprint(a)\n```\n\n#### 5.1.2 标识符\n可以使用任意字符作为常量名、变量名、函数名，但是不能以数字开头，也不能包含空白符、制表符、箭头等特殊字符。\n```swift\nlet 🤭 = \"emoj1\"\n```\n\n\n### 5.2 数据类型\n在Swift中有2种常见的数据类型\n- 值类型\n- 引用类型\n#### 5.2.1 值类型\n包括以下：\n- 枚举enum、可选类型（本身也是枚举）\n- 结构体struct，包括常见的String、Array、Dictionary、Int、Float、Double、Bool、Character、Set\n\n整型说明：\nInt在32位平台上等价于Int32，在64位平台上等价于Int64。\n\n浮点数说明：\nFloat有32位，精度只有6位\nDouble有64位，精度至少有16位\n\n#### 5.2.2 引用类型\nclass类型\n\n#### 5.2.3  字面量\n其实就是直接确定的值\n比如：\n```swift\nlet num = 1\nlet str = \"hello world\"\nlet decimal = 0x01 // 16进制\nlet hex = 0o02 // 8进制\nlet binary = 0b03 // 2进制\nlet doubleHex = 0xFp2 // 16进制，表示15 * 2(^2)，十进制的60\nlet doubleHex = 0xFp-2 // 16进制，表示15 * 2(^-2)，十进制的3.75\n\n// 数组\nlet arr = [1, 2, 3]\n// 字典\nlet dic = [\"name\" : \"小明\", \"age\" : 10]\n```\n\n\n#### 5.2.4 类型转换\n```swift\nlet num: Int = 1\nlet num1: UInt8 = 2\nlet sum = num + Int(num1)\n\n// 下面也是可以的，数字字面量本身没有明确类型\nlet sum = 1 + 3.14\n```\n\n\n#### 5.2.5 元组（Tuple)\n```swift\nlet response = (200, \"success\")\nprint(\"code is \\(response.0)，message is \\(response.1))\")\n\n// 类似于前端的析构\nlet (code, msg) = response\n\nlet response1 = (code: 200, msg: \"success\")\nprint(\"code is \\(response1.code)，message is \\(response1.msg))\")\n```\n\n### 5.3 流程控制相关\n#### 5.3.1 if-else语句\nif中的语句，只能是bool类型，而不能是像OC中1之类的数字\n```swift\nlet num = 10\nif num > 10 {\n    // ...\n} else if num \u003C 10 {\n    // ...\n} else {\n    \n}\n```\n\n#### 5.3.2 while语句\n```swift\nvar num = 1\nwhite num \u003C 5 {\n    num += 1\n}\n\nvar age = 10\nrepeat { // 相当于do-while\n    age += 1\n} while age \u003C 100\n```\n\n\n#### 5.3.3 for循环\nswift中的for循环，可以有for-in的写法，并且配合区间运算符，轻巧简洁\n```swift\nlet nums = [1, 2, 3, 4, 5]\nfor num in 0...3 {\n    print(\"num is \\(nums[num])\")\n}\n\n// 需要注意以下的循环\nfor var i in 0...3 {\n    i += 1\n    print(i)\n} // 打印的是：1，2，3，4\n\n// 可以省略迭代变量\nfor _ in 0...3 {\n    // do something 4 times\n    print(\"*\")\n}\n```\n\n#### 5.3.4 区间运算符\n- 闭区间：\n0...3：相当于 >= 0，并且 \u003C= 3之间的区间\n区间的类型是：`ClosedRange\u003CT>`\n- 半开区间：\n0..\u003C3：相当于 >= 0，并且 \u003C 3之间的区间\n区间的类型是：`Range\u003CT>`\n- 单侧区间\n```swift\nlet minRange = ...10 // \u003C=10以下的区间范围\nlet maxRange = 10... // >=10以上的区间范围\n\nprint(minRange.contains(5)) // true\nprint(minRange.contains(11)) // false\n\nprint(maxRange.contains(-1)) // false\n```\n\n区间的类型是：`PartialRangeThrough\u003CT>`\n\n这里解释以下类型中的T，后面会解释Swift中的这种泛型。\n\n区间运算符也可以使用字符串，通过ASCII来判断值\n```swift\nlet range = \"a\"...\"f\" // ClosedRange\u003CString>\nrange.contains(\"c\") // true\nrange.contains(\"z\") // false\n```\n\n这里扩充一下，有一个函数，表示有间隔的区间（类似于步进器）：\n```swift\nlet start = 0\nlet interval = 2\nlet max = 100\nfor num in stride(from: start, through: max, by: interval) {\n    print(\"100以内的正偶数:\\(num)\")\n}// 0, 2, 4, 6, ..., 100\n```\n\n\n#### 5.3.5 switch语句 & where\n```swift\nlet num = 5\nswitch num {\n    case 1: // 不允许写{}\n        print(\"是1\")\n        break // 可以不写，也不会贯穿case\n    case 2:\n        print(\"是2\")\n        fallthrough // fallthrough可以实现条件贯穿\n    default: // 不允许写{}\n        print(\"非1非2\")\n}\n```\n\n1. 针对switch中的表达式，需要case枚举尽，或者使用default。\n   default在case穷举完之后，可以不使用default。\n2. case和default后面至少需要一条语句，或者break一下。\n3. case支持复合条件，或者case叠加\n```swift\nlet char = \"a\"\nswitch char {\n    case \"a\", \"A\":\n        print(\"都是a\")\n    default:\n        print(\"非a\")\n}\n\nswitch char {\n    case \"a\":\n    case \"A\":\n        print(\"都是a\")\n    default:\n        print(\"非a\")        \n}\n```\n\n4. 可以使用区间或者元组\n```swift\nlet num = 10\nswitch num {\n    case 1...5:\n        print(\"1\u003C=x\u003C=5以内\")\n    case 5\u003C..10:\n        print(\"5\u003Cx\u003C=10以内\")\n    default:\n        print(\"unknown\")\n}\n\nlet pt = (0, 10)\nswitch pt {\n    case (0, 0):\n        print(\"1\")\n    case (_, 10): // 下划线代表忽略对应位置的值\n        print(\"纵轴是10的点\")\n    case (0, _):\n        print(\"横轴是0的点\")\n    case (-1..\u003C0, 10...20):\n        print(\"在阴影区\")                    \n    default:\n        print(\"unknown\")\n}\n\n// 值绑定\nlet pt2 = (0, 10)\nswitch pt2 {\n    case (let x, 0):\n        print(\"横轴坐标\\(x)\")\n    case (0, let y):\n        print(\"纵轴坐标\\(y)\")\n    case let (x, y):\n        print(\"unknown point (\\(x), \\(y))\")\n}\n```\n\n5. 配合where使用\n```swift\nlet turple = (0, 10)\nswitch turple {\n    case (let x, 10): // 绑定到x\n        print(\"x is \\(x), y is 10\")\n    case (0, let y): // 绑定到y\n        print(\"y is \\(y), x is 0\")\n    default:\n        print(\"anything\")\n}\n\nlet turple1 = (0, 10)\nswitch turple1 {\n    case let (x, y) where x == y: // where条件\n        print(\"x is equal to y\")\n    default:\n        print(\"anything\")\n}\n\nlet nums = [1, 2, 3, 4]\nfor num in nums where num > 2 { // 类似于continue\n    print(\"num is \\(num)\")\n}\n```\n\n\n#### 5.3.6 标签语句\n```swift\nupper: for i in 0...3 {\n    for j in 0...3 {\n        if j > 3 {\n            continue upper        \n        }\n        if i == 3 {\n            break upper        \n        }\n        print(\"i is \\(i), j is \\(j)\")\n    }\n}\n```\n\n\n### 5.4 函数\n#### 5.4.1 定义&声明\n```swift\nfunc dosomthing() {\n    // code\n}\n// 等价于\nfunc dosomething() -> Void {\n    // code\n}\n// 等价于\nfunc dosomething() -> () {\n    \n}\n\n// 带参数\nfunc dosomething(param1: Int, param2: Int) -> Int {\n    return param1 + param2\n}\n\n// 隐式返回\n// 如果整个函数体是一个单一表达式，那么函数会隐式返回这个表达式\nfunc dosomething(a: Int, b: Int) -> Int {\n    a + b\n}\ndosomthing(a: 1, b: 2) // 3\n\n// 返回元组\nfunc getValues(a: Int, b: Int) -> (sum: Int, average: Int) {\n    let sum = a + b\n    return (sum, sum >> 1)\n}\nlet result = getValues(a: 10, b: 2)\nprint(result.sum) // 12\nprint(result.average) // 6\n\n// 函数参数标签\nfunc eat(thing food: String) {\n    print(\"eat \\(food)\")\n}\neat(thing: \"苹果\")\n\n// 省略参数\nfunc eat(_ food: String) {\n    print(\"eat \\(food)\")\n}\neat(\"苹果\")\n```\n\n\n#### 5.4.2 参数相关\n```swift\n// 默认参数值\nfunc addUser(name: String = \"小明\", age: Int, score: Int = 100) {\n    print(\"姓名：\\(name)，年纪：\\(age)，成绩：\\(score)\")\n}\naddUser(name: \"Tom\", age: 10, score: 80)\naddUser(name: \"Tom\", age: 10)\naddUser(age: 10, score: 80)\naddUser(age: 10)\n\n// 如果省略参数标签，此时应注意若，语义模糊，编译器编译时或运行时报错\nfunc addUser(_ name: String = \"小明\", age: Int, _ score: Int = 100) {\n    \n}\n\n// 可变参数，以print为例就是一个可变参数的函数\n// 一个函数最多只能有1个可变参数\n// 紧跟在可变参数后面的参数不可省略参数标签\nfunc addNums(numbers: Int...) -> Int { // 此处的Int...表示一组Int参数\n    var sum = 0\n    for num in numbers {\n        sum += num    \n    }\n    return sum\n}\nsum(numbers: 1, 2, 3, 4)\n\nfunc sumTest(_ a: Int..., name: String, _ age: Int) {\n    \n}\n\n// print函数的定义:\n/// - Parameters:\n/// - items: Zero or more items to print.\n/// - separator: A string to print between each item. The default is a single space (`\" \"`).\n/// - terminator: The string to print after all items have been printed. The\n/// default is a newline (`\"\\n\"`).\npublic func print(_ items: Any..., separator: String = \" \", terminator: String = \"\\n\")\n```\n\n\n#### 5.4.3 inout标记的参数地址传递\n使用inout定义一个输入输出参数，可以在函数内部修改外部实参的值。\n```swift\nfunc swapValue(x: inout Int, y: inout Int) {\n    // 第一种方式\n    let temp = x\n    x = y\n    y = temp\n    // 第二种方式，使用元组\n    (x, y) = (y, x)\n}\n\nvar x = 10, y = 20\nswapValue(&x, &y)\n```\n\n注意：\n- 可变参数（比如：Int...）不能标记为inout\n- inout参数不能有默认值\n- inout参数只能传入可以被多次赋值的\n- inout参数的本质是地址传递（引用传递）\n\n#### 5.4.4 函数重载\n定义：函数名相同，但参数个数不同，或者参数类型不同，或者参数标签不同。\n```swift\nfunc setPosition(x: Float, y: Float) -> (Float, Float) { }\nfunc setPosition(x: Float, y: Float, z: Float) -> (Float, Float) { }\n\nfunc setPosition(x: Float, y: Float) -> (Float, Float) { }\nfunc setPosition(x: Int, y: Float) -> (Float, Float) { }\n\nfunc setPosition(_ x: Float, _ y: Float) -> (Float, Float) { }\nfunc setPosition(a: Int, b: Float) -> (Float, Float) { }\n```\n\n注意以下的代码编译器会报错（非重载，因为返回值的类型和函数重载无关）：\n![非重载情况jpg](https://image.xinwei.ltd/17110391266611711039148415.jpg)\n\n\n默认参数值和函数重载一起使用产生二义性时，编译器不会报错\n```swift\nfunc test(a: Int, b: Int) -> Int {\n    a + b\n}\nfunc test(a: Int, b: Int, c: Int = 3) {\n    a + b + c\n}\ntest(a: 1, b: 2) // 调用的是第一个\n```\n\n注意如果可变参数、省略参数标签、函数重载一起使用产生二义性时，编译器可能会报错。\n\n#### 5.4.5 内联函数 inline function\n开启编译器优化（Release模式下默认开启），某些函数会被编译器编译为内联函数，也就是将函数的调用体展开执行。\n\n（注意：如果函数体比较长，或者含有递归调用，或者含有动态派发，一般不会被自动内联）\n\n一般情况下，Swift不需要手动处理内联条件。但是Swift也提供了手动处理内联函数的语义。\n```swift\n// 这种不会被内联，即使开启编译器优化\n@inline(never) func test() {\n    print(\"test\")\n}\n\n// 开启编译器优化后，代码很长的情况，也会被内联。\n// 但是如果有递归调用、动态派发，还是不会被内联。\n@inline(__always) func test() {\n    print(\"test\")\n}\n```\n\n\n#### 5.4.6 函数类型\n函数是有类型的，由形参类型、返回值类型组成\n```swift\nfunc dosomething() {} // 函数类型是：() -> Void 或者 () -> ()\n// 另外提一句，Void是元组()的类型别名，点击Void进去或者看Swift源码可证\n\nfunc add(a: Int, b: Int) -> Int {} //函数类型是：(Int, Int) -> Int\n\n// 声明\nvar fn: (Int, Int) -> Int = add\nfn(1, 2) // 3 这个时候不需要a或者b形参，因为函数类型不包括形参\n```\n\n\n#### 5.4.7 函数作参数，或者作返回值\n```swift\nfunc add(a: Int, b: Int) -> Int {\n    a + b\n}\nfunc minus(a: Int, b: Int) -> Int {\n    a - b\n}\nfunc decideToHandle(_ fn: (Int, Int) -> Int, a: Int, b: Int) {\n    let result = fn(a, b)\n    print(\"result is \\(result)\")\n}\n\n// 作加法\ndecideHandle(add, a: 1, b: 2)\n// 作减法\ndecideHandle(minus, a: 1, b: 2)\n```\n\n\n返回值如果是函数类型，这种函数叫作高阶函数（higher-order function）\n```swift\nfunc add(a: Int, b: Int) -> Int {\n    a + b\n}\nfunc minus(a: Int, b: Int) -> Int {\n    a - b\n}\nfunc handle(_ useAdd: Bool) -> (Int, Int) -> Int {\n    useAdd ? add : minus\n}\n\n// 作加法\nhandle(true)(1, 2) // 3\n// 作减法\nhandle(false)(1, 2) // -1\n```\n\n\n### 5.5 类型别名typealias\n作用是用来给类型取别名的。\n比如：\n```swift\ntypealias MyInt = Int8\nlet num: MyInt = 4\n\ntypealias CustomFn = (Int, Int) -> Int\nfunc add(a: Int, b: Int) -> Int {\n    a + b\n}\nCustomFn = add\nCustomFn(1, 2) // 3\n```\n\n\n### 5.6 嵌套函数\n指的是在函数内部再定义函数。\n如：\n```swift\nfunc handle(_ useAdd: Bool) -> (Int, Int) -> Int {\n    func add(a: Int, b: Int) -> Int {\n        a + b    \n    }\n    func minus(a: Int, b: Int) -> Int {\n        a - b    \n    }\n    return useAdd ? add : minus\n}\nhandle(true)(1, 2) // 3\nhandle(false)(1, 2) // -1\n```\n\n\n这是《Swift知识一览》系列的第一篇文章，若感兴趣，请关注后续发表的文章。\n\n","这篇文章准备做成一个系列，供所有想学习Swift的开发者阅读，也可以作为资深Swift开发者的查阅手册。文章力求简洁易懂，争取条理清晰，也借阅了官网以及网上的一些资料。该篇文章主要介绍Swift的背景领域、安装、学习方法、编译过程以及部分基本语法约定。","https://image.xinwei.ltd/17110381485881711038168386.jpg",499668042977349,{"phone":15,"userId":13,"nickName":16,"vipType":17,"avatar":18,"sign":19,"createdAt":20},"13121171998","全栈老韩",1,"https://image.xinwei.ltd/images/IMG_5430.JPG","全栈工程师，擅长iOS App开发、前端（vue、react、nuxt、小程序&Taro）开发、Flutter、React Native、后端（midwayjs、golang、express、koa）开发、docker容器、seo优化等。","2024-01-01T16:14:30.305Z",2,{"id":21,"name":23},"IT技术",3,{"id":24,"name":26,"parentId":21},"iOS",[],"",1131,0,"swift,ios swift,ios develop,swift develop,swift开发,ios开发"]