Swift语法(上)

373 阅读9分钟

前言

学如逆水行舟,不进则退。共勉!!!

越来越多同学打算开始用 Swift 来开发了,可很多人以前都没接触过 Swift。这篇和我以前文章不同的是,本篇只是面向 Swift 零基础的同学。

语法

基础

变量 let, var

变量是可变的,使用var修饰,常量是不可变的,使用let修饰。类、结构体和枚举里的变量是属性。

var v1:String = "hi" // 标注类型
var v2 = "类型推导"
let l1 = "标题" // 常量
class a {
    let p1 = 3
    var p2: Int {
        p1 * 3
    }
}

属性没有set可以省略get,如果有set需加get。变量设置前通过willSet访问到,变量设置后通过didSet访问。

打印 print(“”)

控制台打印值

print("hi")
let i = 14
print(i)
print("9月(i)是小柠檬的生日")
注释 //
// 单行注释
/*
多行注释第一行。
多行注释第二行。
*/ 
// MARK: 会在minimap上展示
// TODO: 待做
// FIXME: 待修复
可选 ?, !

可能会是 nil 的变量就是可选变量。当变量为 nil 通过??操作符可以提供一个默认值。

var o: Int? = nil
let i = o ?? 0
闭包

闭包也可以叫做 lambda,是匿名函数,对应 OC 的 block。

let a1 = [1,3,2].sorted(by: { (l: Int, r: Int) -> Bool in
    return l < r
})
// 如果闭包是唯一的参数并在表达式最后可以使用结尾闭包语法,写法简化为
let a2 = [1,3,2].sorted { (l: Int, r: Int) -> Bool in
    return l < r
}
// 已知类型可以省略
let a3 = [1,3,2].sorted { l, r in
    return l < r
}
// 通过位置来使用闭包的参数,最后简化如下:
let a4 = [1,3,2].sorted { $0 < $1 }
print(a)

函数也是闭包的一种,函数的参数也可以是闭包。@escaping 表示套以闭包,逃逸闭包是可以在函数返回之后继续调用的。@autoclosure 表示自动闭包,可以用来省略花括号。

函数 func

函数可以作为另一个函数的参数,也可以作为另一个函数的返回。函数是特殊的闭包,在类、结构体和枚举中是方法。

// 为参数设置默认值
func f1(p: String = "p") -> String {
    "p is (p)"
}
// 函数作为参数
func f2(fn: (String) -> String, p: String) -> String {
    return fn(p)
}
print(f2(fn:f1, p: "d")) // p is d
// 函数作为返回值
func f3(p: String) -> (String) -> String {
    return f1
}
print(f3(p: "yes")("no")) // p is no

函数可以返回多个值,函数是可以嵌套的,也就是函数里内可以定义函数,函数内定义的函数可以访问自己作用域外函数内的变量。inout 表示的是输入输出参数,函数可以在函数内改变输入输出参数。defer 标识的代码块会在函数返回之前执行。

访问控制

在 Xcode 里的 target 就是模块,使用 import 可导入模块。模块内包含源文件,每个源文件里可以有多个类、结构体、枚举和函数等多种类型。访问级别可以通过一些关键字描述,分为如下几种:

  • open:在模块外可以调用和继承。
  • public:在模块外可调用不可继承,open 只适用类和类成员。
  • internal:默认级别,模块内可跨源文件调用,模块外不可调用。
  • fileprivate:只能在源文件内访问。
  • private:只能在所在的作用域内访问。
    重写继承类的成员,可以设置成员比父类的这个成员更高的访问级别。Setter 的级别可以低于对应的 Getter 的级别,比如设置 Setter 访问级别为 private,可以在属性使用 private(set) 来修饰。

类型

数字 Int, Float

数字的类型有Int、Float和Double

// Int
let i1 = 100
let i2 = 22
print(i1 / i2) //四舍五入得4
// Float
let f1: Float = 100.0
let f2: Float = 22.0
print(f1 / f2) // 4.5454545
// Double
let d1: Double = 100.0
let d2: Double = 22.0
print(d1 / d2) // 4.545454545454546
// 字面量
print(Int(0b10101)) // 0b开头是二进制 
print(Int(0x00afff)) // 0x开头是十六进制
print(2.5e4) // 2.5x10^2
print(2_000_000) // 2000000
布尔数 Bool

布尔数有 true 和 false 两种值,还有一个能够切换这两个值的 toggle 方法。

var b = false\
b.toggle() // true\
b.toggle() // false\
元组 (a, b, c)

元组里的值类型可以是不同的。元组可以看成是匿名结构体。

let t1 = (p1: 1, p2: "two", p3: [1,2,3])
print(t1.p1)
print(t1.p3)
// 类型推导
let t2 = (1, "two", [1,2,3])
// 通过下标访问
print(t2.1) // two
// 分解元组
let (dp1, dp2, _) = t2
print(dp1)
print(dp2)
字符串
let s1 = "Hi! This is a string. Cool?"
/// 转义父\n表示换行。
/// 其它转义字符有 \0 空字符)、\t 水平制表符 、\n 换行符、\r 回车符
let s2 = "Hi!\nThis is a string. Cool?"
// 多行
let s3 = """
Hi!
This is a string.
Cool?
"""
// 长度
print(s3.count)
print(s3.isEmpty)
// 拼接
print(s3 + "\nSure!")
// 字符串中插入变量
let i = 1
print("Today is good day, double (i)(i)!")
/// 遍历字符串
/// 输出:
/// o
/// n
/// e
for c in "one" {
    print(c)
}
// 查找
print(s3.lowercased().contains("cool")) // true
// 替换
let s4 = "one is two"
let newS4 = s4.replacingOccurrences(of: "two", with: "one")
print(newS4)
// 删除空格和换行
let s5 = " Simple line. \n\n  "
print(s5.trimmingCharacters(in: .whitespacesAndNewlines))

Unicode、Character 和 SubString 等内容参见官方字符串文档: Strings and Characters — The Swift Programming Language (Swift 5.1)

枚举

Swift的枚举有类的一些特性,比如计算属性、实例方法、扩展、遵循协议等等。

enum E1:String, CaseIterable {
    case e1, e2
}
// 关联值
enum E2 {
    case e1([String])
    case e2(Int)
}
let e1 = E2.e1(["one","two"])
let e2 = E2.e2(3)
switch e1 {
case .e1(let array):
    print(array)
case .e2(let int):
    print(int)
}
print(e2)
// 原始值
print(E1.e1.rawValue)
// 遵循 CaseIterable 协议可迭代
for ie in E1.allCases {
    print("show (ie)")
}
// 递归枚举
enum RE {
    case v(String)
    indirect case node(l:RE, r:RE)
}
let lNode = RE.v("left")
let rNode = RE.v("right")
let pNode = RE.node(l: lNode, r: rNode)
switch pNode {
case .v(let string):
    print(string)
case .node(let l, let r):
    print(l,r)
    switch l {
    case .v(let string):
        print(string)
    case .node(let l, let r):
        print(l, r)
    }
    switch r {
    case .v(let string):
        print(string)
    case .node(let l, let r):
        print(l, r)
    }
}
泛型

泛型可以减少重复代码,是一种抽象的表达方式。where 关键字可以对泛型做约束。

func fn<T>(p: T) -> [T] {
var r = [T]()
r.append(p)
return r
}
print(fn(p: "one"))
// 结构体
struct S1<T> {
var arr = [T]()
mutating func add(_ p: T) {
arr.append(p)
}
}
var s = S1(arr: ["zero"])
s.add("one")
s.add("two")
print(s.arr) // ["zero", "one", "two"]
关联类型
protocol pc {
    associatedtype T
    mutating func add(_ p: T)
}
struct S2: pc {
    typealias T = String // 类型推导,可省略
    var strs = [String]()
    mutating func add(_ p: String) {
        strs.append(p)
    }
}
不透明类型

不透明类型会隐藏类型,让使用者更关注功能。不透明类型和协议很类似,不同的是不透明比协议限定的要多,协议能够对应更多类型。

protocol P {
    func f() -> String
}
struct S1: P {
    func f() -> String {
        return "one\n"
    }
}
struct S2<T: P>: P {
    var p: T
    func f() -> String {
        return p.f() + "two\n"
    }
}
struct S3<T1: P, T2: P>: P {
    var p1: T1
    var p2: T2
    func f() -> String {
        return p1.f() + p2.f() + "three\n"
    }
}
func someP() -> some P {
    return S3(p1: S1(), p2: S2(p: S1()))
}
let r = someP()
print(r.f())
类型转换

使用 is 关键字进行类型判断, 使用 as 关键字来转换成子类。

class S0 {}
class S1: S0 {}
class S2: S0 {}
var a = [S0]()
a.append(S1())
a.append(S2())
for e in a {
    // 类型判断
    if e is S1 {
        print("Type is S1")
    } else if e is S2 {
        print("Type is S2")
    }
    // 使用 as 关键字转换成子类
    if let s1 = e as? S1 {
        print("As S1 (s1)")
    } else if let s2 = e as? S2 {
        print("As S2 (s2)")
    }
}

类和结构体

类可以定义属性、方法、构造器、下标操作。类使用扩展来扩展功能,遵循协议。类还以继承,运行时检查实例类型。

class C {
     var p: String
     init(_ p: String) {
         self.p = p
     }
     // 下标操作
     subscript(s: String) -> String {
         get {
             return p + s
         }
         set {
             p = s + newValue
         }
     }
 }
 let c = C("hi")
 print(c.p)
 print(c[" ming"])
 c["k"] = "v"
 print(c.p)
结构体

结构体是值类型,可以定义属性、方法、构造器、下标操作。结构体使用扩展来扩展功能,遵循协议。

struct S {
    var p1: String = ""
    var p2: Int
}
extension S {
    func f() -> String {
        return p1 + String(p2)
    }
}
var s = S(p2: 1)
s.p1 = "1"
print(s.f()) // 11
属性

类、结构体或枚举里的变量常量就是他们的属性。

struct S {
    static let sp = "类型属性" // 类型属性通过类型本身访问,非实例访问
    var p1: String = ""
    var p2: Int = 1
    // cp 是计算属性
    var cp: Int {
        get {
            return p2 * 2
        }
        set {
            p2 = newValue + 2
        }
    }
    // 只有 getter 的是只读计算属性
    var rcp: Int {
        p2 * 4
    }
}
print(S.sp)
print(S().cp) // 2
var s = S()
s.cp = 3
print(s.p2) // 5
print(S().rcp) // 4

willSet 和 didSet 是属性观察器,可以在属性值设置前后插入自己的逻辑处理。

方法
enum E: String {
    case one, two, three
    func showRawValue() {
        print(rawValue)
    }
}
let e = E.three
e.showRawValue() // three
// 可变的实例方法,使用 mutating 标记
struct S {
    var p: String
    mutating func addFullStopForP() {
        p += "."
    }
}
var s = S(p: "hi")
s.addFullStopForP()
print(s.p)
// 类方法
class C {
    class func cf() {
        print("类方法")
    }
}

static和class关键字修饰的方法类似 OC 的类方法。static 可以修饰存储属性,而 class 不能;class 修饰的方法可以继承,而 static 不能。在协议中需用 static 来修饰。

继承
// 类继承
class C1 {
    var p1: String
    var cp1: String {
        get {
            return p1 + " like ATM"
        }
        set {
            p1 = p1 + newValue
        }
    }
    init(p1: String) {
        self.p1 = p1
    }
    func sayHi() {
        print("Hi! (p1)")
    }
}
class C2: C1 {
    var p2: String
    init(p2: String) {
        self.p2 = p2
        super.init(p1: p2 + "'s father")
    }
}
C2(p2: "Lemon").sayHi() // Hi! Lemon's father
// 重写父类方法
class C3: C2 {
    override func sayHi() {
        print("Hi! (p2)")
    }
}
C3(p2: "Lemon").sayHi() // Hi! Lemon
// 重写计算属性
class C4: C1 {
    override var cp1: String {
        get {
            return p1 + " like Out of the blade"
        }
        set {
            p1 = p1 + newValue
        }
    }
}
print(C1(p1: "Lemon").cp1)### // Lemon like ATM
print(C4(p1: "Lemon").cp1)### // Lemon like
Out of the blade

通过 final 关键字可以防止类被继承,final 还可以用于属性和方法。使用 super 关键字指代父类。

函数式

map

map 可以依次处理数组中元素,并返回一个处理后的新数组。

let a1 = ["a", "b", "c"]
let a2 = a1.map {
"\($0)2"
}
print(a2) // ["a2", "b2", "c2"]

使用 compactMap 可以过滤 nil 的元素。flatMap 会将多个数组合成一个数组返回。

filter

根据指定条件返回

let a1 = ["a", "b", "c", "call my name"]
let a2 = a1.filter {
    $0.prefix(1) == "c"
}
print(a2) // ["c", "call my name"]
reduce

reduce 可以将迭代中返回的结果用于下个迭代中,并,还能让你设个初始值。

let a1 = ["a", "b", "c", "call my name.", "get it?"]
let a2 = a1.reduce("Hey u,", { partialResult, s in
    // partialResult 是前面返回的值,s 是遍历到当前的值
    partialResult + " (s)"
})
print(a2) // Hey u, a b c call my name. get it?
sorted

排序

// 类型遵循 Comparable
let a1 = ["a", "b", "c", "call my name.", "get it?"]
let a2 = a1.sorted()
let a3 = a1.sorted(by: >)
let a4 = a1.sorted(by: <)
print(a2) // Hey u, a b c call my name. get it?
print(a3) // ["get it?", "call my name.", "c", "b", "a"]
print(a4) // ["a", "b", "c", "call my name.", "get it?"]
// 类型不遵循 Comparable
struct S {
    var s: String
    var i: Int
}
let a5 = [S(s: "a", i: 0), S(s: "b", i: 1), S(s: "c", i: 2)]
let a6 = a5
    .sorted { l, r in
        l.i > r.i
    }
    .map {
        $0.i
    }
print(a6) // [2, 1, 0]

控制流

If If let If case let
// if
let s = "hi"
if s.isEmpty {
    print("String is Empty")
} else {
    print("String is (s)")
}
// 三元条件
s.isEmpty ? print("String is Empty again") : print("String is (s) again")
// if let-else
func f(s: String?) {
    if let s1 = s {
        print("s1 is (s1)")
    } else {
        print("s1 is nothing")
    }
    // nil-coalescing
    let s2 = s ?? "nothing"
    print("s2 is (s2)")
}
f(s: "something")
f(s: nil)
// if case let
enum E {
    case c1(String)
    case c2([String])
    func des() {
        switch self {
        case .c1(let string):
            print(string)
        case .c2(let array):
            print(array)
        }
    }
}
E.c1("enum c1").des()
E.c2(["one", "two", "three"]).des()
Guard guard, guard let

更好地处理异常情况

// guard
func f1(p: String) -> String {
    guard p.isEmpty != true else {
        return "Empty string."
    }
    return "String (p) is not empty."
}
print(f1(p: "")) // Empty string.
print(f1(p: "lemon")) // String lemon is not empty.
// guard let
func f2(p1: String?) -> String {
    guard let p2 = p1 else {
        return "Nil."
    }
    return "String (p2) is not nil."
}
print(f2(p1: nil)) // Nil.
print(f2(p1: "lemon")) // String lemon is not nil.
For-in
let a = ["one", "two", "three"]
for str in a {
    print(str)
}
// 使用下标范围
for i in 0..<10 {
    print(i)
}
// 使用 enumerated
for (i, str) in a.enumerated() {
    print("第(i + 1)个是:(str)")
}
// for in where
for str in a where str.prefix(1) == "t" {
    print(str)
}
// 字典 for in,遍历是无序的
et dic = [    "one": 1,    "two": 2,    "three": 3]
for (k, v) in dic {
    print("key is (k), value is (v)")
}
// stride
for i in stride(from: 10, through: 0, by: -2) {
    print(i)
}
/*
 10
 8
 6
 4
 2
 0
 */
While while, repeat-while
// while
var i1 = 10
while i1 > 0 {
    print("positive even number (i1)")
    i1 -= 2
}
// repeat while
var i2 = 10
repeat {
    print("positive even number (i2)")
    i2 -= 2
} while i2 > 0

使用 break 结束遍历,使用 continue 跳过当前作用域,继续下个循环

Switch
func f1(pa: String, t:(String, Int)) {
    var p1 = 0
    var p2 = 10
    switch pa {
    case "one":
        p1 = 1
    case "two":
        p1 = 2
        fallthrough // 继续到下个 case 中
    default:
        p2 = 0
    }
    print("p1 is (p1)")
    print("p2 is (p2)")
    // 元组
    switch t {
    case ("0", 0):
        print("zero")
    case ("1", 1):
        print("one")
    default:
        print("no")
    }
}
f1(pa: "two", t:("1", 1))
/*
 p1 is 2
 p2 is 0
 one
 */
// 枚举
enum E {
    case one, two, three, unknown(String)
}
func f2(pa: E) {
    var p: String
    switch pa {
    case .one:
        p = "1"
    case .two:
        p = "2"
    case .three:
        p = "3"
    case let .unknown(u) where Int(u) ?? 0 > 0 : // 枚举关联值,使用 where 增加条件
        p = u
    case .unknown(_):
        p = "negative number"
    }
    print(p)
}
f2(pa: E.one) // 1
f2(pa: E.unknown("10")) // 10
f2(pa: E.unknown("-10")) // negative number

小结

故不及跬步无以致千里,不积小流无以成江海。想成为大神都是从小白一点一点学习变成大神的,所以我们最重要的就是保持学习的习惯。优秀的人已经点赞和关注了,祝你早日成为大牛。

iOS资料|地址

相关阅读: Swift 并发初步