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

相关推荐
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
KeithTsui2 天前
ZFC in LEAN 之 前集的等价关系(Equivalence on Pre-set)详解
开发语言·其他·算法·binder·swift
袁代码2 天前
Swift 开发教程系列 - 第4章:函数与闭包
ios·swift·ios开发
安泽13143 天前
高德地图美食
开发语言·swift·美食
袁代码3 天前
Swift 开发教程系列 - 第2章:Swift 基础语法
swift·ios开发·基础教程
袁代码3 天前
Swift 开发教程系列 - 第1章:Swift 简介与开发环境配置
swift·ios开发·基础教程
孚亭4 天前
一些swift问题
swift
莫问alicia4 天前
echarts 实现3D饼状图 加 label标签显示
前端·3d·echarts·swift
uiop_uiop_uiop6 天前
iOS Swift5算法恢复——HMAC
ios·iphone·swift
東三城8 天前
【ios】---SwiftUI开发从入门到放弃
ios·swiftui·swift·1024程序员节