swift学习资料2022

目录

String 与 NSString 的关系与区别

Swift字符串的Range截取

throws 和 rethrows 的用法与作用

fileprivate和internal修饰符

open与public的区别?

swift中 closure 与OC中block的区别

swift中,如何阻止方法,属性,下标被子类改写?

associatedtype 的作用

try? 和 try!是什么意思​​​​​​​

map、filter、reduce 的作用?

defer、guard的作用?

如何自定义下标获取

这段复杂的代码按字母顺序对名称数组进行排序。尽可能简化闭包。

什么是可选的,可选可以解决哪些问题?

构体和类之间的主要区别。

什么是泛型(通用类型)?它们解决了什么问题?

nil 和 .none有什么区别?

Swift和OC的区别?

swift的派发机制

Struct和Class区别

swift中mutating的作用?

associatedtype 的作用?

什么时候使用 final

定义静态方法时关键字 static 和 class 有什么区别

Self 的使用场景

一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

inout 的作用

Error 如果要兼容 NSError 需要做什么操作

什么是高阶函数

如何解决引用循环

下面的代码会不会崩溃,说出原因

给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明

 一个 Sequence 的索引是不是一定从 0 开始?

数组都实现了哪些协议

如何让自定义对象支持字面量初始化

为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃

一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示

不通过继承,代码复用(共享)的方式有哪些

如何自定义模式匹配

swift语法糖?!的本质

什么是函数式编程


  1. String 与 NSString 的关系与区别

    1. Swift的String是struct,而NSString类是NSObject
    2. 什么时候用NSString:​​​​​​​
      1. NSString特有操作和动态特性
      2. NSString有containsString,string只能用rangeOfString
      3. String与Range配合比较麻烦
    3. 什么情况下使用String:​​​​​​​
      1. String更符合字符串“不变”特性,性能提升
      2. String实现了像CollectionType的接口。比如for...in的枚举遍历所有字
  2. Swift字符串的Range截取

    1. let str = "123456789"
      let start = str.startIndex//表示str的开始位置
      let end = str.endIndex//表示str的结束位置
      let startOffset = str.index(start, offsetBy: 2)//表示str的开始位置 + 2
      let endOffset = str.index(end, offsetBy: -2)//表示str的结束位置 - 2
              
      print(str[start])//输出 1 第1个字符
      print(str[startOffset])//输出 3 第3个字符
      print(str[endOffset])//输出 8 第8个字符(10-2)
      print(str[end])//报错!因为实endIndex指向第10个字符,而第10个字符是不存在的
      
      let range1:ClosedRange = 1 ... 4
      let range2:CountableClosedRange = 1 ... 4
      let range3:Range = 1 ..< 4
      let range4:CountableRange = 1 ..< 4
              
      let array = ["a", "b", "c", "d", "e", "f"]
      print(array[range1])
      print(array[range2])
      print(array[range3])
      print(array[range4])
      
      输出:
      ["b", "c", "d", "e"]
      ["b", "c", "d", "e"]
      ["b", "c", "d"]
      ["b", "c", "d"]
  3. throws 和 rethrows 的用法与作用

    1. throws:抛出异常,要do catch try
    2. rethrows:针对的不是函数或者方法本身,而是他携带的闭包类型的参数,当他的闭包类型的参数throws的时候,我们要使用rethrows将这个异常向上传递
  4. fileprivate和internal修饰符

    1. fileprivate访问级别所修饰的属性或者方法在当前的Swift源文件里可以访问。
    2. internal:(默认访问级别,internal修饰符可写可不写)
      internal访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。
    3. open > public > internal > fileprivate > private
  5. open与public的区别?

    1. public:可以别任何人访问,但是不可以被其他module复写和继承。
    2. open:可以被任何人访问,可以被继承和复写。
  6. swift中 closure 与OC中block的区别

    1. closure是匿名函数、block是一个结构体对象
    2. closure则默认给外部访问的变量加上了__block修饰词的block。
    3. 逃逸闭包,一般用于异步,函数结束了,逃逸闭包也还没有结束,要闭包执行完才结束,所以一般逃逸闭包,要考虑循环引用的问题。
  7. swift中,如何阻止方法,属性,下标被子类改写?

    1. final 关键字声明类、属性、方法和下标。
      final 声明的类不能被继承,final 声明的属性、方法和下标不能被重写。
      如果只是限制一个方法或属性被重写,只需要在该方法或者属性前加一个 final.
      如果需要限制整个类无法被继承, 那么可以在类名之前加一个final。
  8. associatedtype 的作用

    1. 关联类型: 类似于协议中的泛型,提高代码的复用性,为协议中的某个类型提供了一个别名,其代表的真实类型在具体实现中定义
    2. 泛型(generic)可以使我们在程序代码中定义一些可变的部分,在运行的时候指定。
      使用泛型可以最大限度地重用代码、保护类型的安全以及提高性能。
    3. //协议,使用关联类型
      protocol TableViewCell {
          associatedtype T
          func updateCell(_ data: T)
      }
       
      //遵守TableViewCell
      class MyTableViewCell: UITableViewCell, TableViewCell {
          typealias T = Model
          func updateCell(_ data: Model) {
              // do something ...
          }
      }
  9. try? 和 try!是什么意思​​​​​​​

    1. try?不处理错误,抛出异常函数时, 如果函数抛出异常, 则返回 nil, 否则返回函数返回值的可选值,
    2. try!保证不会出现错误 强制解,抛出异常的时候崩溃, 否则则返会函数返回值
  10. map、filter、reduce 的作用?

    1. [1, 2, 3].map{"\($0)"}// 一一映射成另外样子,count不变,数字数组转换为字符串数组,["1", "2", "3"]
    2. [1, 2, 3].filter{$0 % 2 == 0} // 筛选偶数,2
    3. [1, 2, 3].reduce(""){$0 + "\($1)"}//多个浓缩转换成一个, 转换为字符串并拼接,"123"
  11. defer、guard的作用?

    1. defer:主要的作用是延迟执行以及在return之前执行。
  12. 如何自定义下标获取

    1. 实现 subscript 即可,索引除了数字之外, 其他类型也是可以的
    2. extension AnyList {
          subscript(index: Int) -> T{
              return self.list[index]
          }
          subscript(indexString: String) -> T?{
              guard let index = Int(indexString) else {
                  return nil
              }
              return self.list[index]
          }
      }
  13. 这段复杂的代码按字母顺序对名称数组进行排序。尽可能简化闭包。

    1. 类型推断系统会自动判断闭包中参数的类型和返回类型,就可以去掉类型:
      
      animals.sort { (one, two) -> Bool in
           return one < two
      }
      可以用$I符号替换参数名:
      
      animals.sort { return $0 < $1 }
      在单语句闭包中,可以省略返回关键字。最后一条语句的值将成为闭包的返回值:
      
      animals.sort { $0 < $1 }
      后,由于Swift知道数组的元素符合equatable,因此可以简单地编写:
      
      animals.sort(by: <)
  14. 什么是可选的,可选可以解决哪些问题?

    1. 使用可选类型(optionals)来处理值可能缺失的情况。在objective-c中,只有在使用nil特殊值的引用类型中才可以表示值缺失。值类型(如int或float)不具有此功能。
      Swift将缺乏值概念扩展到引用类型和值类型。可选变量可以包含值或零,表示是否缺少值。
  15. 构体和类之间的主要区别。

    1. 类支持继承;结构不支持。
      类是引用类型;结构体是值类型。
  16. 什么是泛型(通用类型)?它们解决了什么问题?

    1. 提高代码的复用性。
    2. 在swift中,可以在函数和数据类型中使用泛型,例如在类、结构体或枚举中。
      泛型解决了代码重复的问题。当有一个方法接受一种类型的参数时,通常会复制它以适应不同类型的参数。
      例如,在下面的代码中,第二个函数是第一个函数的“克隆”,但它接受字符串而不是整数。
    3. func areIntEqual(_ x: Int, _ y: Int) -> Bool {
        return x == y
      }
      
      func areStringsEqual(_ x: String, _ y: String) -> Bool {
        return x == y
      }
      
      areStringsEqual("ray", "ray") // true
      areIntEqual(1, 1) // true
      通过采用泛型,可以将这两个函数合并为一个函数,同时保持类型安全。下面是通用实现:
      
      func areTheyEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
        return x == y
      }
      
      areTheyEqual("ray", "ray")
      areTheyEqual(1, 1)
      由于在本例中测试的是相等性,所以将参数限制为遵守 Equatable 协议的任何类型。此代码实现了预期的结果,并防止传递不同类型的参数。
  17. nil 和 .none有什么区别?

    1. 没有区别,none == nil
  18. Swift和OC的区别?

    1. swift是静态语言,有类型推断,OC是动态语言。
    2. swift面向协议编程,OC面向对象编程
    3. swift注重值类型,OC注重引用类型。
    4. swift支持泛型,OC只支持轻量泛型
    5. swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,OC支持动态派发(消息派发)方式。
    6. swift支持函数式编程,swift的协议不仅可以被类实现,也可以被struct和enum实现
    7. swift有元组类型、支持运算符重载,swift支持命名空间,swift支持默认参数
    8. swift比oc代码更加简洁
    9. swift语法简单易读、代码更少,更加清晰、易于维护
    10. 更加安全,optional的使用更加考验 程序员 对代码安全的掌控
    11. 泛型、结构体、枚举都很强大
    12. 函数为一等公民,便捷的函数式编程
    13. 有命名空间 基于module
    14. 类型判断
  19. swift的派发机制

    1. 三种派发机制:直接派发, 函数表派发 和 消息机制派发
      1. 直接派发 :最快指令集更少, 编译器有很大的优化空间, 例如函数内联等,但缺乏动态性,没继承,静态调用。
      2. 函数表派发:使用了一个数组存储类声明和函数指针. 称为虚函数表。查表比直接派发慢. 从字节码角度看, 多两次读和一次跳转的损耗. 另一个慢在于编译器无法优化.这种基于数组, 缺陷在于无法拓展. 子类会在最后插入新的函数, 没有位置可以让 extension 安全地插入函数.
      3. 消息机制派发:是oc的消息转发机制,是最动态的方式. 效率最低,但是缓存后跟函数表派发一样快
    2. Swift 的派发机制:使用 dynamic 修饰的时候会通过 Objective-C 的运行时进行消息机制派发.总结起来有这么几点:
      1. 值类型、协议和类的extension总是会使用直接派发
      2. NSObject 声明作用域里的函数都会使用函数表进行派发.
      3. 协议里声明的, 并且带有默认实现的函数会使用函数表进行派发.
      4. NSObject 的 extension 会使用消息机制进行派发
    3. 指定派发方式 
      1. 直接派发:final、static、@inline
      2. 消息派发:dynamic、@objc
  20. Struct和Class区别

    1. Struct不支持继承、Class支持继承
    2. Struct是值类型,Class是引用类型
    3. Struct无法修改自身属性值,函数需要添加mutating关键字
    4. Struct初始化方法是基于属性的
    5. Struct不需要deinit方法,因为值类型不关心引用计数,Class需要deinit方法。
  21. swift中mutating的作用?

    1. swift中协议是可以被Struct和enum实现的,mutating关键字是为了能在被修饰的函数中修改struct或enum的变量值。对Class完全透明。
  22. associatedtype 的作用?

    1. 简单来说就是 protocol 使用的泛型
    2. protocol ListProtcol {
          associatedtype Element
          func push(_ element:Element)
          func pop(_ element:Element) -> Element?
      }
    3. 实现协议的时候, 可以使用 typealias 指定为特定的类型, 也可以自动推断, 如​​​​​​​​​​
    4. class IntList: ListProtcol {
          typealias Element = Int // 使用 typealias 指定为 Int
          var list = [Element]()
          func push(_ element: Element) {
              self.list.append(element)
          }
          func pop(_ element: Element) -> Element? {
              return self.list.popLast()
          }
      }
      class DoubleList: ListProtcol {
          var list = [Double]()
          func push(_ element: Double) {// 自动推断
              self.list.append(element)
          }
          func pop(_ element: Double) -> Double? {
              return self.list.popLast()
          }
      }
  23. 什么时候使用 final

    1. final 用于限制继承和重写. 如果只是需要在某一个属性前加一个 final。 如果需要限制整个类无法被继承, 那么可以在类名之前加一个final​​​​​​​
  24. 定义静态方法时关键字 static 和 class 有什么区别

    1. ​​​​​​​​​​​​​​​​​​​​​static 定义的方法不可以被子类继承, class 则可以
    2. class AnotherClass {
          static func staticMethod(){}
          class func classMethod(){}
      }
      class ChildOfAnotherClass: AnotherClass {
          override class func classMethod(){}
          //override static func staticMethod(){}// error
      }
  25. Self 的使用场景

    1. Self 通常在协议中使用, 用来表示实现者或者实现者的子类类型. 例如, 定义一个复制的协议
    2. protocol CopyProtocol {
          func copy() -> Self
      }
    3. 如果是结构体去实现, 要将Self 换为具体的类型
    4. struct SomeStruct: CopyProtocol {
          let value: Int
          func copySelf() -> SomeStruct {
              return SomeStruct(value: self.value)
          }
      }
    5. 如果是类去实现, 则有点复杂, 需要有一个 required 初始化方法
    6. class SomeCopyableClass: CopyProtocol {
          func copySelf() -> Self {
              return type(of: self).init()
          }
          required init(){}
      }
  26. 一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

    1. OptionSet, 一般使用 struct 实现. 由于 OptionSet 要求有一个不可失败的init(rawValue:) 构造器, 而 枚举无法做到这一点(枚举的原始值构造器是可失败的, 而且有些组合值, 是没办法用一个枚举值表示的)
    2. 选项
      struct SomeOption: OptionSet {
          let rawValue: Int
          static let option1 = SomeOption(rawValue: 1 << 0)
          static let option2 =  SomeOption(rawValue:1 << 1)
          static let option3 =  SomeOption(rawValue:1 << 2)
      }
      let options: SomeOption = [.option1, .option2]
  27. inout 的作用

    1. 输入输出参数, 如:
    2.  
      func swap( a: inout Int, b: inout Int) {
          let temp = a
          a = b
          b = temp
      }
      var a = 1
      var b = 2
      print(a, b)// 1 2
      swap(a: &a, b: &b)
      print(a, b)// 2 1
  28. Error 如果要兼容 NSError 需要做什么操作

    1. 其实直接转换就可以, 例如 SomeError.someError as NSError 但是这样没有错误码, 描述等等, 如果想和 NSError 一样有这些东西, 只需要实现 LocalizedErrorCustomNSError 协议, 有些方法有默认实现, 可以略过, 如:
    2.   
      enum SomeError: Error, LocalizedError, CustomNSError {
          case error1, error2
          public var errorDescription: String? {
              switch self {
              case .error1:
                  return "error description error1"
              case .error2:
                  return "error description error2"
              }
          }
          var errorCode: Int {
              switch self {
              case .error1:
                  return 1
              case .error2:
                  return 2
              }
          }
          public static var errorDomain: String {
              return "error domain SomeError"
          }
          public var errorUserInfo: [String : Any] {
              switch self {
              case .error1:
                  return ["info": "error1"]
              case .error2:
                  return ["info": "error2"]
              }
          }
      }
      print(SomeError.error1 as NSError)
      // Error Domain=error domain SomeError Code=1 "error description error1" UserInfo={info=error1}
  29. 什么是高阶函数

    1. 一个函数如果可以以某一个函数作为参数, 或者是返回值, 那么这个函数就称之为高阶函数, 如 map, reduce, filter
  30. 如何解决引用循环

    1. 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的
    2. delegate 使用 weak 属性
    3. 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned, 修饰
  31. 下面的代码会不会崩溃,说出原因

    1. var mutableArray = [1,2,3]
      for _ in mutableArray {
          mutableArray.removeLast()
      }
    2. 不会,removeLast如果是空数组会崩溃,在for等同于一个空判断的迭代器,只要有值才执行
  32. 给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明

    1.  
      extension Array where Element == String {
          var isStringElement:Bool {
              return true
          }
      }
      ["1", "2"].isStringElement
      //[1, 2].isStringElement// error
  33.  一个 Sequence 的索引是不是一定从 0 开始?

    1. sequence是相同类型集合的迭代器,迭代器不是数组,索引是可以继续迭代还是重新开始,所以它不一定是从0开始,跟序号索引不强绑定,它是next,不是从0开始
  34. 数组都实现了哪些协议

    1. MutableCollection, 实现了可修改的数组
    2. ExpressibleByArrayLiteral, 实现了数组可以从[1, 2, 3] 这种字面值初始化的能力
  35. 如何让自定义对象支持字面量初始化

    1. ExpressibleBy____Literal
    2. ExpressibleByArrayLiteral 可以由数组形式初始化
    3. ExpressibleByDictionaryLiteral 可以由字典形式初始化
    4. ExpressibleByNilLiteral 可以由nil 值初始化
    5. ExpressibleByIntegerLiteral 可以由整数值初始化
    6. ExpressibleByFloatLiteral 可以由浮点数初始化
    7. ExpressibleByBooleanLiteral 可以由布尔值初始化
    8. ExpressibleByUnicodeScalarLiteral
    9. ExpressibleByExtendedGraphemeClusterLiteral
    10. ExpressibleByStringLiteral
    11. Bool : ExpressibleByBooleanLiteral
      Int : ExpressibleByIntegerLiteral
      Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral
      Dictionary : ExpressibleByDictionaryLiteral
      String : ExpressibleByStringLiteral
      Array、Set : ExpressibleByArrayLiteral
      Optinal : ExpressibleByNilLiteral
      class MyUrl{
         var url:URL
         init(string: String){
            self.url = URL(string: string)
         }
      }
      
      extension myUrl:ExpressibleByStringLiteral {
          init(stringLiteral value: String) {
              self.url = URL(string: value)
          }
      }
      
      let url:myUrl = "https://www.baidu.com"
      print(url.url) // "https://www.baidu.com"
      
      var num: Int = true //错误
      
      extension Int : ExpressibleByBooleanLiteral {
          public init(booleanLiteral value: Bool) {
              self = value ? 1 : 0
          }
      }
      var num: Int = true //正确
      print(num) // 输出:1

       
  36. 为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃

    1. 数组是一段连续的地址,越界也是可以访问到那一片内存的,但是不合法的访问就是野指针
    2. 字典不一样,它存储的是指针,不是真真正的,找不到就返回nil
  37. 一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示

    1.  
      Int、Float 都有一个协议 
      func myMethod(_ value: T) where T: Numeric { 
          print(value + 1) 
      } 
      或者 ExpressibleByIntegerLiteral 协议也行
      
  38. 不通过继承,代码复用(共享)的方式有哪些

    1. Extensions
    2. ​​​​​​​Protocols
  39. 如何自定义模式匹配

    1. infix operator =~ 
      func =~ (str: String, pattern: String) -> Bool {
      } 
      infix、 prefix、 postfix 用于自定义表达式的声明, 分别表示 中缀、前缀、后缀
      

      Swift 中没有内置的正则表达式,有一个相似的功能,就是匹配模式

  40. swift语法糖?!的本质

    1. ? 和 ! 其实分别是Swift语言中对一种可选类型( Optional) 操作的语法糖,​​​​​​​Optional其实是个enum,里面有NoneSome两种类型。其实所谓的nil就是Optional.None , 非nil就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用Optional的时候要拆包(从enum里取出来原始值)的原因
    2. var name: String?
      // 上面这个Optional的声明,是”我声明了一个Optional类型值,
       它可能包含一个String值,也可能什么都不包含”,
      也就是说实际上我们声明的是Optional类型,而不是声明了一个String类型 
      (这其实理解起来挺蛋疼的...)
  41. 什么是函数式编程

    1. 编程程序的一种代码规范,主要思想是把运算过程尽量写成一系列嵌套的函数调用
    2. 它使代码更像自然语言,告诉程序员要干什么,而不是怎么干,把怎么干的细节拆分到各个函数中。例如1+1=,那么就变成一加一
    3. 特点:
      1. 代码简洁,开发快速
      2. 接近自然语言,易于理解
      3. 更方便的代码管理。函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同
      4. 易于"并发编程"。函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题
  42. ​​​​​​​​​​​​​​

​​​​​​​

猜你喜欢

转载自blog.csdn.net/harder321/article/details/127312756