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. 标准库源码分析、项目实战
下载过源码后,可以新建一个空的工程,然后将源码拉进项目中去
可以不做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类型来提供简单的反射功能