Swift巩固

可选项绑定(Optional Binding)

可以使用可选项绑定来判断可选项是否包含值 p如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false

if let number = Int("123") { 
print("字符串转换整数成功:\(number)") // number是强制解包之后的Int值 // number作用域仅限于这个大括号 } else { print("字符串转换整数失败") }

等价写法

f let first = Int("4") {
    if let second = Int("42") {
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
} } 
} 
// 4 < 42 < 100 

if let first = Int("4"),
    let second = Int("42"),
    first < second && second < 100 {
    print("\(second) < \(second) < 100")
} 
// 4 < 42 < 100

空合并运算符 ??(Nil-Coalescing Operator)

let a: Int? = 1

let b: Int? = 2

let c = a ?? b // c是Int? , Optional(1)

 

 

let a: Int? = 1

let b: Int = 2

let c = a ?? b // c是Int , 1

 

let a: Int? = nil

let b: Int = 2

let c = a ?? b // c是Int , 2

 

let a: Int? = nil

let b: Int? = 2

let c = a ?? b // c是Int? , Optional(2)

 

let a: Int? = nil

let b: Int = 2// 如果不使用??运算符 let c: Int

if let tmp = a {

    c = tmp

} else {

    c=b

}

 

let a: Int? = nil

let b: Int? = nil

let c = a ?? b // c是Int? , nil

多个 ?? 一起使用

let a: Int? = 1

let b: Int? = 2

let c = a ?? b ?? 3 // c是Int , 1

guard语句

guard语句的条件为false时,就会执行大括号里面的代码

guard语句的条件为true时,就会跳过guard语句

guard语句特别适合用来“提前退出”

当使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能在外层作用域中使用

func login(_ info: [String : String]) {

guard let username = info["username"] else {
print("请输入用户名")
return
}
guard let password = info["password"] else {
print("请输入密码")
return
}
// if username ....
// if password ....
print("用户名:\(username)", "密码:\(password)", "登陆ing")
} 

guard 条件 else {
// do something.... 

退出当前作用域 

// returnbreakcontinuethrow error }

隐式解包(Implicitly Unwrapped Optional)

在某些情况下,可选项一旦被设定值之后,就会一直拥有值

在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值

可以在类型后面加个感叹号 ! 定义一个隐式解包的可选项

let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
    print(num1 + 6) // 16
}
if let num3 = num1 {
    print(num3)
} 

let num1: Int! = nil
// Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
let num2: Int = num1

 

结构体

Swift 标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分

比如BoolIntDouble StringArrayDictionary等常见类型都是结构体

所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法)

类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器

如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器

成员的初始化是在这个初始化器中完成的

class Point {
    var x: Int = 10
    var y: Int = 20
}
let p1 = Point()

class Point {
    var x: Int
    var y: Int
init() { x = 10 
y = 20 } 
}
let p1 = Point()

//上面2段代码是完全等效的 

结构体与类的本质区别

结构体是值类型(枚举也是值类型),类是引用类型(指针类型)

值类型

值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份

类似于对文件进行copy、paste操作,产生了全新的文件副本。属于深拷贝(deep copy)

引用类型

引用赋值给var、let或者给函数传参,是将内存地址拷贝一份 p类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝(shallow copy)

Swift中,创建类的实例对象,要向堆空间申请内存

 

枚举、结构体、类都可以定义方法

一般把定义在枚举、结构体、类内部的函数,叫做方法

方法占用对象的内存么

不占用, 

方法、函数都存放在代码段

 

闭包(Closure)

闭包是可以在你的代码中被传递和引用的功能性独立代码块

全局函数是一个有名字但不会捕获任何值的闭包;

内嵌函数是一个有名字且能从其上层函数捕获值的闭包;

 

 

可以把闭包想象成是一个类的实例对象

内存在堆空间

捕获的局部变量\常量就是对象的成员(存储属性)

组成闭包的函数就是类内部定义的方法

 

尾随闭包

如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性

尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式

逃逸闭包

当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它是在函数返回之后调用的。

当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。

 

属性

存储属性(Stored Property)

类似于成员变量这个概念

存储在实例的内存中

结构体、类可以定义存储属性 ü 枚举不可

在创建类 或 结构体的实例时,必须为所有的存储属性设置一个合适的初始值 

可以在初始化器里为存储属性设置一个初始值

可以分配一个默认的属性值作为属性定义的一部分

 

计算属性(Computed Property)

本质就是方法(函数)

不占用实例的内存

枚举、结构体、类都可以定义计算属性

set传入的新值默认叫做newValue,也可以自定义

只读计算属性:只有get,没有set

定义计算属性只能用var,不能用let

let代表常量:值是一成不变的

计算属性的值是可能发生变化的(即使是只读计算属性)

枚举原始值rawValue的本质是:只读计算属性

单例模式

public class FileManager {
    public static let shared = FileManager()
    private init() { }
} 

public class FileManager {
    public static let shared = {
// ....
// ....r
eturn FileManager() 
}() 
    private init() { }
}

初始化器

类、结构体、枚举都可以定义初始化器

类有2种初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer)

每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器

默认初始化器总是类的指定初始化器

类偏向于少量指定初始化器,一个类通常只有一个指定初始化器

 

初始化器的相互调用规则

指定初始化器必须从它的直系父类调用指定初始化器

便捷初始化器必须从相同的类里调用另一个初始化器

便捷初始化器最终必须调用一个指定初始化器

// 指定初始化器
 init(parameters) { 
statements } 
// 便捷初始化器

convenience init(parameters) { 
statements }

重写

当重写父类的指定初始化器时,必须加上override(即使子类的实现是便捷初始化器)

如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上override

因为父类的便捷初始化器永远不会通过子类直接调用,因此,严格来说,子类无法重写父类的便捷初始化器

1、如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器

如果子类提供了父类所有指定初始化器的实现(要么通过方式1继承,要么重写)

required

用required修饰指定初始化器,表明其所有子类都必须实现该初始化器(通过继承或者重写实现) 

如果子类重写了required初始化器,也必须加上required,不用加override

属性观察器

父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器

 

反初始化器(deinit)

einit叫做反初始化器,类似于C++的析构函数、OC中的dealloc方法

当类的实例对象被释放内存时,就会调用实例对象的deinit方法 

class Person {
    deinit {
print("Person对象销毁了") } 
} 

deinit不接受任何参数,不能写小括号,不能自行调用

父类的deinit能被子类继承

子类的deinit实现执行完毕后会调用父类的deinit

 

协议(Protocol)

协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)

协议中定义属性时必须用var关键字

实现协议时的属性权限要不小于协议中定义的属性权限

 

协议的继承

一个协议可以继承其他协议

protocol Runnable {
    func run()
} 
protocol Livable : Runnable {
    func breath()
} 
class Person : Livable {
    func breath() {}
    func run() {}
}

CaseIterable

让枚举遵守CaseIterable协议,可以实现遍历枚举值

num Season : CaseIterable {
    case spring, summer, autumn, winter
}
let seasons = Season.allCases print(seasons.count) // 4 for season in seasons { 
    print(season)
} // spring summer autumn winter

isas?as!as

is用来判断是否为某种类型,as用来做强制类型转换

 

自定义错误

Swift中可以通过Error协议自定义运行时的错误信息

enum SomeError : Error {

case illegalArg(String) 
case outOfBounds(Int, Int) 
case outOfMemory 
} 

函数内部通过throw抛出自定义Error,可能会抛出Error的函数必须加上throws声明

unc divide(_ num1: Int, _ num2: Int) throws -> Int {
 if num2 == 0 { 
throw SomeError.illegalArg("0不能作为除数")
 } 
    return num1 / num2
}

需要使用try调用可能会抛出Error的函数

var result = try divide(20, 10)

do-catch

可以使用do-catch捕捉Error

func test() {
    print("1")
    do {
        print("2")
        print(try divide(20, 0))
print("3") 
        }  catch let SomeError.illegalArg(msg) {
 
print("参数异常:", msg) 

        }  catch let SomeError.outOfBounds(size, index) { 

print("下标越界:", "size=\(size)", "index=\(index)") 

        }  catch SomeError.outOfMemory { 

print("内存溢出") } catch {
 
print("其他错误") } 
print("4") } 

try?try!

可以使用try?、try!调用可能会抛出Error的函数,这样就不用去处理Error

assert(断言)

很多编程语言都有断言机制:不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断 n 默认情况下,Swift的断言只会在Debug模式下生效,Release模式下会忽略

func divide(_ v1: Int, _ v2: Int) -> Int { 
assert(v2 != 0, "除数不能为0")

return v1 / v2 
}
print(divide(20, 0))

泛型(Generics)

泛型可以将类型参数化,提高代码复用率,减少代码量

泛型函数赋值给变量

func swapValues<T>(_ a: inout T, _ b: inout T) {
 (a, b) = (b, a) 
}

 

继承(Inheritance)

值类型(枚举、结构体)不支持继承,只有类支持继承

没有父类的类,称为:基类

Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类

子类可以重写父类的下标、方法、属性,重写必须加上override关键字

重写属性

子类可以将父类的属性(存储、计算)重写为计算属性

子类不可以将父类属性重写为存储属性

只能重写var属性,不能重写let属性

重写时,属性名、类型要一致 

子类重写后的属性权限 不能小于 父类属性的权限

如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的

如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的

重写类型属性

class修饰的计算类型属性,可以被子类重写

static修饰的类型属性(存储、计算),不可以被子类重写

可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器

final

被final修饰的方法、下标、属性,禁止被重写

被final修饰的类,禁止被继承

访问控制(Access Control)

在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列, 实体指被访问级别修饰的内容)

open:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上) 

public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写

internal:只允许在定义实体的模块中访问,不允许在其他模块中访问

fileprivate:只允许在定义实体的源文件中访问 

private:只允许在定义实体的封闭声明中访问 

绝大部分实体默认都是internal 级别

 

Swift调用OC

新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h

{targetName}-Bridging-Header.h 文件中#import OC需要暴露给Swift的内容

OC调用Swift

Xcode已经默认生成一个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h

Swift暴露给OC的类最终继承自NSObject

使用@objc修饰需要暴露给OC的成员

使用@objcMembers修饰类

Xcode会根据Swift代码生成对应的OC声明,写入{targetName}-Swift.h 文件

String

NSString

String

NSMutableString

Array

NSArray

Array

NSMutableArray

Dictionary

NSDictionary

Dictionary

NSMutableDictionary

Set

NSSet

Set

NSMutableSet

KVC\KVO

Swift 支持 KVC \ KVO 的条件

属性所在的类、监听器最终继承自 NSObject

@objc dynamic 修饰对应的属性

class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observer: Observer = Observer()
    override init() {
    super.init() 
    self.addObserver(observer, 
    forKeyPath: "age", options: .new, context: nil) 
    }

    deinit { 
    self.removeObserver(observer, forKeyPath: "age") 
    } 
} 

var p = Person()

// observeValue Optional(20) 
p.age = 20// observeValue Optional(25) 
p.setValue(25, forKey: "age") 

class Observer: NSObject {

override func observeValue(forKeyPath keyPath: String?, 
                               of object: Any?,
                               change: [NSKeyValueChangeKey : Any]?,
                               context: UnsafeMutableRawPointer?) {
print("observeValue", change?[.newKey] as Any) } 
}

 

posted @ 2021-03-06 09:54  甘林梦  阅读(115)  评论(0编辑  收藏  举报