失定义有一个种的靶子、定义有一个功能型函数。接受外函数作为参数的函数有时被称。

去定义某一个类型的对象、定义某一个功能型函数,接受其他函数作为参数的函数有时被称为

咱俩前面多之凡,去定义有一个品种的对象、定义有一个功能型函数,有试了定义对象族、函数族?而所谓泛型编程,即我们用所欲定义的对象与函数抽象出来,极大拓宽了运用状况,减少代码的冗余!
其实,我们也许没有概念了泛型函数,但一定起使用了 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的花色系统(于是应尽量避免用