今天下午去面试去面试一家初创公司,而后又接到到了丁香园的电话面试,这篇blog记录一下面试的一些问题,有的回答的还行,有点感受不太好,主要是有些英文单词说的太low了估计被鄙视了吧,下面给你们总结一下面试的一些问题,有些回答是摘要一些大神blog的出处,都有给原连接,但愿见谅~~ios
这个问得其实不是很难,主要看你了不了解了,我由于了解一些后台的东西,因此回答的还行,下面我给你们看两幅图片你们就基本了解了:git
总结一下,其实http请求就是发送和接受报文,报文的具体格式就如上图所示,具体由三部分构成,GET 和 POST比较明显的不一样就是请求方式和参数的位置不一样,其余原理的不一样你们能够去下面的连接去看:github
其中请求头里面能够放不少参数,好比报文的大小啊,啥的一些参数,具体能够百度,这里就不展开了。web
下面给两个大神们推荐的连接,你们能够自行查看:面试
这个问题回答的就比较菜了,程序比较复制,当时特地记了一下,没想到面试的时候仍是有点蒙,回答的很是菜,如今在普及一下,下面是一篇讲的很详细的blog地址,你们能够去原地址去看,我下面也简单总结一下:objective-c
对称加密是指加密和解密使用相同密钥的加密算法。它要求发送方和接收方在安全通讯以前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人均可以对他们发送或接收的消息解密,因此密钥的保密性对通讯相当重要。算法
对称加密算法的优、缺点:数据库
优势:算法公开、计算量小、加密速度快、加密效率高。编程
缺点:
1)交易双方都使用一样钥匙,安全性得不到保证;swift
2)每对用户每次使用对称加密算法时,都须要使用其余人不知道的唯一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增加,密钥管理成为用户的负担。
3)能提供机密性,可是不能提供验证和不能否认性。
这种加密或许理解起来比较困难,这种加密指的是能够生成公钥和私钥。凡是公钥加密的数据,公钥自身不能解密,而须要私钥才能解密;凡是私钥加密的数据,私钥不能解密,须要公钥才能解密。这种算法事实上有不少,经常使用的是RSA,其基于的数学原理是两个大素数的乘积很容易算,而拿到这个乘积去算出是哪两个素数相乘就很复杂了,具体原理有兴趣能够自行研究。
非对称加密相比对称加密更加安全,但也存在两个明显缺点:
1)CPU计算资源消耗很是大。一次彻底TLS握手,密钥交换时的非对称解密计算量占整个握手过程的90%以上。而对称加密的计算量只至关于非对称加密的0.1%,若是应用层数据也使用非对称加解密,性能开销太大,没法承受。
2)非对称加密算法对加密内容的长度有限制,不能超过公钥长度。好比如今经常使用的公钥长度是2048位,意味着待加密内容不能超过256个字节。
因此公钥加密目前只能用来做密钥交换或者内容签名,不适合用来作应用层传输内容的加解密。
首先服务器端用非对称加密(RSA)产生公钥和私钥。而后把公钥发给客 户端,路径或许有人会截取,可是没有用,由于用公钥加密的文件只有私钥能够解密,而私钥永远都不会离开服务器的。当公钥到达客户端以后,客户端会用对称加密产生一个秘钥而且用公钥来加密发送给服务器端,这个秘钥就是之后用来通讯的钥匙。这样服务器端收到公钥加密的秘钥时就能够用私钥来解公钥从而得到秘钥。这样的话客户端和服务器端都得到了秘钥,信息交流相对是安全的。流程图以下:
听起来确实是挺安全的,但实际上,还有一种更恶劣的攻击是这种方法无 法防范的,这就是传说中的“中间人攻击”。在身份认证的过程当中,出现了一个“中间人”拦截咱们的信息,他有意想要知道大家的消息。咱们将这个中间人称为M。当服务器第一次给客户端发送公钥的时候,途径M。M知道你要进行密钥交换了,它把公钥扣了下来,伪装本身是客户端,伪造了一个伪秘钥(对称加密产生的),而后用服务器发来的公钥加密了伪秘钥发还给服务器,这样服务器觉得和客户端完成了密钥交换,实际上服务器是和M完成了密钥交换(得到了伪秘钥)。同时M假扮成服务器自行用非对称加密产生伪公钥和伪私钥,与客户端进行秘钥交换,拿到客户端发送过来的秘钥。如今客户端拿着秘钥,M拿着秘钥和为伪秘钥,服务器拿着伪秘钥,整个交流的过程就是:
还有不少你们直接去大神的blog去看吧,写的很详细,我这就点到为止了~~
当听到这么问题的时候仍是有点仓促的,隐约记得是经过二进制的头部的标识来区分的,当时也不太肯定就含糊的回答了一下,说是经过二进制文件的头部标识来区分的,看面试官的样子不是很满意,回答百度学习一波,百度结果以下所示,附带原连接:
能够经过二进制头识别文件类型,可使用UE或者WinHex软件打开:
1). JPEG/JPG
2). TGA
3). PNG
4). GIF
5). BMP
6). PCX
7). TIFF
8). ICO
9). CUR
10). IFF
11). ANI
以上是一些文件的区别方式,回答的总方向仍是对的,可能回答的不够好,下次就知道了。
这个问题比较尴尬,由于英文不太好,加上平时用的也很少,回答的比较吞吞吐吐,就说了NSLock
、@synchronized
和dispatch的semaphore
,其中有些单词的读法还不太准,想一想就很尴尬,下面大概总结一下,有一下几种:
下面总结一下,说实话太多有点记不过来了 - . -,附带详细的原文地址:
各类线程锁 | 使用场景和简单介绍 |
---|---|
@synchronized |
适用线程很少,任务量不大的多线程加锁 |
NSLock |
比较经常使用的一种锁,性能通常 |
dispatch_semaphore_t |
使用信号来作加锁,性能很好 |
NSCondition |
使用其作多线程之间的通讯调用不是线程安全的 |
NSConditionLock |
单纯加锁性能很是低,比NSLock低不少,可是能够用来作多线程处理不一样任务的通讯调用 |
NSRecursiveLock |
递归锁的性能出奇的高,可是只能做为递归使用,因此限制了使用场景 |
NSDistributedLock |
由于是MAC开发的,就不讨论了 |
POSIX(pthread_mutex) |
底层的api,复杂的多线程处理建议使用,而且能够封装本身的多线程 |
OSSpinLock |
性能也很是高,惋惜出现了线程问题 |
再总结一下,总的意思就是通常用dispatch_semaphore_t
就好了,再简单点用NSLock
,另外带一个swift
出的一个线程锁的方式:
func synchronized(lock: AnyObject, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}复制代码
这个问题我是接着上一个问题以后回答的,感受线程安全主要是数据竞争带来的,下面简单讲解一下:
线程安全的代码能够从多个线程或并发任务安全地调用,而不会形成任何问题(数据损坏,系统崩溃等)。例如当你多线程编程时,你用let定义一个数组,由于它是只读的,你能在同一时间不一样线程去使用它,而不会形成线程安全的问题,然而当你用var定义一个数组时就不同了,它不是线程安全的,当多个线程在同一时间访问和修改数组时会产生不可预知的结果。
这个问题回答的通常般吧,说了一下简单的构造和实现,而后让我说具体类的时候有点心累了,由于确实记得不是很清楚了,下面简单总结一下一些主要的类:
SDWebImageManager
SDWebImageCombinedOperation
SDImageCache
SDWebImageDownloader
这是一些简单的类,你们想要详细了解能够去这篇文章去看,很是详细!!!
跟上个问题同样,简单的回答了一下,都怪本身没仔细专研过这些,只是简单看过,停留在应用层面上,下面一样简单介绍一下,附带大神blog地址吧:
Manager
SessionDelegate
ResponseSerialization
URLStringConvertible
这是一些简单的类,你们想要详细了解能够去这篇文章去看,很是详细!!!
这个幸亏用过了,不过也没深刻过,就简单抽象的讲了一下响应式编程的思想,而后从应用使用方面讲解了一下:RxSwift
的目的是让让数据/事件流和异步任务可以更方便的序列化处理,可以使用swift
进行响应式编程;让后让我说一下RxSwift
里面有哪些Subjects
,这个就比较尴尬了,这让我只用过PublishSubject
和Driver
的人情何以堪,下面一样列一下Subjects
列表和大神地址:
PublishSubject
ReplaySubject
BehaviorSubject
Variable
Driver
下面是大神blog,有详细介绍你们能够去阅读~~~
这个也简单用过,也是没往深刻研究,也是大概说了一下使用过程,和多线程数据共享的坑,首先realm
是一个跨平台移动数据库引擎,支持iOS
、OS X
(Objective-C
和Swift
)以及Android
,核心数据引擎C++
打造,并非创建在SQLite
之上的ORM
。
废话很少说,直接上代码:
let person = Person(name: "Jane")
try! realm.write {
realm.add(person)
}
// 如下是跨线程必要的操做,先建一个Reference
let personRef = ThreadSafeReference(to: person)
// 而后在须要返回数据的线程里面去resolve
DispatchQueue(label: "background").async {
let realm = try! Realm()
guard let person = realm.resolve(personRef) else {
// person 已被删除
return
}
try! realm.write {
person.name = "Jane Doe"
}
}复制代码
这里是官方中文文档,你们能够去看看,很是详细~~~
runtimer
和runloop
由于看过一篇文章写的特别好,有必定了解,说了runtime
的一些主要功能和应用的地方,下面简单介绍一下:
RunTime
简称运行时;其中用的比较多的就是用类目给某个类动态添加属性。
RunLoop
简单来讲就是事件循环,保持APP一直处于存活方式的一种机制,让线程能随时处理事件但并不退出,下面有一篇超级棒的RunLoop
文章给你们介绍一下,我这就不展开说了,了解RunLoop
看那篇文章足够了。
这个回答的就比较轻松了,下面随便列几个吧,你们有其余的能够补充一下:
NSUserDefaults
plist
NSKeyedArchiver
SQL
coredata
realm
主要经过NSURLCache
对请求的数据进行缓存,具体实现能够去这个github上去查看~~~~
详情看这篇blog,这里简单陈述一下,其实在iOS9
出了一个方法,调用一下就清除了:
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
//// Date from
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
//// Execute
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
// Done
}];复制代码
主要用到的类有NSHTTPCookie
,你们一样也能够去大神的blog去查看~~~~
这个问题回答的不太好,说实话当时连续面试了两家,闹着已经有点蒙了,这里借用喵神翻译的一本书上的原话来描述一下:
autorelease
它会将接受该消息的对象放到一个预先创建的自动释放池 (auto release pool
) 中,并在 自动释放池收到 drain
消息时将这些对象的引用计数减一,而后将它们从池子中移除 (这一过程形象地称为“抽干池子”)。”
【摘录来自: 王巍 (onevcat). “Swifter - Swift 必备 Tips (第三版)”。 iBooks. 】
实现原理的话大概就是被autorelease
标记的类会被加入一个池子,当这个池子drain
时里面的引用计数会减1。
说实话其实这个问题当时比较吵,听的不是很清楚,问了好几遍问题,挺尴尬的,下面简单介绍一下吧,由于这个在swift
中用的比较多:
extension
在swift
中相似oc
的类目,能够扩展方法,计算属性,不能添加存储属性;extension
让类实现某个协议,通常这个用的也比较多,好比实现Comparable
这个协议;extension
对协议进行扩展,添加默认实现,属于黑魔法吧,很是好用。前两点在面试的时候都又提到,最后一点压根忘了,本身的面试发挥真不是通常的差- . -
幸亏以前看了喵神翻译的【Swift进阶】 ,受益颇深,下面一样借用喵神的话给你们简单描述一下,你们能够去买这本书,仍是挺划算的,下面大量复制喵神书里面的内容,请见谅:
在 Swift 标准库中,像是 Array,Dictionary 和 Set 这样的集合类型是经过一种叫作写时复制 (copy-on-write) 的技术实现的。咱们这里有一个整数数组:
var x = [1,2,3]
var y = x复制代码
若是咱们建立了一个新的变量 y,而且把 x 赋值给它时,会发生复制,如今 x 和 y 含有的是独立的结构体。在内部,这些 Array 结构体含有指向某个内存的引用。这个内存就是数组中元素所存储的位置,它们位于堆 (heap) 上。在这个时候,两个数组的引用指向的是内存中同一个位置,这两个数组共享了它们的存储部分。不过,当咱们改变 x 的时候,这个共享会被检测到,内存将会被复制。这样一来,咱们得以独立地改变两个变量。昂贵的元素复制操做只在必要的时候发生,也就是咱们改变这两个变量的时候发生复制:
x.append(5)
y.removeLast()
x // [1, 2, 3, 5]
y // [1, 2]复制代码
若是 Array 结构体中的引用在数组被改变的一瞬间时是惟一的话 (好比,没有声明 y),那么也不会有复制发生,内存的改变将在原地进行。这种行为就是写时复制,做为一个结构体的做者,你并不能免费得到这种特性,你须要本身进行实现。当你本身的类型内部含有一个或多个可变引用,同时你想要保持值语义,而且避免没必要要的复制时,为你的类型实现写时复制是有意义的。
下面看看经过简单的例子看一下:
var array = [COWStruct()]
array[0].change() // No copy
var otherArray = [COWStruct()]
var x = array[0]
x.change() // Copy复制代码
然而本身去实现一个写时复制的话,首先你要判断引用的惟一性,不是惟一的话进行写时复制,惟一的话直接改变须要改变的值。
结构体和类主要的区别就是一个是值类型,一个是引用类型;值类型是写时复制的,引用类型是不会发生写时复制的;当咱们须要一个简单不须要继承、很少变的数据时候咱们首选结构体,由于在数据结构上来讲结构体的存取效率是高于类的,反之当咱们须要一个数据结构比较大,须要继承,变化比较多的时候咱们选择类,由于在变化的过程当中结构体可能会发生写时复制,而类不会;下面举一个简单的例子:
以Array
和NSMutableArray
来讲:
当有一个数组,数据量相对比较小,也不用去常常改变它,只是用来存数据和取数据,咱们首先Array
当数组的数据量很大的时候,而且常常要去对他进行添加,删除等操做,而且常常赋值给其余变量的话就推荐使用NSMutableArray
这个是一个比较开发性的问题了,我想到的是用策略模式的方式来简单实现一下,使用策略模式的好处是方便文件类型的扩展,下面我简单画个简单的UML图你们看一看吧:
固然这只是一个初步的模型,还有不少细节待考虑,好比文件缓存什么的,是存本地仍是磁盘,这都得去考虑,小弟只是抛砖引玉给个简单的思路。
写完已是深夜了,以上是我丁香园电话面试的一些问题,和以前面试一些回答很差的问题,最后面试完我问了一下丁香园的面试官对我感受怎样,他说广度还行深度不够,我确实又有这点问题,想学的知识比较多,有时候也没来得及去看实现原理,只是简单的过一下,没深刻研究透彻,这是我须要增强的地方,以后若是有二面的话我会在继续更新的,最后谢谢你们的阅读~~我是WCL,你们能够去我github关注一波