zoukankan      html  css  js  c++  java
  • Swift学习:泛型,关联类型,类型约束,协议类型及错误解决,some

    https://blog.csdn.net/weixin_42433480/article/details/96651379?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-6-96651379.nonecase&utm_term=swift泛类&spm=1000.2123.3001.4430

    泛型(Generics)

    • 泛型可以将类型参数化,提高代码复用率,减少代码量
    1.  
      func swapValues<T>(_ a: inout T, _ b: inout T) {
    2.  
      (a, b) = (b, a)
    3.  
      }
    4.  
       
    5.  
      var i1 = 10
    6.  
      var i2 = 20
    7.  
      swapValues(&i1, &i2)
    8.  
      print(i1, i2) // 20, 10 i1和i2的值交换了
    9.  
       
    10.  
      var d1 = 10.0
    11.  
      var d2 = 20.0
    12.  
      swapValues(&d1, &d2)
    13.  
      print(d1, d2) //20.0, 10.0 d1和d2的值交换了
    14.  
       
    15.  
      struct Date {
    16.  
      var year = 0, month = 0, day = 0
    17.  
      }
    18.  
      var dd1 = Date(year: 2011, month: 9, day: 10)
    19.  
      var dd2 = Date(year: 2012, month: 10, day: 11)
    20.  
      swapValues(&dd1, &dd2)
    21.  
      print(dd1,dd2)//Date(year: 2012, month: 10, day: 11), Date(year: 2011, month: 9, day: 10) dd1和dd2的值交换了
    22.  
       
    23.  
       

    通过反汇编看一下上面的泛型交换值函数的额原理:

    T为Int:

    T为Double:

    可以看出,虽然泛型的类型不同,但是可以看出两个函数的地址都是0x100001800,调用的同一个函数,可以区分不同类型进行计算,只要是靠metadata也就是元类型来实现的

    • 泛型函数赋值给变量,也是可以说赋值给参数

    • 泛型的举例应用:
    1. 类的泛型

    类泛型的继承:

     记住要写泛型<E>

     2.   结构体的泛型

    因为push会添加元素到数组,pop会删除元素从数组,都会改变内存,所以要加mutating

    Score<Int>.grade("A")需要加Int是因为初始化时就要告诉泛型的类型,即使你没调用泛型


    关联类型

    • 关联类型的作用:给协议中用到的类型定义一个占位名称
    • 协议中可以拥有多个关联类型

      使用typealias给关联类型设置真实类型可以省略,因为会根据你传入的参数类型自动识别

      也可以传入一个泛型来代替关联类型


    类型约束

    针对泛型的类型约束:

    1.  
      protocol Runnable {
    2.  
       
    3.  
      }
    4.  
       
    5.  
      class Person {
    6.  
       
    7.  
      }
    8.  
       
    9.  
      //要求传入的泛型既是Person或Person 的子类,又要遵守Runnable协议
    10.  
      func swapValues<T: Person & Runnable>(_ a: inout T, _ b: inout T) {
    11.  
      (a, b) = (b, a)
    12.  
      }

    针对关联类型的约束:关联类型遵守Equatable协议,泛型也要遵守相同的协议:

    1.  
      protocol Stackable {
    2.  
      associatedtype Element: Equatable
    3.  
      }
    4.  
       
    5.  
      class Stack<E: Equatable> : Stackable {typealias Element = E}

    多个泛型结合where的复杂约束:

    S1和S2两个泛型都遵守Stackable协议,并且S1和S2的Element属于同一类型,S1的Element遵守Hashable协议

    1.  
      func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable{
    2.  
      return true
    3.  
      }
    4.  
       
    5.  
      var stack1 = Stack<Int>()
    6.  
      var stack2 = Stack<Int>()
    7.  
      var isEqual = equal(stack1, stack2)
    8.  
      print(isEqual) //true

    但是当stack1和stack2的泛型类型不一致会报错:


    协议类型的注意点

    1.  
      protocol Runnable { }
    2.  
      class Person: Runnable { }
    3.  
      class Car: Runnable { }
    4.  
       
    5.  
      func get(_ type: Int) -> Runnable {
    6.  
      if type == 0 {
    7.  
      return Person()
    8.  
      }
    9.  
      return Car()
    10.  
      }
    11.  
       
    12.  
      var r1 = get(0)
    13.  
      var r2 = get(1)
    14.  
      print(r1, r2) //TestSwift.Person TestSwift.Car

    由上面的方法可知,get方法需要返回一个遵守Runnable 协议的值,而Person和Car正好都遵守Runnable 协议,所以正确

    • 如果协议中有associatedtype

    具体报的错误:

    报错的原因是这种写法,编译器在编译时期不能确定associatedtype(关联类型)的值

    顺便说一句,作为参数会有同样的错误:


    协议类型错误的解决方案

    1.   使用泛型解决

    1.  
      func get<T : Runnable>(_ type: Int) -> T {
    2.  
      if type == 0 {
    3.  
      return Person() as! T
    4.  
      }
    5.  
      return Car() as! T
    6.  
      }
    7.  
       
    8.  
      var r1: Person = get(0)
    9.  
      var r2: Car = get(1)

    成功原因:这种情况下,我们就明确了get(0)的返回值是Person类型,get(1)的返回值是Car类型,这样在编译时期就可以知道相对应的associatedtype(关联类型)的值,所以正确

    但是这种写法存在风险,如果你把代码写成var r1:Car = get(0),这样当走入get方法时代码就会变为Person as!Car,这样把Person类型强制转换为Car类型,就会报错的

    2.  使用some关键字声明一个不透明类型,不透明类型就是some修饰的返回值只知道是遵守Runnable协议的,具体是什么类型不知道,实际类型的信息更不知道,并且some会强制只有一个返回值类型

    这种写法限制了只返回一种类型,这样编译器就会知道返回值类型,从而知道associatedtype(关联类型)的值,所以正确


    some

    • some限制只能返回一种类型

    • some除了用在返回值类型上,一般还可以用在属性类型上

    这时的属性只知道遵守Runnable协议,不知道它的具体类型,更不知道具体类型的属性和方法。

  • 相关阅读:
    检测到有潜在危险的 Request.Form 值
    检查用户是否有权限
    尝试用户自动登录
    用反射封装HttpHandler,实现通过action方法名调用方法
    mac 系统下安装MYSQL数据库
    iOS 下配置XMPP 服务器openfire详解
    【2014最新】iOS App 提交上架store 详细流程(转)
    面试题
    iOS开发之如何在xcode中自定义快捷键
    AFNetwork 作用和用法详解
  • 原文地址:https://www.cnblogs.com/sundaysme/p/14018075.html
Copyright © 2011-2022 走看看