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类型来提供简单的反射功能

相关推荐
报错小能手17 小时前
ios开发方向——swift并发进阶核心 Task、Actor、await 详解
开发语言·学习·ios·swift
用户79457223954132 天前
【AFNetworking】OC 时代网络请求事实标准,Alamofire 的前身
objective-c·swift
报错小能手2 天前
SwiftUI 框架 认识 SwiftUI 视图结构 + 布局
ui·ios·swift
东坡肘子2 天前
被 Vibe 摧毁的版权壁垒,与开发者的新护城河 -- 肘子的 Swift 周报 #131
人工智能·swiftui·swift
报错小能手2 天前
ios开发方向——swift错误处理:do/try/catch、Result、throws
开发语言·学习·ios·swift
小夏子_riotous2 天前
openstack的使用——5. Swift服务的基本使用
linux·运维·开发语言·分布式·云计算·openstack·swift
mCell3 天前
MacOS 下实现 AI 操控电脑(Computer Use)的思考
macos·agent·swift
用户79457223954133 天前
【DGCharts】iOS 图表渲染事实标准——8 种图表类型、高度可定制,3 行代码画出一条折线
swiftui·swift
chaoguo12343 天前
Any metadata 的内存布局
swift·metadata·value witness table
tangweiguo030519874 天前
SwiftUI布局完全指南:从入门到精通
ios·swift