美文网首页
一些问题(一)

一些问题(一)

作者: 一个栗 | 来源:发表于2020-12-13 12:43 被阅读0次

1. 类(class)和结构体(struct)有什么区别?

在 Swift 中,类是引用类型,结构体是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。

class Temperature {
  var value: Float = 37.0
}

class Person {
  var temp: Temperature?

  func sick() {
    temp?.value = 41.0
  }
}

let A = Person()
let B = Person()
let temp = Temperature()

A.temp = temp
B.temp = temp

A.sick()

上面这段代码,由于 Temperature 是 class ,为引用类型,故 A 的 temp 和 B 的 temp指向同一个对象。A 的 temp修改了,B 的 temp 也随之修改。这样 A 和 B 的 temp 的值都被改成了 41.0。如果将 Temperature 改为 struct,为值类型,则 A 的 temp 修改不影响 B 的 temp。
内存中,引用类型如类是在堆上的,值类型如结构体是在栈上的,相比于栈上的操作,堆上的操作更加复杂耗时,所以苹果官方推荐使用结构体,这样可以提高App运行效率。
class优势

  • class可以继承,子类可以使用父类的特性和方法
  • 类型转换可以在runtime的时候检查和解释一个实例的类型
  • 可以用deinit来释放资源
  • 一个类可以被多次引用
    struct优势
  • 结构较小,适用于复制操作,相比于一个class的实例被多次饮用更加安全。
  • 无需担心内存memory leak或者多线程冲突问题。

2.Swift是面向对象还是函数式的编程语言?

既是面向对象,也是面向函数。
面向对象:支持类的封装、继承、多态。
函数式编程:支持map、reduce、filter、flatmap这类去除中间状态,数学函数式的方法,更加强调运算结果而不是中间过程。

3.什么是可选类型(optional)?

在Swift中,可选类型是为了表达当一个变量值为空的情况。当一个值为空时,他就是nil。Swift中无论是引用类型或是值类型的变量,都可以是可选类型变量。

// 值类型Float,value 默认值为37.0
var value: Float? = 37.0
// 值类型String,key 默认值为 nil
var key: String? = nil
// 引用类型 UIImage,image 默认值为 nil
let image: UIImage?

OC中没有明确提出可选类型的概念,然而其引用类型却可以为nil,以此来标识其变量值为空的情况。Swift将这一理念扩大到值类型,并且明确提出了可选型的概念。

4.在Swift中,什么是范型(Generics)?

范型在Swift中主要为增加代码的灵活性而生,它可以使得对应的代码满足任意类型的变量或方法。
如以下代码,交换任意类型的变量:

func swap<T>(_ a: inout T, _ b: inout T) {
  (a, b) = (b, a)
}

Swift 是类型安全的语言,所以这里交换的两个变量其类型必须一致。

5. 请说明并比较以下关键词:Open, Public, Internal, File-private, Private

Swift有5个级别的访问控制权限,从高到低是Open, Public, Internal, File-private, Private。
基本原则是:高级别的变量不允许被定义为低级别变量的成员变量。比如一个 private 的 class 中不能含有 public 的 String。反之,低级别的变量却可以定义在高级别的变量中。比如 public 的 class 中可以含有 private 的 Int。

  • Open:具备最高的访问权限,修饰的类和方法可以在任意Module中被访问和重写,是Swift3中新添加的访问权限。
  • Public:权限仅次于Open,区别是他修饰的对象在任意Module中可以被访问,但不能重写。
  • Internal:默认的权限,表示只能在当前定义的Module中访问和重写,可以被一个Module中的多个文件访问,但不能被其他的Module中的访问。
  • File-private:Swift3新添加的权限,被修饰的对象只能在当前文件中使用,即它可以被一个文件中不同的class、extension、struct共同使用。
  • Private:最低的访问权限,对象只能在定义的作用域内及其对应的扩展中使用,离开了这个对象,即使是同一个文件中的对象,也无法访问。

6. 请说明并比较以下关键词:strong, weak, unowned

Swift内存管理机制和OC一样为ARC,基本原理是,一个对象没有任何强引用指向它时,其占用的内存会被回收,反之,只要有任何一个强引用指向该对象,它就会一直存在内存中。

  • strong代表强引用,默认属性,引用计数+1
  • weak弱引用,引用计数不会增加,且对象释放后,指针会自动置为nil,不会野指针。
  • unowned和弱引用本质上一样,区别是对象释放后,依然会有一个无效的引用指向该对象,不是Optional也不是nil,如果继续访问,会崩溃。
    weak和unowned的使用场景有如下区别:
  • 当访问对象时,该对象可能已经释放,则用weak,如delegate。
  • 当访问对象确定不会被释放,则用owned,比如self的使用。
  • 为了安全起见,直接用weak。

7. 在 Swift 中,怎样理解是 copy-on-write?

当值类型如struct在复制时,复制的对象和原对象实际上在内存里指向同一个对象,当且仅当复制后的对象进行修改的时候,才会在内存里创建一个新的对象,如

// arrayA 是一个数组,为值类型
let arrayA = [1, 2, 3]
// arrayB 这个时候与 arrayA 在内存中是同一个东西,内存中并没有生成新的数组
var arrayB = arrayA
// arrayB 被修改了,此时 arrayB 在内存中变成了一个新的数组,而不是原来的 arrayA
arrayB.append(4)

复制的数组和原数组共享同一个地址直到其中之一发生改变,这样设计使得值类型可以复制多次而无需耗费多余内存,只有变化的时候才会增加开销,内存的使用更加高效。

8. 什么是属性观察(Property Observer)?

指在当前类型内对特定属性进行监视,并作出响应的行为。是swift的特性,有2种,为willSet和didSet,例如

var title: String {
  willSet {
    print("将标题从\(title)设置到\(newValue)")
  }
  didSet {
    print("已将标题从\(oldValue)设置到\(title)")
  }
}

这段代码对title做了监听,title发生改变前,willSet对应的作用域将被执行,新的值是newValue,当title发生改变之后,didSet对应的作用域将被执行,原来的值为oldValue,这就是属性观察。
总结:初始化方法对属性的设定,以及在willSet和didSet中对属性的再次设定都不会触发属性观察的调用。

9. 结构体中修改成员变量的方法

下面代码有什么问题?

protocol Pet {
  var name: String { get set }
}

struct MyDog: Pet {
  var name: String

  func changeName(name: String) {
    self.name = name
  }
}

应该在 func changeName(name: String) 前加上关键词 mutating,表示该方法将会修改结构体中自己的成员变量。

注意,在设计协议的时候,由于protocol 可以被 class 和 struct 或者 enum 实现,故而要考虑是否用 mutating 来修饰方法。

类class中不存在这个问题,因为类可以随意修改自己的成员变量。

10. 用 Swift 实现或(||)操作

最直接的解法

func ||(left: Bool, right: Bool) –> Bool {
  if left {
    return true
  } else {
    return right
  }
}

勉强正确但并不高效,或操作的本质是当左边为真的时候,我们无须计算右边,而上面这种事先将右边默认值预先准备好,再传入进行操作。当右边值的计算十分复杂时,会造成性能上浪费,所以正确方法如下:

func ||(left: Bool, right: @autoclosure () -> Bool) –> Bool {
  if left {
    return true
  } else {
    return right()
  }
}

autoclosure可以将右边值的计算推迟到判定left为false的时候,这样就可以避免第一种方法带来的不必要的开销了。

11. 实现一个函数。输入是任一整数,输出要返回输入的整数+ 2

利用Swift 的 Currying 特性:

func add(_ num: Int) -> (Int) -> Int {
  return { val in
    return num + val
  }
}

let addTwo = add(2), addFour = add(4), addSix = add(6), addEight = add(8)

Swift中的柯里化特性是函数式编程思想的体现,他将接受多个参数的方法进行变形,并用高阶函数的方式进行处理,使整个代码更加灵活。

12. 实现一个函数。求 0 到 100(包括0和100)以内是偶数并且恰好是其他数字平方的数。

简单方法:

func evenSquareNums(from: Int, to: Int) -> [Int] {
  var res = [Int]()

  for num in from...to where num % 2 == 0{
    if (from...to).contains(num * num) {
      res.append(num * num)
    }
  }

  return res
}

evenSquareNums(from: 0, to: 100)

上面的写法正确,但不够优雅。首先这个方法完全可以利用泛型进行优化,同时可以在创建 res 数组时加上 reserveCapacity 以保证其性能。其实面对这个题目,最简单直接的写法是用函数式编程的思路,一行既可以解决问题:

(0...10).map { $0 * $0 }.filter { $0 % 2 == 0 }

Swift 有函数式编程的思想。其中 flatMap, map, reduce, filter 是其代表的方法。本题中map和 filter 的组合使用。相比于一般的 for 循环,这样的写法要更加得简洁漂亮。

相关文章

  • 一些问题

    我是一个有些轻微心理问题的人。 累了一天回家,单位提拔年轻干部却因为对领导的不合,把自己耽搁了。 本是一个很单纯的...

  • 一些问题

    昨天深夜反侧难眠。 点亮手机屏幕,看到格格突然一长串的感叹。 本想着回复,但突然意识到,三言两语是无法说清,于是标...

  • 一些问题

    这几年的遭遇让我渐渐明白,有一些问题是青春期必须要想明白的。在人生的前五分之一,三观和行为模式基本都稳定了下来,因...

  • 一些问题

    1. git clone url 和 git pull的区别 git clone是将repository整个下载...

  • 一些问题

    1.如何模拟弱网环境。 比如在测试支付,一个支付按钮 点击下去,购买一件一块钱的商品。(app余额购买,不启动支付...

  • 一些问题

    一.更新 gem 的时候出错

  • 一些问题

    一些问题 宏观方向 如果出了一本熊猫的漫画,你更期待哪种形象?(单选)动物拟人化(七龙珠的悟空)有人的部分特征但是...

  • 一些问题

    Xcode打包提示: Cannot use '@try' with Objective-C exceptions ...

  • 一些问题

    DOM 1.什么是DOM? Document Object Model,文档对象模型 2.DOM操作 JavaSc...

  • 一些问题

    String s1 = "OK"; String s2 = "O" + "K"; s1 == s2?

网友评论

      本文标题:一些问题(一)

      本文链接:https://www.haomeiwen.com/subject/xxgegktx.html