swift语言学习总结

发布于:2024-04-26 ⋅ 阅读:(12) ⋅ 点赞:(0)
  1. Var 表示变量, let表示常量。
  2. 数组和map, 都用中括号[].可以直接赋值。可以用下标或键访问。

var shoppingList = ["catfish", "water", "tulips", "blue paint”]//最后一个可以加逗号。

shoppingList[1] = "bottle of water"

var occupations = [

    "Malcolm": "Captain",

    "Kaylee": "Mechanic",

]

空数组就赋[], 而空map 就赋[:]。有点无语。

  1. 字符串处理。用\() 可以处理字符串的变量插入。

let apples = 3

let oranges = 5

let appleSummary = "I have \(apples) apples."

let fruitSummary = "I have \(apples + oranges) pieces of fruit."

三个双引号(""")来包含多行字符串内容

4.swift也有可空类型, String?, 同样用!表示非空。但是不像其他语言用null表示空, 而是nil。非要别出新裁。

双??的用法和dart一样。

  1. Switch语法,支持任何类型的case。而且不需要用break, 找到case后执行完自己会退出switch,不会往下执行。当需要提前退出时,也可以用break。多个case可以用逗号合并。还有:区别匹配case 100..<1000;元组匹配(1,1)。  Case let Where语句。fallthrough可以继续下一个case。
  2. 条件语句和循环语句都允许不用包含在括号里面, if, for-in, while,repeat-while。
  3. repeat-while就是do-while
  4. 在for循环中,..< 来表示下标范围, 但不包含上界, 如果包含的就用…

var total = 0

for i in 0..<4 {// 0到3

    total += i

}

for i in 0..<4 {// 0到4

    total += i

}

  1. 强制类型转换是用转换后的类型加上括号。Int(3.14)转换后就是3.
  2. 可以定义类名的别名,typealias。   typealias AudioSample = Int   AudioSample就可以当Int来使用了。这种傻逼功能还是少用。让人想起c/c++宏定义魔窟。
  3. 元组。 (name:type, name:type),和kotlin的很像。let http200Status = (statusCode: 200, description: "OK”)。针对函数返回值很有用的。

  1. 可选绑定 if let的用法。 把一个可控类型的参数赋值给一个正常参数,如果可控参数有值,就赋值成功,同时if语句是true, 在if语句里面就不需要用!来表示非空了。还可以多个一起,只要一个不成功就是false。

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {

}

  1. 运算符方面,包含了java的所有功能, 包括三元运算符是一样的,这点很好比kotlin强。另外,增加了??和单双边运算符[…]
  2. 字符串方面和java大同小异。 字符串插值\(), 去语义#”  ”#. 字符串拼接也是+,和java一样。
  3. 数组方面。 数组类型定义是这样:[type],由于有类型推断,也可以不用写。 支持两个数组相加后合并到一起。

15.数组修改,追加append, 删除remove, 追加另外一个数组+=, 插入insert, 判空isEmpty。按照方法来说,应该是链表实现的。

16.集合set,方法和java差不多。

  1. Guard关键词,和 if let相识,只是在不满足时会跟一个else。
  2. 关于函数,总体和kotlin一样。但返回是用->表示;在函数参数前可以加标签注释, 如果参数名前加了下划线_j就不能加了;动态参数用…. ;当函数体只有一条代码,返回时,可以不带return;函数类型的使用也和kotlin类似。
  3. 闭包整体和kotlin的一样。闭包是引用类型。

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

尾随方式

reversedNames = names.sorted(){ (s1, s2) -> Bool in return s1 > s2 }

可以用$0,$1…,来特指参数。 如果其中一个不用,那用_替代。

reversedNames = names.sorted(){ return $0>$1 }

可以把括号也省掉

reversedNames = names.sorted{ return $0>$1 }

如果闭包在最后,那么也可以是尾随闭包。

    func test1( values:String, process:()->Bool){

    }

        test1(values:""){true}

闭包逃逸,就是说闭包传到函数里面,但是没有执行闭包, 只是把闭包引用给了别人。要加一个 @escaping

var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {

    completionHandlers.append(completionHandler)

}

自动闭包就是函数定义。

let customerProvider = { customersInLine.remove(at: 0) } //定义了一个函数;

在函数中直接写执行代码没有括号。要加 @autoclosure

func serve(customer customerProvider: @autoclosure () -> String) {

    print("Now serving \(customerProvider())!")

}

serve(customer: customersInLine.remove(at: 0))

可以即是自动闭包又可以是逃役闭包,

var customerProviders: [() -> String] = []

func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {

    customerProviders.append(customerProvider)

}

  1. 枚举,成员前要加case。

两种定义方式:

enum CompassPoint {

    case north

    case south

    case east

    case west

}

enum Planet {

    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune

}

var directionToHead = CompassPoint.west

一旦 directionToHead 被声明或者被推断出为 CompassPoint 类型,可以间断成这样:

directionToHead = .east

遍历枚举成员:

enum Beverage: CaseIterable {

    case coffee, tea, juice

}

let numberOfChoices = Beverage.allCases.count

枚举关联值,这个功能很好,就是附带信息用的。和kotlin的sealed class有点像,就是没有执行方法。比如把异常的code和error情况塞到里面。

enum Barcode {

    case upc(Int, Int, Int, Int)

    case qrCode(String)

}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)

使用时如下:

switch productBarcode {

case .upc(let numberSystem, let manufacturer, let product, let check):

    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")

case .qrCode(let productCode):

    print("QR code: \(productCode).")

}

枚举初始值。枚举值可以是任何类型的值,可以是int/double/String。

和C#一样,会递增。如果不设值第一个默认是0

enum Planet: Int {

    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune

}

enum CompassPoint: String {

    case north, south, east, west

}

let earthsOrder = Planet.earth.rawValue

// earthsOrder 值为 3

let sunsetDirection = CompassPoint.west.rawValue

// sunsetDirection 值为 "west"

前面提到“枚举值可以是任何类型的值”。所以也可以是枚举自身啊。所以这里就有递归问题了。

而且递归是支持的,用关键字:indirect

enum ArithmeticExpression {

    case number(Int)

    indirect case addition(ArithmeticExpression, ArithmeticExpression)

    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)

}

你也可以在枚举类型开头加上 indirect 关键字来表明它的所有成员都是可递归的:

indirect enum ArithmeticExpression {

    case number(Int)

    case addition(ArithmeticExpression, ArithmeticExpression)

    case multiplication(ArithmeticExpression, ArithmeticExpression)

}

例子:let five = ArithmeticExpression.number(5)

let four = ArithmeticExpression.number(4)

let sum = ArithmeticExpression.addition(five, four)

let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

因为枚举的最终使用还是在选择语句上:

func evaluate(_ expression: ArithmeticExpression) -> Int {

    switch expression {

    case let .number(value):

        return value

    case let .addition(left, right):

        return evaluate(left) + evaluate(right)

    case let .multiplication(left, right):

        return evaluate(left) * evaluate(right)

    }

}

  1. 结构体struct。 swift还保留着struct。可能是内存回收机制缺陷导致的,便于一些情况下的回收。还有就是速度更快一点,相当于是在栈上。

struct是值类型,所以赋值的时候是值拷贝,生成了另外一个struct实例。和类差异并不大。

和类相同的地方:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作用于通过下标语法访问它们的值
  • 定义构造器用于设置初始值
  • 通过扩展以增加默认实现之外的功能
  • 遵循协议以提供某种标准功能

类还有如下的附加功能:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 析构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用

类的恒等运算符

  • 相同(===)
  • 不相同(!==)

使用这两个运算符检测两个常量或者变量是否引用了同一个实例:

if tenEighty === alsoTenEighty {

    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")

}

  1. 属性。 分为存储属性和计算属性。 存储属性就是property,计算属性就是getter/setter方法。

变量用var,常量用let.

懒加载,用 lazy 来标示一个延时加载存储属性。必须声明成var, 因为是构造后才使用, 不能是let。

但是swift的懒加载不支持多线程安全。

class DataImporter {

    /*

    DataImporter 是一个负责将外部文件中的数据导入的类。

    这个类的初始化会消耗不少时间。

    */

    var fileName = "data.txt"

    // 这里会提供数据导入功能

}

class DataManager {

    lazy var importer = DataImporter()

    var data = [String]()

    // 这里会提供数据管理功能

}

计算属性getter/setter, 下面是完整的方式:

struct Point {

    var x = 0.0, y = 0.0

}

struct Size {

    var width = 0.0, height = 0.0

}

struct Rect {

    var origin = Point()

    var size = Size()

    var center: Point {

        get {

            let centerX = origin.x + (size.width / 2)

            let centerY = origin.y + (size.height / 2)

            return Point(x: centerX, y: centerY)

        }

        set(newCenter) {

            origin.x = newCenter.x - (size.width / 2)

            origin.y = newCenter.y - (size.height / 2)

        }

    }

}

简化Get, 当单行代码时。可以省去return。

简化Set, 用newValue替代设置值。

struct CompactRect {

    var origin = Point()

    var size = Size()

    var center: Point {

        get {

            Point(x: origin.x + (size.width / 2),

                  y: origin.y + (size.height / 2))

        }

        set {

            origin.x = newValue.x - (size.width / 2)

            origin.y = newValue.y - (size.height / 2)

        }

    }

}

当只有getter时,可以简化, 省去get关键字和{}。

struct Cuboid {

    var width = 0.0, height = 0.0, depth = 0.0

    var volume: Double {

        return width * height * depth

    }

}

属性观察器

  • willSet 在新的值被设置之前调用
  • didSet 在新的值被设置之后调用

即使新值和当前值相同的时候也不例外。

在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 willSet 和 didSet 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。

class StepCounter {

    var totalSteps: Int = 0 {

        willSet(newTotalSteps) {

            print("将 totalSteps 的值设置为 \(newTotalSteps)")

        }

        didSet {

            if totalSteps > oldValue  {

                print("增加了 \(totalSteps - oldValue) 步")

            }

        }

    }

}

属性包装器,就是getter/setter的套用模版。可以是结构体也可以是类。

@propertyWrapper

struct SmallNumber {

    private var maximum: Int

    private var number: Int

    var wrappedValue: Int {

        get { return number }

        set { number = min(newValue, maximum) }

    }

    init() {

        maximum = 12

        number = 0

    }

    init(wrappedValue: Int) {

        maximum = 12

        number = min(wrappedValue, maximum)

    }

    init(wrappedValue: Int, maximum: Int) {

        self.maximum = maximum

        number = min(wrappedValue, maximum)

    }

}

struct NarrowRectangle {

    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int

    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int

}

这样height最大值就是5,width最大值就是4。

  1. 结构体/类的下标,枚举也可以。定义下标使用 subscript 关键字。

一维数组

subscript(index: Int) -> Int {

    get {

      // 返回一个适当的 Int 类型的值

    }

    set(newValue) {

      // 执行适当的赋值操作

    }

}

二维数组:

subscript(row: Int, column: Int) -> Int {

    get {

      // 返回一个适当的 Int 类型的值

    }

    set(newValue) {

      // 执行适当的赋值操作

    }

}

  1. 继承。 用:继承, 用self替代this, 调用超类方法用super。重写在func前加override。防止重写用final。
  2. 构造函数。全是用init()来替代类名构造, 要么已经初始化好了,直接class()。可选属性在init里可以不初始化。

常量的初始化,可延迟到构造函数里。