批里批里 (゜-゜)つ🍺 干|

七つ一旋桜

园龄:4年2个月粉丝:6关注:3

📂笔记
🔖swift
2021-09-16 23:04阅读: 46评论: 0推荐: 0

Swift入门笔记

基础语法

新建变量

新建变量有两种方式:

  1. 新建变量

    使用var关键字

    var a = "xxx"
    
  2. 新建常量

    使用let关键字

    let a = "xxx"
    

打印输出可以使用

接受console输入可以使用readLine()函数,输出到console可以使用print()函数


数据类型

内置数据类型

  1. Int

    整型,字长受平台影响

  2. Uint

    无符号整型,字长受平台影响

  3. Float,Double

    浮点型

  4. Bool

    布尔值

  5. String

    字符串

  6. Charactor

    字符类型

  7. Optional

    可选类型:表示有值或没有值

  8. Any和AnyObject

    任意类型,AnyObject表示任何类的实例,Any表示任何类型

类型别名

关键字:typealias

typealias myint = Int
var x: myint = 1
print(x)
print("\(x)") // 等同于 print(x)

可选类型(写法和用法同kotlin)

即可以为nil值的类型

Swfit语言定义后缀作为命名类型Optional的简写,换句话说,以下两种声明是相等的:

var optionalInteger: Int?
var optionalInteger: Optional<Int>

Optional 是一个含有两种情况的枚举,NoneSome(T),用来表示可能有或可能没有值。任何类型都可以明确声明为(或者隐式转换)可选类型。当声明一个可选类型的时候,要确保用括号给 ? 操作符一个合适的范围。例如,声明可选整数数组,应该写成 (Int[])? 写成 Int[]? 会报错。

当你声明一个可选变量或者可选属性的时候没有提供初始值,它的值会默认为 nil


运算符

算术运算符

swift3中取消了++--运算,其他运算符用法和写法同c

区间运算符

  • 闭区间运算符

    闭区间运算符a...b定义一个包含从a到b(包括a和b)的所有值的区间,b必须大于等于a。 ‌ 闭区间运算符在迭代一个区间的所有值时是非常有用的。1...5 区间值为 1, 2, 3, 4 和 5

  • 半开区间运算符

    半开区间a..<b定义一个从a到b但不包括b的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。1..< 5 区间值为 1, 2, 3, 和 4

// 闭区间运算符
for i in 1...5 {
    print("\(i)")
}
// 半开区间运算符
for i in 1..<5 {
    print("\(i)")
}

运算结果分别为:

1
2
3
4
5
1
2
3
4

区间运算符可以创建切片

var slice = arr[0..<arr.count()]

三元运算符

condition ? X : Y

用法同c


条件分支

if-else同c

switch用法同golang


循环

c中的for循环在swift3中已被弃用

swift没有do-while

swift中repeat-while用法同c中的do-while

for-in语句用法同python


字符串

// 使用字面量创建空字符串
var s = ""
// 实例化String类创建字符串
var s = String()

在字符串中插入值

var a = 1, b = 2
var s = "\(a) + \(b) = \(a + b)"
print(s)

输出结果

1 + 2 = 3

字符串长度

字符串长度使用 String.count 属性来计算

Swift 3 版本使用的是 String.characters.count

var s = "xyz"
print("\(s.count)")

输出结果

3

字符

字符是长度为1的字符串

var c: Character = "A"

遍历字符串中每个字符

for ch in "string".characters {
    print(ch)
}

数组

用构造语法创建

var arr = [Type]()
var someArray = [SomeType](repeating: InitialValue, count: NumbeOfElements)

在数组末尾续接元素可以用append()方法或者+=运算符

var arr = [1, 2, 3]
print("\(arr)")

输出结果

[1, 2, 3]

使用count()方法何以获取元素个数


字典

使用构建语法创建字典

var dict = [keyType: valueType]()
var d = [1:"One", 2:"Two", 3:"Three"]

遍历字典

for (key, val) in dict {
    print("\(key):\(val)")
}

可以使用count()方法输出字典中键值对个数


函数

func f(x) -> returnType {
    ...
    return params
}

eg.

func add(a:Int, b:Int) ->Int {
    return a + b
}
print(add(a:1, b:2)) // 参数要指定名称

输出

3

和rust, kotlin等语言一样函数可以使用元组作为返回值,或者无返回值

外部参数名

声明函数时可以给参数列表的参数起别名,如果写了外部参数名就必须使用外部参数名

func add(num1 a:Int, num2 b:Int)-> Int{
    return a + b
}
print(add(num1:1, num2:2))

可变参数

可变参数通过在变量类型名后面加入(...)的方式来定义

func output<T>(params: T...) {
    for i in params {
        print(i)
    }
}
output(params: 1, 2, 3)

输出结果

1
2
3

值传参和引用传参

函数默认进行值传参,如果要使用引用传参需要使用inout关键字

func swap(x: inout Int, y: inout Int) {
    let z = x
    x = y
    x = z
}
var a = 1, b = 2
swap(x:&a, y:&b) // 调用要使用&操作符
print("\(a) \(b)")

闭包

Swift中的闭包有很多优化的地方:

  1. 根据上下文推断参数和返回值类型
  2. 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
  3. 可以使用简化参数名,如0,1(从0开始,表示第i个参数...)
  4. 提供了尾随闭包语法(Trailing closure syntax)

swift的闭包比较类似于lambda表达式

let f = {(a: Int, b: Int) -> Int in
	return a + b
}
print(f(1, 2)) // 不用指定参数名

输出结果

3

闭包表达式

闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。

sort方法

Swift 标准库提供了名为 sorted(by:) 的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。

var arr1 = [1, 2, 3, 4, 5]
print(arr1)
func cmp(a: Int, b: Int) -> Bool {
    return a > b
}
arr1 = arr1.sorted(by: cmp)
print(arr1)

var arr1 = [1, 2, 3, 4, 5]
print(arr1)
arr1 = arr1.sorted(by: {(a: Int, b: Int) -> Bool in return a > b})
print(arr1)

输出结果

[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]

简化:

arr = arr,sorted(by: {s1,s2 in return s1 > s2})
arr = arr.sorted(by: {s1,s2 in s1 > s2})
arr = arr.sorted(by: {$0 > $1})
arr = arr.sorted(by: >)

以上四种写法都是可行的,具体简化到哪种程度需要看是否会影响到代码可读性

无参无返回值闭包

当闭包的类型为() -> Void时则称之为无参数无返回值闭包

闭包的应用

  1. forEach

    // 遍历集合中的每一个元素
    var collection = [1, 2, 3]
    collection.forEach{
        print($0)
    }
    
  2. filter

    // 根据条件筛选元素
    var arr = [1, 2, 3]
    arr = arr.filter{
        return $0 & 1 == 0
    }
    print(arr)
    
  3. reduce

    // reduce 有两个参数,第一个为整型作为起始值,第二个为闭包
    // 让起始值与每个元素进行闭包中的操作
    arr = arr.reduce{
    	return $0 + $1
    }
    
  4. map

    // 和js一样
    

枚举

定义枚举

enum Number{
    case One
    case Two
    case Three
    case Four
}

定义枚举必须开头大写名称,属性必须用case定义

使用

// 赋值
var a = Number.One
var b:Number = .Two

原始值

enum Number: Int {
    case One = 1 // 定义第一个值之后往后都会递增
    case Two
    case Three
}
var a = Number.One
var b = a.rawValue // 获取原始值
print(a)
print(b)

输出结果

One
1

结构体与类

swift中结构体是值类型,类是引用类型

等价运算符

引用类型和值类型存在本质差别,因此用引用类型变量比较是否相等时应该使用===,他表示两个引用类型的变量是否引用了同一个类实例

延迟储存属性

在声明属性时在前面加一个lazy关键字表示该属性为延迟存储属性。延迟储存属性在实例第一次被调用才会赋初始值

class Foo {
    var a = 0
    lazy var b = 0
}

getter & setter

class Foo {
    var a = 0
    var bool: String{
        get {
            return (a == 0 ? "False" : "True")
        }
        set {
			switch newValue {
                case "False": a = 0
                case "True": a = 1
                default: break
            }
        }
    }
}
var f = Foo()
f.a = 1
print(f.bool)
f.bool = "False"
print(f.a)

输出结果

True
0

属性观察器

swift提供了一种叫做属性观察器的机制监控属性的变化。

属性观察期有两个方法willSet()didSet()

willSet()在属性的值被改变前调用,旧值会作为常量参数传入,默认名为newValuedidSet()在属性的值被改变后调用,旧值作为参数传入,默认名为oldvalue,默认名均可改

class Foo {
    var a = 0 {
        willSet(params) {
            print("a will be set to \(params)")
        }
        didSet {
            print("a did be from \(oldValue) to \(a)")
        }
    }
}
var foo = Foo()
foo.a = 10

输出结果

a will be set to 10
a did be from 0 to 10

全局属性

每一个类的一个实例都有自己独立的属性,如果要把一个类的一个所有实例共享一个属性的话可以用类型属性

声明类型属性使用关键字static

class Foo {
    var a = 0
    static var b = 2
}
print("\(Foo.b)")
Foo.b = 10
print("\(Foo.b)")

输出结果

2
10

可变方法

swift中类的方法可以改变属性的值,但是结构体的方法不可以,如果结构体的方法想要改属性的值则需要加mutating关键字

struct Foo {
    var a = 0
    mutating func mut() {
        a += 1
    }
}
var foo = Foo()
foo.mut()
print(foo.a)

输出结果

1

下标方法

通过索引采集和中获取值的方法,运算符为[]

在类、结构体、和枚举类型中可以自定义下标方法从而对实例的属性进行访问

声明格式:

subscript(index: Int)->Int{
    get{
        // return index
    }
    set(newValue){
        // set new value
    }
}

继承性

子类可以调用父类的方法,访问父类的属性和下标,还可以重载父类的方法、属性和下标

定义一个子类的格式

class SubClass: SuperClass {
    definition
}

重载

子类重载父类的特性时需要加override关键字

class SuperClass{
    func f() {
        print("这个方法属于父类")
    }
}
class SubClass: SuperClass {
    override func f() {
        print("这个方法被重载了")
    }
}

除了重载方法还可以重载属性(即重载属性的getter方法和setter方法,还可以给被重载的属性添加属性观察器)

禁止重载

子类可以重载父类的特性,如果要禁止父类的特性被重载可以使用final关键字

class SuperClass {
    final var a = 0
    final func f() {
        print("none")
    }
}

构造和析构

  1. 定义构造器

    class Foo {
        init() {
            // initialization process
        }
    }
    

    构造器使用init关键字命名方法,可以没有参数

    class Foo {
        var a: String
        var b: Int
        var c: Double
        init() { // 无参构造不可省
            a = ""
            b = 0
            c = 0
        }
        init(a:String, b:Int, c: Double) {
            self.a = a
            self.b = b
            self.c = c
        }
    }
    var foo1 = Foo()
    var foo2 = Foo(a:"abc", b:1, c:2)
    print("\(foo1.a), \(foo1.b), \(foo1.c)")
    print("\(foo2.a), \(foo2.b), \(foo2.c)")
    

    输出结果

    , 0, 0.0
    abc, 1, 2.0
    

    如果想要省略外部名可以使用_关键字

    class Foo {
        var a: String
        var b: Int
        var c: Double
        init() {
            a = ""
            b = 0
            c = 0
        }
        init(_ a:String, _ b:Int, _ c: Double) {
            self.a = a
            self.b = b
            self.c = c
        }
    }
    var foo1 = Foo()
    var foo2 = Foo("abc", 1, 2)
    print("\(foo1.a), \(foo1.b), \(foo1.c)")
    print("\(foo2.a), \(foo2.b), \(foo2.c)")
    

    省略外部名会降低代码可读性

  2. 可选属性的自动初始化

    如果属性值可能为空则需要将其声明为可选型,可选型变量系统会自动将其声明为nil。该属性不需要在构造器中初始化

  3. 常量属性的初始化

    构造器中可以修改常量的的值,但是一旦初始化完成常量属性的值就不能再改变了

    class Foo {
        let a: Int
        init() {
            a = 0
        }
        init(a: Int) {
            self.a = a
        }
    }
    var foo1 = Foo()
    var foo2 = Foo(a: 10)
    print("\(foo1.a) \(foo2.a)")
    
  4. 构造器代理

    定义复杂的构造器时可以使用已定义的构造器完成部分构造工作以此增强代码复用性

    1. 值类型构造器代理

      值类型的构造器代理只能用于类型本身

      struct Foo {
          var a: Int
          var b: Int
          init() {
              a = 1
              b = 2
          }
          init(a: Int) {
              self.a = a
              b = 2
          }
          init(a:Int, b: Int) {
              self.init(a: a)
              self.b = b
          }
      }
      var foo1 = Foo(a: 10)
      var foo2 = Foo(a:20, b:10)
      
    2. 引用类型构造器代理

      和结构体构造器重载不同,类的构造器代理默认调用其直接父类的构造方法,如果想要调用当前类的其他构造方法则需要使用convenience关键字

      class SuperClass {
          var a: Int
          init() {
              a = 10
          }
      }
      class SubClass: SuperClass {
          var b:Int
          override init() {
      		b = 1
              super.init() // 调用父类构造器必须要在最下面
          }
          convenience init(b: Int) {
              self.init()
              self.b = b
          }
      }
      var a = SubClass(b:10)
      
  5. 析构器

    swift会自动释放不再需要的实例资源,但是如果自己申请了资源就需要手动清理

    只有引用类型才有析构器

    声明格式如下

    deinit {
        // release some resource
    }
    
    class Foo {
        var a: Int
        init(a:Int) {
            self.a = a
        }
        deinit {
            print("call deconstructor")
        }
    }
    var foo = Foo(a:10)
    

协议

即接口,声明一个协议使用protocol关键字

定义格式如下

protocol InterfaceName{
	...   
}

一个类可以遵从多个协议

class Class: Protocol1, Protocol2 {
    ...
}

一个类如果要同时继承类和遵从协议则需要将父类名称放在协议前面

class SubClass: SuperClass, Protocol1, Protocol2 {
    ...
}

协议的属性和方法

在协议中定义属性时还需要声明属性是只写的还是可读可写

{get} 为可读,{get set}为可写

protocol Interface {
    var a: Int {get} // 声明为可读
    var b: Int {get set} // 声明为可读可写
}

协议中定义方法不需要写方法体

protocol Interface {
	func f() -> Int
}

当一个类遵守协议时可以使用classstatic关键字声明属性和方法

protocol Bar {
    static var attr:Int {get}
    static func f() -> Void
}
class Foo {
    var attr:Int
    init() {
        attr = 1
    }
    func f() {
        print("Yes")
    }
}
var foo = Foo()

关联类型

如果在定义协议的属性时不确定属性的类型就可以使用关联类型

使用关键字associatedtype定义关联类型

protocol Interface {
    associatedtype UnkownType
    var a: UnkownType {get}
}
class Foo: Interface {
    var a: Int
    init() {
        a = 1
    }
}
var foo = Foo()

协议中的构造器

协议中可以定义构造器,遵守协议的类必须实现协议规定的构造器,协议中声明构造器不需要方法体,实例实现协议需要required关键字

protocol Foo {
    var a: Int{get}
    init(a:Int)
}
class Bar: Foo {
    var a:Int
    required init(a: Int) {
        self.a = a
    }
}
var bar = Bar(a:10)

扩展类实现协议

如果一个类声明的时候没有遵守协议,则可以通过扩展类的方式遵守协议,声明扩展类可以使用关键字extension

扩展类只能声明函数不能声明属性

protocol Foo {
    func f() -> Void
}
class Bar {
    var b = 0
}
extension Bar: Foo {
    func f() {
        print("extension")
    }
}
var bar = Bar()

协议的继承

一个协议可以继承一个或多个协议

protocol Foo {
    var a:Int{get}
}
protocol Bar {
    var b:Int{get}
}
protocol Baz: Foo, Bar {
    var c: Int{get}
}
class Qux: Baz {
    var a = 0
    var b = 0
    var c = 0
}
var quux = Qux()

泛型

泛型函数

声明函数时在函数名后加<T>T为泛型

func swap<T>(_ a: inout T, _ b: inout T) {
    let c = b
    b = a
    a = c
}
var foo = 1, bar = 2
var baz = "3", qux = "4"
swap(&foo, &bar)
swap(&baz, &qux)
print("\(foo) \(bar)")
print("\(baz) \(qux)")

输出结果

2 1
4 3

泛型类

声明类时在类名后加<T>,T为泛型

class Pair<T, U> {
    var first: T
    var second: U
    init(_ first: T, _ second: U) {
        self.first = first
        self.second = second
    }
}
var p = Pair<Int, String>(10, "xxx")

异常

定义异常

swift中用遵守协议ErrorType的类型表示异常。ErrorType是一个空协议,专用于表示异常

通常用枚举定义一组相同类型的异常

enum CreatingStudentError: Error {
    case NoName
    case InvalidAge
    case InvalidId
}

抛出异常

当异常出现时通过抛出异常处理程序

throw CreatingStudentError.InvalidId

要将函数中的异常传递给调用该函数的代码就要用throws关键字标识可能抛出异常的函数

func f() throws {
    ...
}

标识了throws的函数被称为throwing函数,throwing函数从内部抛出异常

enum MyMathError: Error {
    case InvalidDivisor
}
func f(_ a: Int, _ b: Int) throws -> (Int, Int) {
    guard b != 0 else {
        throw MyMathError.InvalidDivisor
    }
    return (a / b, a % b)
}
let (a, b) = try f(1, 0)

处理异常

通过do-catch结构语句对异常进行匹配和处理

do {
    try sth.
} catch error1 {
    handle error1
} catch error2 {
    handle error2
}
enum MyMathError: Error {
    case InvalidDivisor
}
func f(_ a: Int, _ b: Int) throws -> (Int, Int) {
    guard b != 0 else {
        throw MyMathError.InvalidDivisor
    }
    return (a / b, a % b)
}
do {
	let (a, b) = try f(1, 0)
    print((a, b))
} catch MyMathError.InvalidDivisor {
    print("Divisor is invalid")
}

输出结果

Divisor is invalid

可选型异常

如果对异常的具体情况不关心时可以将一场定义为可选型简化处理,即通过try?可以将异常转换为可选值

如果抛出了异常,那么throwing函数会返回nil

enum MyMathError: Error {
    case InvalidDivisor
}
func f(_ a: Int, _ b: Int) throws -> Int {
    guard b != 0 else {
        throw MyMathError.InvalidDivisor
    }
    return a / b
}
let a = try? f(1, 0)
print(a)

输出结果为

nil

断言无异常

使用try!使异常传递失效,如果异常抛出时则会得到一个运行时异常

enum MyMathError: Error {
    case InvalidDivisor
}
func f(_ a: Int, _ b: Int) throws -> (Int, Int) {
    guard b != 0 else {
        throw MyMathError.InvalidDivisor
    }
    return (a / b, a % b)
}
let a = try! f(1, 0)
print(a)

可选链

在可选型后面加一个!可以强制拆包该可选型的值

可选链是调用可能为空的对象的属性、方法及下标的过程,如果对象不为空则调用成功,否则返回nil

多个这样的调用可以形成一个可选链,链上任何一个结点调用nil都会导致整条链调用失败

一个可选链是否调用成功可以通过返回值是否为nil来判断

class Foo {
    var a = 0
}
class Bar {
    var foo: Foo?
}
let bar = Bar()
if let tmp = bar.foo?.a {
    print(tmp)
} else {
    print("foo is nil")
}

可选链的使用

应用可选链可以实现向下访问多层属性、方法及下标

说白了就链式编程,用法和kotlin一致


访问控制

模块和源文件是swift访问控制当中的基本单位

模块是以独立单元构建和发布的框架或应用, 在swift中可以通过import引用一个模块

swift有三个访问级别public internal private

  1. public表示可以访问模块中的任何实体
  2. internal表示只能访问当前模块中任何internal实体,不能访问其他模块中的实体
  3. private表示只能在当前源文件中被使用的实体,也成称为私有实体,private可以用来隐藏某些功能的实现细节

定义一个实体的访问级别需要在实体的定义前加上访问级别关键字并用()标注读写权限

class Foo{
    private(set) var a: Int // 表示定义了一个整型实体a,其写权限为private(除了类本身的方法以外无法修改属性的值)
}

类型操作符

swift提供了两种类型操作符isas

  1. is

    通过is可以检查值的类型

    let a = 0
    if a is Int {
        print("a is an integer")
    }
    
  2. as

    通过as可以改变值的类型

    类型操作符as可以把一个实体向下转换成他的子类型,转换过程可能成功也可能失败,对于这种情况,类型操作符有两种形式as?as!

    as?返回一个可选值,as!则强制向下转型并强制拆包

    如果转型失败as?返回一个nilas!则会触发运行时异常

    对于AnyObject实例,as!可以将其类型断言

    func f(arg: Any) -> Any {
        return arg
    }
    var a = f(arg: 1)
    a = a as! Int
    if a is Int {
        print("Yes")
    }
    

    输出结果

    Yes
    

扩展

向一个已有的类型添加新的功能

在swift中扩展包括:扩展计算型属性、扩展构造器、扩展方法、扩展下标

声明格式如下

extension OldType {
    ...
}

扩展计算型属性

extension Int {
    var kg: Int {
        return self * 1000
    }
    var ton: Int {
        return self * 1000_000
    }
}
print(1.kg) // 打印1000

扩展构造器

class Foo {
    var a: Int
    init() {
        a = 0
    }
}
extension Foo {
    convenience init(a: Int) {
        self.a = a
    }
}
var foo = Foo(a:1)
print(foo.a)

扩展方法

struct Foo {
    var a: Int
    init() {
        a = 0
    }
}
extension Foo {
    func myprint() {
        print(a)
    }
}
var foo = Foo()
print(foo.myprint())

扩展下标

extension String {
  public subscript(bounds: CountableRange<Int>) -> String {
    let s = self[index(startIndex, offsetBy: bounds.lowerBound) ..< index(startIndex, offsetBy: bounds.upperBound)]
    return String(s)
  }
  
  public subscript(bounds: CountableClosedRange<Int>) -> String {
    let s = self[index(startIndex, offsetBy: bounds.lowerBound) ... index(startIndex, offsetBy: bounds.upperBound)]
    return String(s)
  }
  
  public subscript(index: Int) -> String {
    let character = self[self.index(startIndex, offsetBy: index)]
    return String(character)
  }
}
print("String"[2])
print("String"[0..<3])
print("String"[0...3])

输出结果

r
Str
Stri

内存管理

swift使用自动引用计数(ARC)的机制解决应用程序的内存管理问题。

当创建一个类时,系统会分配一片内存储存实例的相关数据,当该实例不再被使用时ARC会自动释放这片内存。

当该实例被使用时,ARC会跟踪并计算该实例的引用数,当引用数不为0时该实例不会被销毁。

属性、常量及变量对实例的引用被称为强引用

实例的引用被赋值为nil会断开实例的强引用

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) 开始初始化")
    }
    deinit {
        print("\(name) 被析构")
    }
}

// 值会被自动初始化为nil,目前还不会引用到Person类的实例
var reference1: Person?
var reference2: Person?
var reference3: Person?

// 创建Person类的新实例
reference1 = Person(name: "Runoob")


//赋值给其他两个变量,该实例又会多出两个强引用
reference2 = reference1
reference3 = reference1

//断开第一个强引用
reference1 = nil
//断开第二个强引用
reference2 = nil
//断开第三个强引用,并调用析构函数
reference3 = nil

输出结果

Runoob 开始初始化
Runoob 被析构

强引用循环

通过判断一个实例引用次数是否为0决定实例是否会被销毁。然而有时会出现实例不在会被使用但是引用次数不为0的情况,这种情况会导致内存泄漏,这种情况被叫做强引用循环

class Foo {
    var qux: Int
    var bar: Bar?
    init(qux: Int) {
        self.qux = qux
    }
    deinit {
        print("deinit")
    }
}
class Bar {
    var qux: Int
    var foo: Foo?
    init(qux: Int) {
        self.qux = qux
    }
    deinit {
        print("deinit")
    }
}

// 实例被创建并赋值为nil
var foo: Foo?
var bar: Bar?

// 赋值
foo = Foo(qux: 1)
bar = Bar(qux: 1)

// 创建强引用循环
foo!.bar = bar
bar!.foo = foo

// 尝试断开强引用,但是析构器并未被调用
foo = nil
bar = nil

Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:

  • 弱引用

    关键字为weak

  • 无主引用

    关键字为unowned

弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。

对于生命周期中会变为nil的实例使用弱引用。相反的,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用

对上面的例子稍作修改

class Foo {
    var qux: Int
    weak var bar: Bar?
    ...
class Bar {
    var qux: Int
    weak var foo: Foo?
    ...

输出结果

deinit
deinit

闭包中的强引用循环

由于类和闭包都是引用类型,当将一个闭包赋值给一个类实例的某个属性时,如果闭包体中引用了该实例,那么就会导致闭包捕获自己从而产生强引用循环

class Foo {
    var qux: Int
    lazy var f: () -> String = {
        return "\(self.qux)"
    }
    init(qux: Int) {
        self.qux = qux
    }
    deinit {
        print("deinit")
    }
}
var foo: Foo? = Foo(qux: 1)
print(foo!.f())
foo = nil

输出结果

1

对于闭包的强引用循环也可以使用弱引用或无主引用解决

对上面例子稍作修改

lazy var f: () -> String = {
    [unowned self] in // 在闭包体前加上参数列表,对自身的捕获使用弱引用或无主引用关键字标识
    return "\(self.qux)"
}

输出结果

1
deinit

高级运算符

溢出运算符

在swift中并不会主动进行溢出运算,如果发生数据溢出则会报错,想要主动进行可溢出的运算则要使用溢出运算符

溢出运算符有三种&+ &- &*,分别对应溢出加法,溢出减法,溢出乘法

print(UInt8.max &+ 1)
print(UInt8.min &- 1)
print(UInt8.max &* 10)

输出结果

0
255
246

运算符重载

函数名即为运算符,重载单目运算符需要在函数声明前添加prefix关键字

struct Point {
    var x = 0
    var y = 0
    static func -(a: Point, b: Point)-> Point { // 双目运算
        return Point(x: a.x - b.x, y: a.y - b.y)
    }
    static prefix func -(point: Point)-> Point { // 单目运算
        return Point(x: -point.x, y: -point.y)
    }
}
var a = Point(x:1, y:0)
var b = Point(x:0, y:1)
let c = a - b

本文作者:七つ一旋桜

本文链接:https://www.cnblogs.com/poifa/p/15302711.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   七つ一旋桜  阅读(46)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起