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
- 类或者方法的功能确实已经完备了,通常是一些辅助性质的工具类或者方法,比如MD5加密类这种,算法都十分固定,我们基本不会再继承和重写
- 避免子类继承和修改造成危险
- 为了让父类中某些代码一定会执行
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、如何解决引用循环
- 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的
- delegate 使用 weak 属性
- 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned 修饰 (在引用对象的生命周期内,如果它可能为nil,那么就用weak引用。反之,当你知道引用对象在初始化后永远都不会为nil就用unowned)
15、定义静态方法时关键字 static 和 class 有什么区别
- static 和 class都是用来指定类方法
- class关键字指定的类方法 可以被 override
- static关键字指定的类方法 不能被 override
网友评论