• 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

    KVCKVO

    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) } 
    }
  • 相关阅读:
    .Net中多线程类的使用和总结
    单例模式完整解析
    避免构造/析构函数调用虚函数(转)
    正则表达式
    序列化与反序列化
    数组的使用,指针的使用
    jmeter单接口和多接口测试
    HTML5 input placeholder 颜色修改
    h5动画效果总结
    8月份月度反思--做一个快乐的程序员
  • 原文地址:https://www.cnblogs.com/ljcgood66/p/14173663.html
Copyright © 2020-2023  润新知