改为 Swift 泛型的高阶玩家(附实战适配 Demo)Map Filter Reduce

我们事先多之是,去定义有一个路的对象、定义有一个功能型函数,有尝试过定义对象族、函数族?而所谓泛型编程,即我们将所需要定义的对象和函数抽象出来,极大拓宽了利用状况,减少代码的冗余!
其实,我们可能没定义了泛型函数,但毫无疑问起动了 Swift
标准库中之泛型函数:map()、filter()、reduce()。而这些泛型函数,众所周知,应用场合极大,基本可以作用被其他参数类型。而我辈平素动最多的
Array 和 Dictionary
也还是泛型集合,我们得以通往当时有限单集聚传输基本任何项目的价,而输出的色为是因为我们输入的种规定,这为是泛型的一模一样万分特色。
假若立即篇稿子用会晤构成一个施用泛型编程的适配工具来谈谈泛型的高阶玩法。

1 map函数

悬念: 我们意在使下图般的,在不同尺寸的设施适配不同之封面图及文件。

连无是swift标准库中也一个下泛型的函数。

效果图

领外函数作为参数的函数有时受称呼高阶函数

而且,我们意在效果代码越简单越好,可读性越强更好,像下一样就是会达到效果:

map 的泛型版本定义为Array的扩充。

ScreenFeatureManager.shared
.adapt(toDevice: .iPhone(inch35: 30, inch40: 40, inch47: 50, inch55: 60))

处理规定项目的函数定义为该品种的恢弘。

那,我们欠怎么开也?在此之前,先介绍下就要以到的泛型函数。

亮点:自动补全还健全 暧昧的命名更少 以及(通常)代码结构更清楚。

Swift 标准库中之泛型函数

实在,如果你熟悉函数式编程,那么您对这些泛型函数应该了如指掌,如果你询问都嗜上了函数式编程,何不利用
RxSwift 进行函数响应式编程呢?这里产生几篇 RxSwift
开发的实战,望有助于大家进一步深入认识 RxSwift 函数响应式开发:

  • <荐> RxSwift + ReactorKit
    构建信息流框架
  • 下 RxSwift 构建不同风格的读模式(附
    Demo)
  • 同等年半支付经历,使用 RxSwift
    构建一个类型的主导框架,这种姿势足够优雅也?

上述皆为实战篇,往后见面出其 知识点讲解篇

2 Filter 通用型函数

Map:

Map
函数一般是奉一个加的往往组,然后经过有非降维的测算处理,得到并返一个新的数组。

苹果官方概念

extension Array {
    func map<T>( transform: Element ->  T) -> [T] {
      var result: [T] = []
       for x in self {
          result.append(transform(x))
       }
       return result
    }
}

在 Map 中定义一个泛型类,经过 transform
闭包函数处理后,通过泛型数组去用到拍卖后的新数据,成为新的数组。

应用
以以下数组中的每个元素增加1继输出

let objects: [Int] = [1, 2, 3]

事先利用深谙不了之 For 循环

var newObjects: [Int] = [] 
for object in objects {
   let newObject = object + 1
   newObjects.append(newObject)
}

接下来,使用 Map 函数

// objects.map { newObject in  return newObject + 1 } 
// 上面是完整的 Map 函数编写,但如果闭包中的代码比较简单,我们都会省略 return,如下:
objects.map { newObject in  newObject + 1 }

得看到,四履之的代码块经 Map
函数处理后,成为了链式的代码段,借这也可引入一个初的定义,即函数式编程:主要是以扑灭冗余且复用场景极大的代码块,抽象成复用性极强之代码段,当然以上代码还不够函数式,我们可持续优化:

// 定义好计算函数
func addCompute(_ object: Int) -> Int {
  return object + 1
}
//进一步优化调整输出函数
objects.map { newObject in  addCompute(newObject) }

函数式编程:需要我们用函数作为其它函数的参数传递,或者作为返回值返还,有时也于喻为高阶函数。

filter 函数接受一个函数作为参数;filter 函数的型是Element-Bool
–对于数组中之兼具因素,此函数都见面判定他是否应当叫含有在结果丁:

Filter:

Filter
函数同样是收一个加以的再三组,通过加的筛选标准,取得数组中符合条件的因素,并回一个初的数组。

苹果官方概念

extension Array {
    func  filter( includeElement: Element ->  Bool) -> [Element] {
      var result: [Element] = []
       for x in self {
          result.append(includeElement(x))
       }
       return result
    }
}

当 filter 中定义一个泛型元素 Element,经过 includeElement
闭包函数筛选处理以后,再经过泛型数组拿到处理后底初数据,成为新的数组。

应用
俺们将到以上定义好的 objects 数组,拿到内装有的偶数
for 循环

let newObjects: [Int] = []
for oldObject in Objects {
   if oldObject%2 == 0 {
      newObjects.append(oldObject)   
   }
}

filter 函数

 objects.filter { filterElement in  filterElement%2 == 0 }

同等的,你得感受下 filter 函数处理下,链式代码的可读性。

比如map 一样,Swift标准库中的数据类型已经产生定义好之filter函数。

Reduce

Reduce 函数接收一个输入数组,同时用吸收一个 T 类型的发端值,
经过 combine 函数处理下,返回一个及也 T 类型的结果。在有的诸如 OCaml 和
Haskell 一样的函数语言中,reduce 函数被称呼 fold 或 fold_left。而
reduce
可英译为做,简单来说就是透过我们所想的道组成一个数组中之要素。

苹果官方概念

extension Array {
    func  reduce<T>( initialValue: T, combine: (T, Element) -> T) -> [T] {
      var result = initialValue
       for x in self {
          result = combine(result, x)
       }
       return result
    }
}

每当 reduce 中起半点独泛型元素 T && Element,combine
是对准于数组的处理函数,我们输入初始值和数组中的每一个要素之后,即可输出返回一个上佳之值。

应用
咱还用到 map 中定义好之 objects
数组,拿到中每个元素相乘后底结果。
for 循环

func reduceInstance() {
  let newObject: Int = 1
  for oldObject in Objects {
     newObject * oldObject
  }
  return newObject
}

reduce 函数

 objects.reduce(1) { result, x in  result * x }

 // 我们也可以将运算符作为最后一个参数,让这段代码更短且不影响可读性
 objects.reduce(1, combine: *)

如上,即为利用 reduce 后处理的结果

3.Reduce

最后

咱们尝试着以用上述三个函数去意一个数组。

let lastObjects: [Int] = [2017, 10, 7, 11, 09, 6]

场景
我们要用一个整形数组中的元素:

  • 优先将兼具的因素 + 1
  • 罗产生其中的偶数元素
  • 用拥有筛选到的元素相加

lastObjects.map { element in element + 1 }
.filter { element in element%2 == 0 }
.reduce(0, combine: +)

恍如复杂的采取场景,使用泛型函数编程是匪是更换得格外粗略?以上气象你尝试用
for 循环?

譬如计算数据的以及;

泛型编程:适配工具的实战开发

如上,我们上课了苹果用泛型构建的函数,接下去我们进来一个简可专门实用的泛型实战。

以变量a初始化为某值,然后针对输入数组的各一样码进行遍历,最后因为某种方式更新结果。为了定义一个得以反映所待种的泛型函数,我们得对片卖信息进行抽象:赋给result变量的开头值,和用来在列一样糟糕巡回中创新结果的函数。Reduce函数就配合这种模式。

特意大的运场景

咱们展示到界面澳门网上娱乐及之要素:图片、文字,很多早晚要以不同尺寸的设备及显现不同的态度(大小、位置、样式),这个时节咱们欠怎么收拾?仔细一想,其实这个还是生格外多景的,可能吗会见招致十分多功能性冗余替代码块,该怎么惩罚?
使用泛型编程正好可缓解了这些问题。

Reduce函数在Swift标准库助也已经提供了。

性能定义

概念屏幕类型(iPhone/iPad),而各个种类型,都起例外尺寸的屏幕大小:

enum DeviceType<T> {
    case iPhone(inch35: T, inch40: T, inch47: T, inch55: T)
    case iPad(common: T, pro: T)
}

概念屏幕的尺寸系数及当前屏幕尺寸,目的是深受外界得以透过该属性直接了解当前凡是那种尺寸的屏幕:

struct DeviceDiaonal {
    static let iPhone4: Double = 3.5
    static let iPhoneSE: Double = 4.0
    static let iPhone6: Double = 4.7
    static let iPhone6Plus: Double = 5.5
}

// 当前屏幕尺寸
var currentDiaonal: Double = DeviceDiaonal.iPhone6

概念屏幕的条件和当前屏幕规格,目的是吃外界可以透过该属性直接掌握当前是那种屏幕规格的:

// 屏幕规格
enum ScreenSpecs {
    enum PhoneInch {
        case inch35, inch40, inch47, inch55
    }
    enum PadInch {
        case common, pro
    }
    case iPhone(PhoneInch), iPad(PadInch)
}

// 当前屏幕规格
var screenSpecs: ScreenSpecs = .iPhone(.inch47)

咱们得利用Reduce来代表有的函数。这个谜底证实了reduce能够透过通用的法门来反映一个一定广阔的编程模式:遍历数组并盘算结果。

初始化构造器的构建

盖时工具类是一个拍卖接近,所以我们可将那定义也单例类,而其初始化构造器仅限于被单例调用。那么,我们得以初始化构造器初始化什么性质为?因为当时是一个屏幕特性的单例类,毋庸置疑,我们好一直通过此类,就可将到当前屏幕的保有特性,因此在初始化构造器中,我们要针对脚下有的屏幕特性开展初始化

// 构造单例(调用 init 构造函数)
static let shared = ScreenFeatureManager()

fileprivate init() {
    let screenWidth = UIScreen.main.bounds.width
    switch screenWidth {
    case 320:
        if screenHeight <= 480 {
            currentDiaonal = DeviceDiaonal.iPhone4
            screenSpecs = .iPhone(.inch35)
        } else {
            currentDiaonal = DeviceDiaonal.iPhoneSE
            screenSpecs = .iPhone(.inch40)
        }

    case 375:
        currentDiaonal = DeviceDiaonal.iPhone6
        screenSpecs = .iPhone(.inch47)

    case 414:
        currentDiaonal = DeviceDiaonal.iPhone6Plus
        screenSpecs = .iPhone(.inch55)

    case 768:
        screenSpecs = .iPad(.common)

    case 1024:
        screenSpecs = .iPad(.pro)

    default:
        break
    }
}

从那之后,我们初始化了部分屏幕特性,接下去,我们将这些屏幕特性加到正菜中!

tip:通过reduce
来定义一切似乎不是呀好主意。因不出意外的话语你的代码最终火灾运行中大量复制生成的累累组,换句话说,它只能一再分配内存,释放内存。

下泛型类构造适配函数

运用前面定义好之 DeviceType
类型,对于这类别,我们好因不同的色输入不同之泛型值(T),然后以函数内,拿到齐同一步就是处理好之屏幕特性,结合输入值进行判定处理,不同的屏幕会映射会起不同的泛型值(T),并将到该映射下的泛型值输出返回。

func adapt<T>(toDevice type: DeviceType<T>) -> T {

    // 多个输入值,判断处理之后,输出一个单一的泛型值(多对一的映射)
    switch type {
    case let .iPhone(inch35, inch40, inch47, inch55):
        switch screenSpecs {
        case .iPhone(.inch35):
            return inch35
        case .iPhone(.inch40):
            return inch40
        case .iPhone(.inch47):
            return inch47
        case .iPhone(.inch55):
            return inch55
        default:
            return inch47
        }

    case let .iPad(common, pro):
        switch screenSpecs {
        case .iPad(.common):
            return common
        case .iPad(.pro):
            return pro
        default:
            return common
        }
    }
}

4.泛型和any 类型

应用

上述泛型函数构造好之后,适配工作就是易得特别简单。
譬如说有三单适配点:

  • 不等装备,拥有不同之封面图
  • 不同装备,封面图的 size 是未同等的
  • 差装备,其标题颜色、样式、大小都未均等

我们欠怎么适配以上之求呢?

fileprivate func adaptiveConfiguration() {
    //适配封面图的宽度(在 storyBoard 中宽度与高度成一比例,适配了宽度,高度也会跟着变化)
    coverImageViewWidthConstraint.constant = ScreenFeatureManager.shared.adapt(toDevice: .iPhone(inch35: 150, inch40: 250, inch47: 350, inch55: 420))

    // 适配不同的设备不同的封面图
    coverImageView.image = ScreenFeatureManager.shared.adapt(toDevice: .iPhone(inch35: UIImage(named: "home_adapt_inch35"), inch40: UIImage(named: "home_adapt_inch40"), inch47: UIImage(named: "home_adapt_inch47"), inch55: UIImage(named: "home_adapt_inch55")))

    //适配主题标题内容
    themeTitleLabel.text = ScreenFeatureManager.shared.adapt(toDevice: .iPhone(inch35: "杳无音迅(inch35)", inch40: "杳无音迅(inch40)", inch47: "杳无音迅(inch47)", inch55: "杳无音迅(inch55)"))

    //适配主题标题的字体样式
    themeTitleLabel.font = ScreenFeatureManager.shared.adapt(toDevice: .iPhone(inch35: UIFont.boldSystemFont(ofSize: 15), inch40: UIFont.boldSystemFont(ofSize: 18), inch47: UIFont.boldSystemFont(ofSize: 21), inch55: UIFont.boldSystemFont(ofSize: 25)))

    //适配主题标题的字体颜色
    themeTitleLabel.textColor = ScreenFeatureManager.shared.adapt(toDevice: .iPhone(inch35: UIColor.black, inch40: UIColor.gray, inch47: UIColor.lightGray, inch55: UIColor.green))

}

由来,适配工具已经开完成,如果您是动 Swift
开发,那么好直接拿该引入你的档次应用。如果发生再度好之兑现方式,期待你评论报。

Demo
https://github.com/iJudson/ScreenFeature
欢迎 stars
Thanks:多谢观看,欢迎收藏文章,欢迎关注、交流…

any 类型 能代表任何项目的价。

any 类型和泛型都能用来定义接受两单不同类别参数的函数。

泛型可以用于定义灵活的函数,类型检查还由编译器负责;

万一any类型则足以避开swift的种类系统(因而应当尽可能避免采用