程序员社区

swift小问题

1、struct和class的区别

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

class有这几个功能struct没有的:

  • class可以继承,这样子类可以使用父类的特性和方法
  • 类型转换可以在runtime的时候检查和解释一个实例的类型
  • 可以用deinit来释放资源
  • 一个类可以被多次引用

struct也有这样几个优势:

  • 结构较小,适用于复制操作,相比于一个class的实例被多次引用更加安全。
  • 无须担心内存memory leak或者多线程冲突问题

顺便提一下,array在swift中是用struct实现的。Apple重写过一次array,然后复制就是深度拷贝了。猜测复制是类似参照那样,通过栈上指向堆上位置的指针来实现的。而对于它的复制操作,也是在相对空间较为宽裕的堆上来完成的,所以性能上还是不错的。

2、map、filter、reduce函数

  • map 可以对数组中的每一个元素做一次处理
  • filter:过滤,可以对数组中的元素按照某种规则进行一次过滤
  • reduce:计算,可以对数组的元素进行计算

3、map 和 flatMap

  • map:是对原对象所有元素进行一对一转换处理,中间不会跳过或遗漏,包括nil元素,map 方法接受一个闭包作为参数, 然后它会遍历整个 numbers 数组,并对数组中每一个元素执行闭包中定义的操作。 相当于对数组中的所有元素做了一个映射。
  • flatMap:更灵活,可变换维度,也能够自动解包,所以当我们对不符合元素,返回nil,最终的结果是过滤掉nil的,从而能够实现过滤。

4、写入时复制 copy-on-write

swift的数组是值类型,值类型的一个特点是在传递和赋值时进行复制。swift使用了copy-on-write来避免频繁复制带来的额外开销。只有当多个对象指向相同的资源,其中一个对象尝试修改资源内容时,copy才会触发。

Swift针对标准库中的集合类型(Array、Dictionary、Set)进行优化。当变量指向的内存空间并没有发生改变,进行拷贝时,只会进行浅拷贝。只有当值发生改变时才会进行深拷贝。

Array、Dictinary、Set每次进行修改前,都会通过类似isUniquelyReferencedNonObjC函数进行判断,判断是否是唯一的引用(即引用计数器为1)。若不为1,则创建新的类型值并返回。若是唯一的则直接赋值。

写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

5、获取当前代码的函数名和行号

字面量 类型
#file String 所在的文件名
#line Int 所在的行数
#column Int 所在的列数
#function String 所在的函数的名字
例如:
// 函数名
func logFunctionName(string: String = #function) {
    print(string)
}
func myFunction() {
    logFunctionName()
}
myFunction() // 打印

// 行数
func logFunctionName(count: Int = #line) {
    print(count)
}
func myFunction() {
    logFunctionName()
}
myFunction() // 打印


也可以简单如下使用
func myFunction() {
    print("\(#function)")
}
myFunction() // 打印

func myFunction() {
    print(“\(#line)”)
}
myFunction() // 打印

6、如何声明一个只能被类 conform(遵循) 的 protocol

协议的继承列表中,通过添加 class 关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。class 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 这里是类类型专属协议的定义部分
}

7、defer 使用场景

defer 语句块中的代码, 会在当前作用域结束前调用, 常用场景如异常退出后, 关闭数据库连接
func someQuery() -> ([Result], [Result]){
    let db = DBOpen("xxx")
    defer {
        db.close()
    }
    guard results1 = db.query("query1") else {
        return nil
    }
    guard results2 = db.query("query2") else {
        return nil
    }
    return (results1, results2)
}

需要注意的是, 如果有多个 defer, 那么后加入的先执行
func someDeferFunction() {
    defer {
        print("\(#function)-end-1-1")
        print("\(#function)-end-1-2")
    }
    defer {
        print("\(#function)-end-2-1")
        print("\(#function)-end-2-2")
    }
    if true {
        defer {
            print("if defer")
        }
        print("if end")
    }
    print("function end")
}
someDeferFunction()
// 输出
// if end
// if defer
// function end
// someDeferFunction()-end-2-1
// someDeferFunction()-end-2-2
// someDeferFunction()-end-1-1
// someDeferFunction()-end-1-2

8、什么时候使用 final

  1. 类或者方法的功能确实已经完备了,通常是一些辅助性质的工具类或者方法,比如MD5加密类这种,算法都十分固定,我们基本不会再继承和重写
  2. 避免子类继承和修改造成危险
  3. 为了让父类中某些代码一定会执行

9、public 和 open 的区别

open

  • open 修饰的 class 在 Module 内部和外部都可以被访问和继承
  • open 修饰的 func 在 Module 内部和外部都可以被访问和重载(override)

public

  • public 修饰的 class 在 Module 内部可以访问和继承,在外部只能访问
  • public 修饰的 func 在 Module 内部可以被访问和重载(override),在外部只能访问

10、dynamic 的作用

由于 swift 是一个静态语言, 所以没有 Objective-C 中的消息发送这些动态机制, dynamic 的作用就是让 swift 代码也能有 Objective-C 中的动态机制, 常用的地方就是 KVO 了, 如果要监控一个属性, 则必须要标记为 dynamic。
首先需要知道的是, KVO, KVC 都是Objective-C 运行时的特性, Swift 是不具有的, 想要使用, 必须要继承 NSObject, 自然, 继承都没有的结构体也是没有 KVO, KVC 的, 这是前提.。
例如, 下面的这段错误代码:

class SimpleClass {
    var someValue: String = "123"
}
//SimpleClass().setValue("456", forKey: "someValue") // 错误, 必须要继承自 NSObject
KVC

Swift 下的 KVC 用起来很简单, 只要继承 NSObject 就行了.

class KVCClass :NSObject{
    var someValue: String = "123"
}
let kvc = KVCClass()
kvc.someValue // 123
kvc.setValue("456", forKey: "someValue")
kvc.someValue // 456
KVO

KVO 就稍微麻烦一些了,由于 Swift 为了效率, 默认禁用了动态派发, 因此想用 Swift 来实现 KVO, 我们还需要做额外的工作, 那就是将想要观测的对象标记为 dynamic.

class KVOClass:NSObject {
    dynamic var someValue: String = "123"
    var someOtherValue: String = "abc"
}

class ObserverClass: NSObject {
    func observer() {
        let kvo = KVOClass()
        kvo.addObserver(self, forKeyPath: "someValue", options: .new, context: nil)
        kvo.addObserver(self, forKeyPath: "someOtherValue", options: .new, context: nil)
        kvo.someValue = "456"
        kvo.someOtherValue = "def"
        kvo.removeObserver(self, forKeyPath: "someValue")
        kvo.removeObserver(self, forKeyPath: "someOtherValue")
    }
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("\(keyPath!) change to \(change![.newKey] as! String)")
    }
}
ObserverClass().observer()
这段代码只会输出someValue change to 456

11、什么时候使用 @objc

  • @objc 用途是为了在 Objective-C 和 Swift 混编的时候, 能够正常调用 Swift 代码. 可以用于修饰类, 协议, 方法, 属性.
  • 常用的地方是在定义 delegate 协议中, 会将协议中的部分方法声明为可选方法, 需要用到@objc
  • 在协议中使用 optional 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上@objc属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议

12、Optional(可选型) 是用什么实现的

Optional 是一个泛型枚举大致定义如下:

enum Optional<Wrapped> {
  case none
  case some(Wrapped)
}
除了使用 let someValue: Int? = nil 之外, 还可以使用let optional1: Optional<Int> = nil 来定义

13、什么是高阶函数

一个函数如果可以以某一个函数作为参数, 或者是返回值, 那么这个函数就称之为高阶函数, 如 map, reduce, filter

14、如何解决引用循环

  1. 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的
  2. delegate 使用 weak 属性
  3. 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned 修饰 (在引用对象的生命周期内,如果它可能为nil,那么就用weak引用。反之,当你知道引用对象在初始化后永远都不会为nil就用unowned)

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

  • static 和 class都是用来指定类方法
  • class关键字指定的类方法 可以被 override
  • static关键字指定的类方法 不能被 override
赞(0) 打赏
未经允许不得转载:IDEA激活码 » swift小问题

一个分享Java & Python知识的社区