Swift面试题

柔情痞子 提交于 2019-12-02 18:37:40

class 和 struct 的区别

1.struct是值类型,class是引用类型。

值类型的变量直接包含它们的数据,对于值类型都有它们自己的数据副本,因此对一个变量操作不可能影响另一个变量。

引用类型的变量存储对他们的数据引用,因此后者称为对象,因此对一个变量操作可能影响另一个变量所引用的对象。

2.二者的本质区别:

struct是深拷贝,拷贝的是内容;class是浅拷贝,拷贝的是指针。

3.property的初始化不同:

class 在初始化时不能直接把 property 放在 默认的constructor 的参数里,而是需要自己创建一个带参数的constructor;而struct可以,把属性放在默认的constructor 的参数里。

4.变量赋值方式不同:

struct是值拷贝;class是引用拷贝。

5.immutable变量:

swift的可变内容和不可变内容用var和let来甄别,如果初始为let的变量再去修改会发生编译错误。struct遵循这一特性;class不存在这样的问题。

6.mutating function: 

struct 和 class 的差別是 struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。

7.继承: 

struct不可以继承,class可以继承。

8.struct比class更轻量:

struct分配在栈中,class分配在堆中。

open与public的区别

public:可以别任何人访问,但是不可以被其他module复写和继承。

open:可以被任何人访问,可以被继承和复写。

swift中struct作为数据模型

优点:

1.安全性:因为 Struct 是用值类型传递的,它们没有引用计数。

2.内存:由于他们没有引用数,他们不会因为循环引用导致内存泄漏。

    速度:值类型通常来说是以栈的形式分配的,而不是用堆。因此他们比 Class 要快很多!

3.拷贝:Objective-C 里拷贝一个对象,你必须选用正确的拷贝类型(深拷贝、浅拷贝),而值类型的拷贝则非常轻松!

4.线程安全:值类型是自动线程安全的。无论你从哪个线程去访问你的 Struct ,都非常简单。

缺点:

1.Objective-C与swift混合开发:OC调用的swift代码必须继承于NSObject。

2.继承:struct不能相互继承。

3.NSUserDefaults:Struct 不能被序列化成 NSData 对象

Swift代码复用的方式有哪些?

1.继承

2.在swift 文件里直接写方法,相当于一个全局函数

3.extension 给类直接扩展方法

Array、Set、Dictionary 三者的区别

Set:是用来存储相同类型、没有确定顺序、且不重复的值的集

Array:是有序数据的集

Dictionary:是无序的键值对的集

map、filter、reduce 的作用

map : 映射 ,将一个元素根据某个函数 映射 成另一个元素(可以是同类型,也可以是不同类型)

filter : 过滤 , 将一个元素传入闭包中,如果返回的是false , 就过滤掉

reduce :先映射后融合 , 将数组中的所有元素映射融合在一起。

map 与 flatmap 的区别

map:作用于数组中的每个元素,闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组。

flatMap:功能和map类似,区别在于flatMap可以去nil,能够将可选类型(optional)转换为非可选类型(non-optionals),把数组中存有数组的数组 一同打开变成一个新的数组(数组降维)

copy on write

写时复制:每当数组被改变,它首先检查它对存储缓冲区 的引用是否是唯一的,或者说,检查数组本身是不是这块缓冲区的唯一拥有者。如果是,那么 缓冲区可以进行原地变更;也不会有复制被进行。不过,如果缓冲区有一个以上的持有者 (如本 例中),那么数组就需要先进行复制,然后对复制的值进行变化,而保持其他的持有者不受影响。

在Swift提供一个函数isKnownUniquelyReferenced,能检查一个类的实例是不是唯一的引用,如果是,说明实例没有被共享

eg: isKnownUniquelyReferenced(&object)

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

函数名:#function 

行号:#line 

文件名:#file

guard、defer、where

defer:defer所声明的 block 会在当前代码执行退出后被调用,如果有多个 defer, 那么后加入的先执行

guard:可以理解为拦截,凡是不满足 guard 后面条件的,都不会再执行下面的代码。

where:在Swift语法里where关键字的作用跟SQL的where一样, 即附加条件判断。where关键字可以用在集合遍历、switch/case、协议中; Swift3时if let和guard场景的where已经被Swift4的逗号取代

String 与 NSString 的关系与区别

String为Swift的Struct结构,值类型;NSString为OC对象,引用类型,能够互相转换

throws 和 rethrows 的用法与作用

throws 用在函数上, 表示这个函数会抛出错误.

有两种情况会抛出错误, 一种是直接使用 throw 抛出, 另一种是调用其他抛出异常的函数时, 直接使用 try xx 没有处理异常.

enum DivideError: Error {
    case EqualZeroError;
}
func divide(_ a: Double, _ b: Double) throws -> Double {
    guard b != Double(0) else {
        throw DivideError.EqualZeroError
    }
    return a / b
}
func split(pieces: Int) throws -> Double {
    return try divide(1, Double(pieces))
}

rethrows 与 throws 类似, 不过只适用于参数中有函数, 且函数会抛出异常的情况, rethrows 可以用 throws 替换, 反过来不行

func processNumber(a: Double, b: Double, function: (Double, Double) throws -> Double) rethrows -> Double {
    return try function(a, b)
}

try?和 try!

这两个都用于处理可抛出异常的函数, 使用这两个关键字可以不用写 do catch.

区别在于, try? 在用于处理可抛出异常函数时, 如果函数抛出异常, 则返回 nil, 否则返回函数返回值的可选值

而 try! 则在函数抛出异常的时候崩溃, 否则则返会函数返回值, 相当于(try? xxx)!

associatedtype 的作用

简单来说就是 protocol 使用的泛型

Swift 下的 KVO , KVC

KVO, KVC 都是Objective-C 运行时的特性, Swift 是不具有的, 想要使用, 必须要继承 NSObject, 自然, 继承都没有的结构体也是没有 KVO, KVC 的.

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

KVO:KVO 就稍微麻烦一些了,由于 Swift 为了效率, 默认禁用了动态派发, 因此想用 Swift 来实现 KVO, 除了继承NSObject,还需要将想要观测的对象标记为 dynamic

@objc

OC 是基于运行时,遵循了 KVC 和动态派发,而 Swift 是静态语言,为了追求性能,在编译时就已经确定,而不需要在运行时的,在 Swift 类型文件中,为了解决这个问题,需要暴露给 OC 使用的任何地方(类,属性,方法等)的生命前面加上 @objc 修饰符

如果用 Swift 写的 class 是继承 NSObject 的话, Swift 会默认自动为所有非 private 的类和成员加上@objc

自定义下标获取

实现 subscript 即可, 索引除了数字之外, 其他类型也是可以的

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]
    }
}

lazy 的作用

懒加载, 当属性要使用的时候, 才去完成初始化

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

需要实现OptionSet, 一般使用 struct 实现. 由于 OptionSet 要求有一个不可失败的init(rawValue:) 构造器, 而 枚举无法做到这一点(枚举的原始值构造器是可失败的, 而且有些组合值, 是没办法用一个枚举值表示的)

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]

static和class的区别

static 可以在类、结构体、或者枚举中使用。而 class 只能在类中使用。

static 可以修饰存储属性,static 修饰的存储属性称为静态变量(常量)。而 class 不能修饰存储属性。

static 修饰的计算属性不能被重写。而 class 修饰的可以被重写。

static 修饰的静态方法不能被重写。而 class 修饰的类方法可以被重写。

class 修饰的计算属性被重写时,可以使用 static 让其变为静态属性。

class 修饰的类方法被重写时,可以使用 static 让方法变为静态方法

class关键字指定的类方法可以被子类重写,但是用static关键字指定的类方法是不能被子类重写的

mutating

mutating用于函数的最前面,用于告诉编译器这个方法会改变自身.

swift增强了结构体和枚举的使用,结构体和枚举也可以有构造方法和实例方法,但结构体和枚举是值类型,如果我们要在实例方法中修改当前类型的实例属性的值或当前对象实例的值,必须在func前面添加mutating关键字,表示当前方法将会将会修改它相关联的对象实例的实例属性值.

协议中,当在结构体或者枚举实现协议方法时,若对自身属性作修改,需要将协议的方法声明为mutating,对类无影响.

在扩展中,同样若对自身属性作修改,需要将方法声明为mutating

swift多线程-Thread

1.类方法创建线程(无需手动启动线程,自动执行) 

2.实例方法创建(需手动开启,执行start方法,才会运行线程) 

3.Thread常用方法

 

 

 

 

 

 

swift多线程-GCD

1.DispatchQueue

init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!