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() else {
throw DivideError.EqualZeroError
}
return a / b
}
func split(pieces: Int) throws -> Double {
return try divide(, 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多线程

1.Thread

//方式1:使用类方法,不需要手动启动线程
Thread.detachNewThreadSelector(#selector(ViewController.downloadImage), toTarget: self, with: nil)

//方式2:实例方法-便利构造器,需调用start()启动线程
let myThread = Thread(target: self, selector: #selector(thread), object: nil)
myThread.start()

线程同步:线程同步方法通过锁来实现,每个线程都只用一个锁,这个锁与一个特定的线程关联。

//定义两个线程
var thread1:Thread?
var thread2:Thread?
//定义两个线程条件,用于锁住线程
let condition1 = NSCondition()
let condition2 = NSCondition() override func viewDidLoad() {
super.viewDidLoad()
thread2 = Thread(target: self, selector: #selector(ViewController.method2), object: nil)
thread1 = Thread(target: self, selector: #selector(ViewController.method1), object: nil)
thread1?.start()
} //定义两方法,用于两个线程调用
func method1(sender:AnyObject){
for i in ..< {
print("Thread 1 running \(i)")
sleep()
if i == {
thread2?.start() //启动线程2
//本线程(thread1)锁定
condition1.lock()
condition1.wait()
condition1.unlock()
}
 }
print("Thread 1 over")
//线程2激活
condition2.signal()
} //方法2
func method2(sender:AnyObject){
for i in ..< {
print("Thread 2 running \(i)")
sleep()
if i == {
//线程1激活
condition1.signal()
//本线程(thread2)锁定
condition2.lock()
condition2.wait()
condition2.unlock()
}
}
print("Thread 2 over")
}

2.Operation和OperationQueue

2.1 直接用定义好的子类:BlockOperation。

let operation = BlockOperation(block: { [weak self] in
// 执行代码return
})
//创建一个NSOperationQueue实例并添加operation
let queue = OperationQueue()
queue.addOperation(operation)

2.2 继承Operation

  把Operation子类的对象放入OperationQueue队列中,一旦这个对象被加入到队列,队列就开始处理这个对象,直到这个对象的所有操作完成,然后它被队列释放。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//创建线程对象
let downloadImageOperation = DownloadImageOperation()
//创建一个OperationQueue实例并添加operation
let queue = OperationQueue()
queue.addOperation(downloadImageOperation)
}
}
class DownloadImageOperation: Operation {
override func main(){
let imageUrl = "http://hangge.com/blog/images/logo.png"
let data = try! Data(contentsOf: URL(string: imageUrl)!)
print(data.count)
}
}

2.3 其他方法

//队列设置并发数
queue.maxConcurrentOperationCount =
//队列取消所有线程操作
queue.cancelAllOperations()
//给operation设置回调
operation.completionBlock = { () -> Void in
print("--- operation.completionBlock ---")
}

3.GCD

3.1 创建队列

//------自己创建队列
//创建串行队列
let serial = DispatchQueue(label: "serialQueue1")
//创建并行队列
let concurrent = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent) //------系统global队列
//Global Dispatch Queue有4个执行优先级:
//.userInitiated 高
//.default 正常
//.utility 低
//.background 非常低的优先级(这个优先级只用于不太关心完成时间的真正的后台任务)
let globalQueue = DispatchQueue.global(qos: .default) //------系统主线程队列
//因为主线程只有一个,所有这自然是串行队列。一起跟UI有关的操作必须放在主线程中执行。
let mainQueue = DispatchQueue.main

3.2 添加任务到队列

async异步追加Block块(async函数不做任何等待)

DispatchQueue.global(qos: .default).async {
//处理耗时操作的代码块... //操作完成,调用主线程来刷新界面
DispatchQueue.main.async {
print("main refresh")
}
}

sync同步追加Block块

//同步追加Block块,与上面相反。在追加Block结束之前,sync函数会一直等待,等待队列前面的所有任务完成后才能执行追加的任务。
//添加同步代码块到global队列
//不会造成死锁,但会一直等待代码块执行完毕
DispatchQueue.global(qos: .default).sync {
print("sync1")
}
print("end1") //添加同步代码块到main队列
//会引起死锁
//因为在主线程里面添加一个任务,因为是同步,所以要等添加的任务执行完毕后才能继续走下去。但是新添加的任务排在
//队列的末尾,要执行完成必须等前面的任务执行完成,由此又回到了第一步,程序卡死
DispatchQueue.main.sync {
print("sync2")
}

3.3 暂停或者继续队列

  这两个函数是异步的,而且只在不同的blocks之间生效,对已经正在执行的任务没有影响。suspend()后,追加到Dispatch Queue中尚未执行的任务在此之后停止执行。而resume()则使得这些任务能够继续执行。

//创建并行队列
let conQueue = DispatchQueue(label: "concurrentQueue1", attributes: .concurrent)
//暂停一个队列
conQueue.suspend()
//继续队列
conQueue.resume()

3.4 asyncAfter 延迟调用

  asyncAfter 并不是在指定时间后执行任务处理,而是在指定时间后把任务追加到queue里面。因此会有少许延迟。注意,我们不能(直接)取消我们已经提交到 asyncAfter 里的代码。

//延时2秒执行
DispatchQueue.global(qos: .default).asyncAfter(deadline: DispatchTime.now() + 2.0) {
print("after!")
}

  如果需要取消正在等待执行的Block操作,我们可以先将这个Block封装到DispatchWorkItem对象中,然后对其发送cancle,来取消一个正在等待执行的block。

//将要执行的操作封装到DispatchWorkItem中
let task = DispatchWorkItem { print("after!") }
//延时2秒执行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + , execute: task)
//取消任务
task.cancel()

3.5 DispatchWorkItem

  DispatchWorkItem可以将任务封装成DispatchWorkItem对象。

  可以调用workItem的perform()函数执行任务,也可以将workItem追加到DispatchQueue或DispatchGroup中。以上所有传block的地方都可换成DispatchWorkItem对象。
DispatchQueue还可以使用notify函数观察workItem中的任务执行结束,以及通过cancel()函数取消任务。

  另外,workItem也可以像DispatchGroup一样调用wait()函数等待任务完成。需要注意的是,追加workItem的队列或调用perform()所在的队列不能与调用workItem.wait()的队列是同一个队列,否则会出现线程死锁。

// 初始化方法
init(qos: DispatchQoS, flags: DispatchWorkItemFlags, block: () -> Void) let workItem = DispatchWorkItem.init {
print("执行任务")
}
3.6 DispatchQueue.concurrentPerform
  sync函数和Dispatch Group的关联API。会按指定次数异步执行任务,并且会等待指定次数的任务全部执行完成,即会阻塞线程。建议在子线程中使用。
DispatchQueue.global().async {
DispatchQueue.concurrentPerform(iterations: ) { (i) in
print("执行任务\(i+1)")
}
print("任务执行完成")
}

3.7 DispatchSemaphore

  信号量:用于控制访问资源的数量。比如系统有两个资源可以被利用,同时有三个线程要访问,只能允许两个线程访问,第三个会等待资源被释放后再访问。

  信号量的初始化方法:DispatchSemaphore.init(value: Int),value表示允许访问资源的线程数量,当value为0时对访问资源的线程没有限制。

  信号量配套使用wait()函数与signal()函数控制访问资源。

  wait函数会阻塞当前线程直到信号量计数大于或等于1,当信号量大于或等于1时,将信号量计数-1, 然后执行后面的代码。signal()函数会将信号量计数+1。

  信号量是GCD同步的一种方式。前面介绍过的DispatchWorkItemFlags.barrier是对queue中的任务进行批量同步处理,sync函数是对queue中的任务单个同步处理,而    

  DispatchSemaphore是对queue中的某个任务中的某部分(某段代码)同步处理。此时将DispatchSemaphore.init(value: Int)中的参数value传入1。

var arr = [Int]()
let semaphore = DispatchSemaphore.init(value: ) // 创建信号量,控制同时访问资源的线程数为1
for i in ... {
DispatchQueue.global().async {
/*
其他并发操作
*/
semaphore.wait() // 如果信号量计数>=1,将信号量计数减1;如果信号量计数<1,阻塞线程直到信号量计数>=1
arr.append(i)
semaphore.signal() // 信号量计加1
/*
其他并发操作
*/
}
}

Swift面试题的更多相关文章

  1. 若干道Swift面试题

    1,说说你认识的Swift是什么?Swift是苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C共同运行于MAC OS和iOS平台,用于搭建基于苹果平台的应用程序. ...

  2. 【面试必备】Swift 面试题及其答案

    初级 问题1- Swift 1.0 or later 什么是optional类型,它是用来解决什么问题的? 答案:optional类型被用来表示任何类型的变量都可以表示缺少值.在Objective-C ...

  3. iOS面试题大全-点亮你iOS技能树

    所有的内容大部分来自于网络的搜集,所以我不是一个创造者,而是一个搬运工.我尽量把题目,尤其是参考答案的出处列明.若有任何疑问,建议,意见,请联系我. 第一部分面试题来源于iOS-Developer-I ...

  4. 2018出炉50道iOS面试题

    基础: 1.如何令自己所写的对象具有拷贝功能? 若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议.如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying与 ...

  5. iOS相关教程

    Xcode Xcode 7中你一定要知道的炸裂调试神技 Xcode 6和Swift中应用程序的国际化和本地化 iOS新版本 兼容iOS 10 资料整理笔记 整理iOS9适配中出现的坑(图文) Swif ...

  6. 【面试必备】Swift&nbsp;面试题及其答案

    原文:Swift Interview Questions and Answers 原作者:Antonio Bello 原作者介绍: Antonio 拥有丰富的编程经验.他开始编程的时候,内存单位还是 ...

  7. 李洪强经典面试题152-Runtime

    李洪强经典面试题152-Runtime   Runtime Runtime是什么 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码, ...

  8. iOS之2016面试题一

    序言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去宝库公司的笔试题,由笔者整理并提供笔者个人参考答 ...

  9. 李洪强iOS经典面试题139-Swift

    李洪强iOS经典面试题139-Swift Swift 网上有很多Swift的语法题,但是Swift现在语法还未稳定,所以在这里暂时不贴出语法题,可以自行搜索. Swift和Objective-C的联系 ...

随机推荐

  1. Python Selenium Webdriver常用方法总结

    Python Selenium Webdriver常用方法总结 常用方法函数 加载浏览器驱动: webdriver.Firefox() 打开页面:get() 关闭浏览器:quit() 最大化窗口: m ...

  2. excel打开csv 出现乱码

    现在做舆情分析的相关项目,在数据处理的时候,发现了一个问题.将数据写入到csv文件,用excel打开(默认)就会出现乱码,如果将数据写入到.xlsx文件就不会出现乱码,因为csv是通用格式,所以我猜想 ...

  3. NPOI导出EXCEL样式

    public void Export(DataRequest<ExportModel> request, DataResponse<dynamic> response) { t ...

  4. 基于Wiremock创建Mock Service平台(转)

    本文链接:https://blog.csdn.net/liuchunming033/article/details/52399397                                   ...

  5. 映美FP-530K+打印发票的各种经验

    本人虽然写了很多lodop博文,但是程序开发一般都是用虚拟打印机测试,实际打印机打印中还可能存在各种问题,毕竟理论都不如实践能获得更多的打印经验.当个人使用过的打印机不多,只有映美FP-530K+,用 ...

  6. docker下安装nginx并实现https访问

    一.启动容器 docker run --detach --name wx-nginx -p 443:443 -p 80:80 -v /home/nginx/data:/usr/share/nginx/ ...

  7. qt creator的使用总结

    每一个不曾起舞的日子,都是对生命的辜负----------尼采 1.  HELP功能: 1)有时点击"HELP"之后,对应的页面里没有左侧的搜索栏.比较理想的状态是将左侧导航栏划分 ...

  8. 【python小记】python操作excel文件

    题记: 最近因为工作需要,学习了python,瞬间对这个轻松快捷的语给吸引了,以前只知道js脚本是写网页的,没有想到python这个脚本语言的应用范围可以这么广泛,现在做一些简单或稍微复杂的操作,基本 ...

  9. 【面试】C++类中的相关函数【构造,拷贝构造,析构,友元】

    构造函数:值的初始化,可带参数,无返回值,可重载,可存在多个 析构函数:释放对象内存空间,无参数,无返回值,不可重载,只能存在一个 拷贝构造函数:拷贝对象,其形参必须是引用 1.空类会默认添加哪些东西 ...

  10. Java基础笔试练习(十一)

    1.下面的方法,当输入为2的时候返回值是多少? public static int getValue(int i) { int result = 0; switch (i) { case 1: res ...