Swift知识点(五)

21. 面向协议编程、响应式编程

问:给出一个字符串,计算出里面数字的个数
swift 复制代码
var str = "1234test1234"

func numberCount(_ str: String) -> Int {
    var count = 0
    //方法一
    for c in str {
        if ("0"..."9").contains(c){
            count = count + 1
        }
    }
    return count
    
    //方法二
    for c in str where ("0"..."9").contains(c) {
        count = count + 1
    }
    return count
    
    //方法三
    let tempStr = str.filter { c in
        return ("0"..."9").contains(c)
    }
    return tempStr.count
}

let result = numberCount(str)
print(result)//8

以上几种方法,都是自定义函数方法,哪用去哪写

既然这个功能是全局性的,可以写到扩展里面:

swift 复制代码
ar str = "1234test1234"
let result = str.numberCount
print(result)

//扩展
extension String {
	//计算属性
    var numberCount: Int {
        var count = 0
        for c in str where ("0"..."9").contains(c) {
            count = count + 1
        }
        return count
    }
}

这种方法,属性名没有特殊标识,有可能String里面有一个numberCount,会造成覆盖

因此,最好命名为xx_numberCount,这种好改

如果可以str.xx.numberCount这样调用更好,看着像是私有库里面的方法

swift 复制代码
var str = "1234test1234"

struct YZ {
    var string: String
    init(_ string: String) {
        self.string = string
    }
    
    var numberCount: Int {
        var count = 0
        for c in str where ("0"..."9").contains(c) {
            count = count + 1
        }
        return count
    }
}

extension String {
    var yz: YZ {
        return YZ(self)
    }
}

let result = str.yz.numberCount
print(result)

这种做法好是好,但还有缺点,只有String的扩展,如果以后是数组或者其他类型求里面的数字个数,还要写其他的。因此,可以用泛型去做:

swift 复制代码
var str = "1234test1234"

//YZ类型,是一个Base泛型
struct YZ<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

extension String {
    var yz: YZ<String> {
        return YZ(self)
    }
    
    static var yz: YZ<String>.Type {
        //返回的是类类型
        return YZ<String>.self
    }
}

extension YZ where Base == String {
    var numberCount: Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count = count + 1
        }
        return count
    }
    
    //类方法
    static func test() {
        print("test")
    }
}

class Person {}
extension Person {
    var yz: YZ<Person> {
        return YZ(self)
    }
}

extension YZ where Base: Person {
    //对象方法
    func run(){
        print("run")
    }
}

let result = str.yz.numberCount
print(result)//8

let person = Person()
person.yz.run()//run

//类型扩展
String.yz.test()//test

上述,使用泛型,做到不需要一个类型写一种样式

但,多个类型有公共特点,可以抽出来,但不适用抽父类,因为,有结构体、有类,Dog跟Person也没有关系。因此,这种情况下,可以使用协议,即:面向协议编程

swift 复制代码
var str = "1234test1234"
//步骤一:
//YZ类型,是一个Base泛型
struct YZ<Base> {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

//步骤二:
//利用协议扩展前缀属性
//协议定义
protocol YZCompatible {}
//协议的扩展(里面可以写实现)
extension YZCompatible {
    var yz: YZ<Self> {
    	set {}
    	get { YZ(self) }
    }
    static var yz: YZ<Self>.Type {
    	//写set,get是为了下面的函数可以加mutating
    	set {}
    	get { YZ<Self>.self }
    }
}

//步骤三:
//给字符串扩展功能,让String拥有前缀属性
extension String: YZCompatible {}
//给 String.yz 类对象 或 String().yz 实例对象 前缀扩展功能
extension YZ where Base == String {
    var numberCount: Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count = count + 1
        }
        return count
    }
    
    //类方法
    static func test() {
        print("test")
    }
}

class Person {}
extension Person: YZCompatible {}

extension YZ where Base: Person {
    //对象方法
    func run(){
        print("run")
    }
}

class Dog {}
extension Dog: YZCompatible {}

let result = str.yz.numberCount
print(result)//8

let person = Person()
person.yz.run()//run

//类型扩展
String.yz.test()//test

响应式编程(Reactive Programing)

响应式编程简称LRP

一般与函数式融合在一起,所以也会叫做:函数响应式编程(Functional Reactive Programming,简称FRP)

比较著名、成熟的响应式框架:

  • ReactiveCocoa

    简称RAC,有OC、Swift版本

  • ReactiveX

    简称Rx,有众多编程语言的版本,比如:RxJava、RxKotlin、RxJS、RxPHP、RxGo、RxSwift

RxSwift

Observable:负责发送事件(Event)
Observer:负责订阅Observable,监听Observable发送的事件(Event)

Event有3种

next:携带具体数据

error:携带错误信息,表面Observable终止,不会再发出事件

completed:表明Observable终止,不会再发出事件

一个RxSwift的简单应用:

发送事件、订阅事件

swift 复制代码
import UIKit
//导入RxSwift
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Observable 负责发送事件
        //Observable创建的时候,Observable接收泛型,因此要具体指出类型,假设类型是Int
        //发送的是Int,到时候接收也是Int
        let observable = Observable<Int>.create { observer in
            
            //发送消息
            observer.onNext(11)
            observer.onCompleted()//到这,后面的22和33不再执行。换成error也一样
            observer.onNext(22)
            observer.onNext(33)
            observer.onCompleted()
            
            return Disposables.create()
        }
        
        //订阅Observer
        observable.subscribe { event in
            switch event {
            case .next(let element)://订阅的内容
                print("next", element)//element是Int类型,因为发送的是Int类型
            case .error(let error)://订阅发生错误
                print("eroor", error)
            case .completed://订阅完成
                print("completed")
            }
        }
    }
    
}
其他几种发送事件的方式:
swift 复制代码
//相当于next
let observable = Observable.just(1)//1

//可以多次打印,相当于next打印3次,每次打印其中一个
let observable = Observable.of(1, 2, 3)//1, 2, 3

//与上面的结果一样
let observable = Observable.from([1, 2, 3])

//如果使用just,里面放数组,相当于订阅一个数组,而不是订阅多次
let observable = Observable.just([1, 2, 3])

还可以发送定时器事件:

swift 复制代码
//dueTime x时间后开始
//period x时间执行一次
//scheduler 线程
let observable = Observable<Int>.timer(.seconds(1), period: .seconds(3), scheduler: MainScheduler.instance)
        
//因为订阅类型是Int,传给text是String,因此不行
//这里借助map,将Int值,转换为了String
observable.map { a in
	"text内容:\(a)"
}.bind(to: label.rx.text)

Disposable

每当Observable被订阅时,都会返回一个Disposable实例,当调用Disposable的dispose,就相当于取消订阅

swift 复制代码
//订阅Observer
let disposable = observable.subscribe { event in
    switch event {
    case .next(let element):
        print("next", element)
        
        label.text = String(element)
    case .error(let error):
        print("eroor", error)
    case .completed:
        print("completed")
    }
}

//某一个地方,调用dispose(),则后续不再订阅
disposable.dispose()

而下面这种,订阅后紧接着就调用dispose(),代码只订阅一次

swift 复制代码
//订阅Observer
let disposable = observable.subscribe { event in
    switch event {
    case .next(let element):
        print("next", element)
        
        label.text = String(element)
    case .error(let error):
        print("eroor", error)
    case .completed:
        print("completed")
    }
}.dispose()

利用Binder

swift 复制代码
let binder = Binder<String>(label) { label, value in
	label.text = value
}
        
Observable.just("2222").subscribe(binder).dispose()
//或者
Observable.just("2222").bind(to: binder).dispose()

22. 标准库源码分析、项目实战

Swift源码地址

下载过源码后,可以新建一个空的工程,然后将源码拉进项目中去

可以不做copy,因为只是查看

大部分源码有用信息在stdlib文件夹下,再具体下就是stdlib-public-core文件夹下

可以通过源码分析下:map、flatMap等高级函数的实现

==

swift 复制代码
var age1: Int? = nil
var age2: Int?? = nil
print(age1 == age2)//false

var age3: Int? = 10
var age4: Int?? = 10
print(age3 == age4)//true

当值为nil的时候,类型不一样,因此,不相等

当值不为nil,且值相等的时候,比较的是值,因此,相等

MetaData

MetaData是前八个字节的内容

每个类型有不同的MetaData

反射

对于任意一个类型,都能够动态获取这个类的所有属性和方法信息

Swift的反射机制目前比较弱,通过Mirror类型来提供简单的反射功能

相关推荐
Keya20 小时前
lipo 命令行指南
ios·xcode·swift
zhangmeng20 小时前
SwiftUI中如何实现子视图向父视图传递数据?
ios·swiftui·swift
Saafo20 小时前
迁移至 Swift Actors
ios·swift
杂雾无尘2 天前
告别构建错误, iOS 开发架构难题全面解析, 避免 CPU 架构陷阱
ios·swift·客户端
大熊猫侯佩3 天前
探秘 WWDC 25 全新 #Playground 宏:提升 Swift 开发效率的超级神器
xcode·swift·wwdc
移动端小伙伴3 天前
10.推送的扩展能力 — 打造安全的通知体验
swift
移动端小伙伴3 天前
推送的扩展能力 — 打造个性化的通知体验
swift
移动端小伙伴3 天前
远程推送(Remote Push Notification)
swift
移动端小伙伴3 天前
本地通知的精准控制三角:时间、位置、情境
swift
移动端小伙伴3 天前
本地通知内容深度解析 — 打造丰富的通知体验
swift