Swift基础学习<二>

201 阅读9分钟

讨论下Swift类与OC类的不同 Swift类特有:

  • 不必显式的使用指针,直接使用let、var定义常量变量
  • 使用类的时候不需要import引用,Swift自动提供外部接口
  • 类有多种构造器 默认构造器、指定构造器、便利构造器
  • 类的析构函数deinit 类似于OC的dealloc
  • 类属性有存储属性、计算属性、类属性,类属性属于类,实例所共享
  • 属性set get方法,willset didset属性监听器写在一块代码内
  • 访问修饰词 open、public、internal、private、fileprivate
  • 可以不继承于NSObject
  • 没有实例变量
  • extension更强大
  • protocol可以定义(可读可写可读写)属性,oc是属性的set/get方法

相同点:

  • 都是引用类型,内存管理使用引用计数
  • 都有继承机制

属性

1、存储属性

//官网示例
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 该区间表示整数 0,1,2
rangeOfThreeItems.firstValue = 6
// 该区间现在表示整数 6,7,8

2、懒加载属性,前边加lazy关键字

//官方示例
class DataImporter {
    /*
    DataImporter 是一个负责将外部文件中的数据导入的类。
    这个类的初始化会消耗不少时间。
    */
    var fileName = "data.txt"
    // 这里会提供数据导入功能
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // 这里会提供数据管理功能
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建

print(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了
// 输出“data.txt”

3、计算属性

//官网示例
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)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 打印“square.origin is now at (10.0, 10.0)”

4、只读计算属性 只有get没有set

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth //return可忽略,隐式返回
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 打印“the volume of fourByFiveByTwo is 40.0”

5、属性观察器 willSet didSet 可以在以下情况添加属性观察器:

  • 自定义的存储属性
  • 继承的存储属性
  • 继承的计算属性
  • willSet 在新值被设置之前调用
  • didSet 在新值被设置后调用
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("将 totalSteps 的值设置为 \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("增加了 \(totalSteps - oldValue) 步")
            }
        }
    }
}

6、包装属性 暂不知使用场景

7、类型属性,类型属性为所有实例所共享,且只有一份 类型属性使用static、class关键字

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

方法

简单说下

  • 使用func关键字
  • 不必在方法内显式使用self,默认指当前实例
  • 枚举和结构体 要修改属性时,方法前需要加 mutating 关键字
  • 类方法使用static、class标识,只能使用类来调用
struct LevelTracker {
    static var highestUnlockedLevel = 1 //类属性
    var currentLevel = 1

    static func unlock(_ level: Int) { //类方法
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    @discardableResult //忽略返回值警告
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

class SomeClass {
    class func someTypeMethod() { //类方法 class关键字
        // 在这里实现类型方法
    }
}
SomeClass.someTypeMethod()//类方法调用

继承

  • Swift 不继承于其他类,该类就是基类
  • 重写使用override关键字
  • 防止重写添加final关键字,类前加final,子类禁止继承
  • 使用super.someMethod()调用父类方法,使用super.someProperty来访问父类someProperty,使用
//定义类
class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // 什么也不做——因为车辆不一定会有噪音
    }
}

//重写方法
class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}

//重写属性
class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

//重写属性观察器
class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

//防重写
final func
final property
final class func
final subscript //下标

构造过程

  • 构造过程是类实例在能使用前的初始化过程。
  • 构造过程必须使存储属性初始化
  • Swift构造器没有返回值,OC返回类实例自己
  • 结构体默认有逐一构造器,类没有
  • 可选属性默认为nil或0,不用再构造器中初始化
  • 指定构造器必须调用它直接父类的指定构造器
  • 便利构造器必须调用同级的其他构造器
  • 便利构造器必须调用指定构造器(这两句话是不是一个意思)
  • 指定构造器必须向上代理
  • 便利构造器横向代理

Swift类初始化分为两个阶段,

阶段1:类中每个存储型属性赋值一个初始值,当每个存储型属性被赋值后第二阶段开始
阶段2:给每个类一次机会,在类能正常使用前,可以自定义存储属性的值

Swift编译器将执行4步检查,以确保构造安全完成:

检查1:指定构造器必须能保证类的所有存储属性都必须初始化完成,才可以向上走继承链
检查2:指定构造器必须在为存储型属性赋值新值之前向上调用了父类构造器,否则新值被覆盖
检查3:便利构造器必须在为属性赋值前,调用了其他指定构造器,否则属性值被覆盖
检查4:构造器在第一阶段完成前,不能使用self、引用属性和方法

以下是官方文档描述:

  • 类的某个指定构造器或便利构造器被调用。
  • 完成类的新实例内存的分配,但此时内存还没有被初始化。
  • 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
  • 指定构造器切换到父类的构造器,对其存储属性完成相同的任务。
  • 这个过程沿着类的继承链一直往上执行,直到到达继承链的最顶部。
  • 当到达了继承链最顶部,而且继承链的最后一个类已确保所有的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。

构造器部分内容比较多,详情请移步构造器

1、逐一构造器

struct Color {
    let red, green, blue: Double
}

//定义
let c = Color(red: 100.0, green: 100.0, blue: 100.0)

2、默认构造器
如果类、结构体的属性都提供了默认值,又没有提供自定义构造器,那么会默认有一个构造器

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

3、指定构造器

init(paras) {

}

4、遍历构造器,前边加convenience关键字

convenience init(parameters) {
    statements
}

5、构造器继承与重写

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
}

//继承
class Bicycle: Vehicle {
    override init() {  // 重写
        super.init() //先调用super,如果类Bicycle有一个自己的存储属性,则该存储属性必须初始化后才能调用super
        numberOfWheels = 2
    }
}

6、构造器自动继承

  • 子类如果没有定义构造器,那么默认继承所有父类指定构造器
  • 如果子类提供了所有的指定构造器,那么自动继承父类所有便利构造器

7、可失败构造器

class Product {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

class CartItem: Product {
    let quantity: Int
    init?(name: String, quantity: Int) {
        if quantity < 1 { return nil }
        self.quantity = quantity
        super.init(name: name)
    }
}

8、必要构造器
1、类的构造器前加 required 关键字,表明子类必须实现该构造器
2、子类实现必要构造器时,构造器前也必须加 required关键字
3、子类重写父类必要构造器时,不需要加 override 关键字

class SomeClass {
    required init() {
        // 构造器的实现代码
    }
}

//子类重写
class SomeSubclass: SomeClass {
    required init() { //不需要添加 override
        // 构造器的实现代码
    }
}

析构

  • 类似于OC中的dealloc,当对象的引用计数为0时,对象被回收,自动调用deinit方法
  • 不可主动调用 deinit 方法
  • 释放额外内存资源
deinit {
    // 执行析构过程
}

可选链

  • 可选链调用是当前值可能为nil的调用
  • 可选链中有一个值为nil,则返回nil,调用结束
  • 使用可选链式调用替换强制解包是不错的选择,强解包如果为空会崩溃
//多层可选调用+可选绑定
if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// 打印“Unable to retrieve the address.”

类型转换

  • is 类型检查符,来检查一个类是不是出于特定类,若属于则返回true,否则返回false
  • as?,as! 类型转换符,强解包慎用,as?转换失败会返回nil
  • Any表示任意类型,包括函数类型,AnyObject表示任意对象类型
for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}

//is 类型检查符
for item in library {
    if item is Movie {
		print("movie")
    } else if item is Song {
        print("song")
    }
}

扩展

Swift中扩展可以:

  • 添加计算属性(实例属性&类属性),不可添加存储属性
  • 定义实例方法和类方法
  • 提供新的构造器
  • 定义下标
  • 使用和定义新的嵌套类型
  • 协议实现
//扩展语法
extension SomeType {
  // 在这里给 SomeType 添加新的功能
}

//遵循并实现协议
extension ViewController: UITableViewDelegate {
    
}

//定义计算属性
extension ViewController: UITableViewDelegate {
    var fullName: String {
        return "li" + lastName
    }
}

//定义构造器
extension ViewController: UITableViewDelegate {
    var fullName: String {
        return "li" + lastName
    }
    
    convenience init(_ name: String) {
        self.init()
        lastName = name
    }
}

//定义方法、下标、嵌套类型等等

协议

协议的语法和定义类、结构体类似

protocol SomeProtocol {
    // 这里是协议的定义部分
}

遵循协议

xxx: someProtocol,子类遵循协议时,someProtocol放在父类后边,多个协议使用,隔开

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 这里是类的定义部分
}

协议提供属性

  • 协议内部可以提供属性,遵循此协议的类或结构体实现该属性,不区分存储属性还是计算属性
  • 属性可读可写,使用set get标识
protocol SomeProtocol {
    var mustBeSettable: Int { get set } //可读可写
    var doesNotNeedToBeSettable: Int { get } //只读
    static var someTypeProperty: Int { get set } //类型属性
}

协议内-方法

类方法和实例方法,不用写大括号{}

protocol SomeProtocol {
    static func someTypeMethod() //类方法
    func random() -> Double //实例方法
}

mutating关键字

只用于值类型(结构体、枚举)

protocol Togglable {
    mutating func toggle()
}

协议内-构造器

  • 不管是作为指定构造器还是便利构造器,类实现构造器时必须带有required关键字
protocol SomeProtocol {
    init(someParameter: Int)
}

//
class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // 这里是构造器的实现部分
    }
}

协议作为类型

  • 协议可以作为一种类型来使用,称作 存在类型
  • 作为函数、方法或构造器中的参数类型或返回值类型
  • 作为常量、变量或属性的类型
  • 作为数组、字典或其他容器中的元素类型

代理

不多说

扩展里遵循协议

  • 这一点写起来代码看着很整洁,当类遵循多个协议时,可以为每个代理写一个类扩展
extension ViewController : UITableViewDataSource { 
}

extension ViewController: UITableViewDelegate {
}

extension ViewController: UITabBarDelegate {
}

协议继承

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 这里是协议的定义部分
}

类专属协议

  • 类专属协议要加上AnyObject关键字,表明只能被类遵循
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
    // 这里是类专属协议的定义部分
}

协议组合

  • 函数传参时,可以判定是否参数遵循多个协议,使用&符号
func xxx(celebrator: protocol1 & protocol2) {

}

协议检查

  • 使用关键字is as? as!
  • is 检查是否遵循某协议,返回true or false
  • as? 当实例遵循协议时,返回协议的可选值,or返回nil

协议-可选项

暂无

协议扩展

1、协议可扩展,遵循协议的实例,直接使用扩展的新属性或方法

protocol RandomNumberGenerator {
    func random() -> Double
}

//为协议 RandomNumberGenerator 扩展方法
extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

2、扩展可以提供协议默认实现,实例没有该,实现默认被替代

protocol XXX {
    var desc: String { get }
}

extension XXX {
    var desc: String {//默认实现
        return "default"
    }
}

协议扩展添加条件

//官方实例,意思是所有的collection内的element都遵循了Equatable协议,才返回true,否则返回false
extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        for element in self {
            if element != self.first {
                return false
            }
        }
        return true
    }
}

强大的泛型

  • Swift提供泛型特性,OC没有
  • 泛型代表了一种代码逻辑的抽象
  • 定义泛型函数时,紧随函数后边,T只是一个占位,不具有真实类型或值,当然也可以使用U/P等其他

泛型函数

例子:
我们要实现一个替换两个变量的方法,因为变量有好多类型,不能一一定义函数,可用泛型抽象出一个方法,具体使用时再传入真正的变量类型
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}

//泛型函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

//调用,还是跟普通函数一样
var a = 8
var b = 7
swapTwoValues(&a, &b)

泛型类型

官方示例:
意思是:定义一个Stack结构体,内部的变量类型不确定,使用<Element>占位,当真正使用Stack结构体时,才确认类型
struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

//使用,结构体名后边紧跟<String>,表示真正的类型为String
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// 栈中现在有 4 个字符串

泛型扩展

泛型扩展时,占位符可直接使用

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

类型约束

官方示例:
意思是:findIndex函数真正的参数必须要遵循Equatable协议
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

关联类型

。。。