常见的iOS面试题

1. Cocoa有私有方法吗?

Cocoa没有任何真正的私有方法。只要知道对象支持的某个方法的名称,即使该对象所在的类的接口中没有该方法的声明,你也可以调用该方法。不过这么做编译器会报错,但是只要新建一个该类的 类别(category),在类别.h文件中写上原始类该方法的声明,类别.m文件中什么也不写,就可以正常调用私有方法了。这就是传说中的 私有方法前向引用。 所以说Cocoa没有真正的私有方法。

2. 什么时候会报unrecognized selector的异常,怎么解决?

简单来说:当调用该对象上某个方法,而该对象上没有实现这个方法的时候, 可以通过“消息转发”进行解决。

objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常 unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:

  • 1. Method resolution
    objc运行时会调用 +resolveInstanceMethod: 或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。

  • 2. Fast forwarding
    如果目标对象实现了 -forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nilself,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。

  • 3. Normal forwarding
    这一步是Runtime最后一次给你挽救的机会。首先它会发送 -methodSignatureForSelector: 消息获得函数的参数和返回值类型。如果-methodSignatureForSelector: 返回nil,Runtime则会发出 -doesNotRecognizeSelector: 消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation: 消息给目标对象。

消息转发流程简图

注意: 不少初学runtime的同学都被这张图(侵删)有点误导了,认为resolveInstanceMethod返回NO才接着走后面的转发流程,而返回YES就停止转发了,其实如果重写的resolveInstanceMethod什么也不做,只是返回YES也会接着走后面的转发流程。
这个返回值对于消息转发流程没有任何意义,从runtime的源码来看这个返回值只和debug的信息相关。
原文链接:https://blog.csdn.net/yinyignfenlei/article/details/105366819

3. App 编译过程有了解吗?

可以参考这篇文章

一般会执行如下几个步骤:

  • 预处理
  • 语法和语义分析
  • 生成代码和优化
  • 汇编
  • 链接

4. iOS程序的启动原理(过程)

一、解析Info.plist
  • 加载相关信息,例如如闪屏
  • 沙箱建立、权限检查
二、Mach-O加载
  • 如果是胖二进制文件,寻找合适当前CPU类别的部分
  • 加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)
  • 定位内部、外部指针引用,例如字符串、函数等
  • 执行声明为attribute((constructor))的C函数
  • 加载类扩展(Category)中的方法
  • C++静态对象加载、调用ObjC的 +load 函数
三、程序执行
  • 调用main()
  • 调用UIApplicationMain()
  • 调用applicationWillFinishLaunching

5. 影响 iOS程序启动性能的因素

一、main()函数之前耗时的影响因素
  • 动态库加载越多,启动越慢。
  • ObjC类越多,启动越慢
  • C的constructor函数越多,启动越慢
  • C++静态对象越多,启动越慢
  • ObjC的+(void)load方法越多,启动越慢
二、main()函数之后耗时的影响因素
  • 执行main()函数的耗时
  • 执行applicationWillFinishLaunching的耗时
  • rootViewController及其childViewController的加载、view及其subviews的加载

6. iOS程序启动性能的优化

iOS程序启动性能的优化

7. 单例优缺点

优点:

  • 一个类只被实例化一次,提供了对唯一实例的受控访问。
  • 节省系统资源
  • 允许可变数目的实例。

缺点:

  • 一个类只有一个对象,可能造成责任过重,在一定程度上违背了“单一职责原则”。
  • 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

8. load 和 Initialize 的区别

  • +initialize 方法:苹果官方对这个方法有这样的一段描述:这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量。

  • +load 方法会在加载类的时候就被调用,也就是应用启动的时候,在调用 main 函数之前会加载所有的类,也会调用每个类的 +load 方法。

  • +initialize 方法类似一个懒加载,如果没有使用这个类,那么系统默认不会去调用这个方法,且默认只加载一次;

  • +initialize 的调用发生在 +init 方法之前,创建子类的时候,子类会去调用父类的+initialize 方法。

9. 什么是 离屏渲染?什么情况下会触发?该如何应对?

离屏渲染就是在当前屏幕缓冲区以外,新开辟一个缓冲区进行操作。

离屏渲染出发的场景有以下:

  • 圆角 (maskToBounds并用才会触发)
  • 图层蒙版
  • 阴影
  • 光栅化
为什么要避免离屏渲染?

CPU GPU 在绘制渲染视图时做了大量的工作。离屏渲染发生在 GPU 层面上,会创建新的渲染缓冲区,会触发 OpenGL 的多通道渲染管线,图形上下文的切换会造成额外的开销,增加 GPU 工作量。如果 CPU GPU 累计耗时 16.67 毫秒还没有完成,就会造成卡顿掉帧。

圆角属性蒙层遮罩 都会触发离屏渲染。指定了以上属性,标记了它在新的图形上下文中,在未愈合之前,不可以用于显示的时候就出发了离屏渲染。

  • 在OpenGL中,GPU有2种渲染方式

    • On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
    • Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
  • 离屏渲染消耗性能的原因

    • 需要创建新的缓冲区
    • 离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
  • 哪些操作会触发离屏渲染?

    • 光栅化,layer.shouldRasterize = YES
    • 遮罩,layer.mask
    • 圆角,同时设置 layer.masksToBounds = YES、layer.cornerRadius大于0
    • 考虑通过 CoreGraphics 绘制裁剪圆角,或者叫美工提供圆角图片
    • 阴影,layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染

10. 说一下对 APNS 的认识?

我写过三篇关于推送框架的总结:

  • 第一篇:http://xiaolu520.com/2017/06/08/WWDC2016-Session707%EF%BC%88UNNotifications%EF%BC%89/
  • 第二篇:http://xiaolu520.com/2017/06/09/WWDC2016-Session708%EF%BC%88%E9%AB%98%E7%BA%A7%E9%80%9A%E7%9F%A5%EF%BC%89/
  • 第三篇:http://xiaolu520.com/2017/06/12/WWDC2016-Session724%EF%BC%88APNS%E6%96%B0%E7%89%B9%E6%80%A7%EF%BC%89/

以前的通知基于 Socket ,但是不稳定。新的通知框架基于 Http

设备的唯一识别码,程序的BundleID,通过长连接发给 APNS 服务器,返回一个 DeviceToken。把这个 DeviceToken 发给服务器,以后服务器把 DeviceToken 和 通知的内容发给 APNS 就可以了。

11. Designated Initializer 的规则

1.推荐一篇写的很好的文章
2.这篇文章写得更简单易懂

12. Autoreleasepool原理及释放时机

AutoreleasePool是在RunLoop即将进入RunLoop和准备进入休眠这两种状态的时候被创建和销毁的。
所以AutoreleasePool的释放有如下两种情况:

  • 一是Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。

  • 二是手动调用AutoreleasePool的释放方法(drain方法)来销毁AutoreleasePool

13.delegate、Block和Notification的区别

  • delegate:一般用于两对象一对一的通信交互。需要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实行通信。通信事件比较多时,建议使用delegate方式,但要注意其属性声明用weak修饰。

  • Block:一般用于两对象一对一的通信交互。 比较简洁,不要定义繁琐的协议方法,但是需注意循环引用问题。

  • Notification:主要用于1对多的通信方式,通信对象之间不需要建立关系,但是通知的代码可读性差,所在对象释放时需要移除通知问题。

14.iOS为什么很少用try catch?

准确的说是OC很少用try catch,因为OC的异常类型多,不统一,而try catch不能捕获所有类型,使用价值不高。同时try catch对异常的处理是stack unwind机制?从itanium C++ ABI中我们知道,stack unwind是采用了压缩的栈帧信息表,遇到异常的时候通过栈回溯的方式一层一层地处理每一层调用栈在异常处理时的相关逻辑,会有一定的性能损失。

  • 一是查压缩表(时间复杂度)
  • 二是逐层处理调用栈(建表,空间复杂度),从这个层面上来说,try catch的使用是有一定性能损耗的,当然这个损耗仅仅在发生异常的时候存在,无异常时没有任何损耗。所以总的来说不用trycatch的原因有两点,1,使用价值不高,2,有性能问题。

swift 在 ABI 上给 error 单独分配了一个寄存器,可以快速地将 error 透传到外面去。

15. AFNetworking为何需要常驻子线程?3.0后为什么又不需要开启了?

一、2.x为何要开启常驻子线程?

先来看看 NSURLConnection 发送请求时的线程情况,NSURLConnection 是被设计成异步发送的,调用了start方法后,NSURLConnection 会新建一些线程用底层的 CFSocket 去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。

AFN(2.x基于NSURLConnection,此类现已废弃) 的做法是把网络请求的发起和解析都放在同一个子线程中进行,但由于子线程默认不开启 runloop,它会向一个 C语言程序那样在运行完所有代码后退出线程。而网络请求是异步的,这会导致获取到请求数据时,线程已经退出,代理方法没有机会执行。因此,AFN 的做法是使用一个 runloop 来保证线程不死。然而频繁的创建线程并启动runloop肯定会造成内存泄露(runloop 无法停止.线程无法退出),所以AFN就创建了一个单例线程,并且保证线程不退出~

二、2.x怎么开启常驻子线程?
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self
selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });

    return _networkRequestThread;
}
三、3.0后为什么又不需要开启了?

NSURLConnection的一大痛点就是:发起请求后,这条线程并不能随风而去,而需要一直处于等待回调的状态。从iOS9.0开始 deprecated 了NSURLConnection,替代方案就是NSURLSession

self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration 
delegate:self delegateQueue:self.operationQueue];

从上面的代码可以看出,NSURLSession发起的请求,不再需要在当前线程进行代理方法的回调!可以指定回调的delegateQueue,这样我们就不用为了等待代理回调方法而苦苦保活线程了。

同时还要注意一下,指定的用于接收回调的Queue的maxConcurrentOperationCount设为了1,这里目的是想要让并发的请求串行的进行回调。

- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);
    AFURLSessionManagerTaskDelegate *delegate = nil;
    [self.lock lock];
    //给所要访问的资源加锁,防止造成数据混乱
    delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
    [self.lock unlock];
    return delegate;
}

上面的代码对 self.mutableTaskDelegatesKeyedByTaskIdentifier 的访问进行了加锁,目的是保证多线程环境下的数据安全。既然加了锁,就算maxConcurrentOperationCount不设为1,当某个请求正在回调时,下一个请求还是得等待一直到上个请求获取完所要的资源后解锁,所以这边并发回调也是没有意义的。相反多task回调导致的多线程并发,还会导致性能的浪费。

/*
AF3.x会给每个 NSURLSessionTask 绑定一个 
AFURLSessionManagerTaskDelegate ,这个TaskDelegate相当于把
NSURLSessionDelegate进行了一层过滤,最终只保留类似
didCompleteWithError这样对上层调用者输出的回调。
*/
- (void)URLSession:(__unused NSURLSession *)session 
task:(NSURLSessionTask *)task 
didCompleteWithError:(NSError *)error
{
//此处代码进行了大量删减,只是为了让大家清楚的看到这个方法做的最重要的事
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), 
manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
    }
}
四、为什么AF3.0中需要设置self.operationQueue.maxConcurrentOperationCount = 1;,而AF2.x却不需要?

功能不一样:AF3.0的operationQueue是用来接收NSURLSessionDelegate回调的,鉴于一些多线程数据访问的安全性考虑,设置了maxConcurrentOperationCount = 1来达到串行回调的效果。而AF2.0的operationQueue是用来添加operation并进行并发请求的,所以不要设置为1。

- (AFHTTPRequestOperation *)POST:(NSString *)URLString
                      parameters:(id)parameters
                         success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                         failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure];
    [self.operationQueue addOperation:operation];
    return operation;
}

更详细的文章: https://www.likecs.com/show-203542885.html

16. SDWebImage是如何做到Url不变的情况下,更新图片内容

通过查阅HTTP协议相关的资料得知,与服务器返回的Last-Modified相对应的request header里可以加一个名为If-Modified-Since的key,value即是服务器回传的服务端图片最后被修改的时间,第一次图片请求时If-Modified-Since的值为空,第二次及以后的客户端请求会把服务器回传的Last-Modified值作为If-Modified-Since的值传给服务器,这样服务器每次接收到图片请求时就将If-Modified-Since与Last-Modified进行比较,如果客户端图片已陈旧那么返回状态码200、Last-Modified、图片内容,客户端存储Last-Modified和图片;如果客户端图片是最新的那么返回304 Not Modified、不会返回Last-Modified、图片内容。

Apache比较时是看If-Modified-Since之后有没有更新图片,Nginx比较时是看If-Modified-Since与Last-Modified是否相等,所以对于Apache服务器环境客户端每次都要严格的存储服务器回传的Last-Modified以便下次请求时作为If-Modified-Since的值传给服务器,对于Nginx服务器环境客户端不必存储服务器回传的Last-Modified,每次请求时只需将图片自身的fileModificationDate作为If-Modified-Since的值传服务器即可。在实际开发中,如果遇到明明传了If-Modified-Since、服务器图片也变更了、但是客户端却请求不到最新的图片的情况时,那么就需要查看一下服务器对这两个时间戳的比较逻辑。

通过查看SDWebImageDownloader的源码得知,它开放了一个headersFilter的block,意在让开发者可以对所有图片请求追加一些额外的header,这正合我意。那么我们就可以在诸如AppDelegate didFinishLaunching的地方追加如下代码:

SDWebImageDownloader *imgDownloader = SDWebImageManager.sharedManager.imageDownloader;
imgDownloader.headersFilter  = ^NSDictionary *(NSURL *url, NSDictionary *headers) {
 
    NSFileManager *fm = [[NSFileManager alloc] init];
    NSString *imgKey = [SDWebImageManager.sharedManager 
cacheKeyForURL:url];
    NSString *imgPath = [SDWebImageManager.sharedManager.imageCache 
defaultCachePathForKey:imgKey];
    NSDictionary *fileAttr = [fm attributesOfItemAtPath:imgPath error:nil];
 
    NSMutableDictionary *mutableHeaders = [headers mutableCopy];
    NSDate *lastModifiedDate = nil;
 
    if (fileAttr.count > 0) {
        if (fileAttr.count > 0) {
            lastModifiedDate = (NSDate *)fileAttr[NSFileModificationDate];
        }
    }
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
    formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
    formatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss z";
 
    NSString *lastModifiedStr = [formatter stringFromDate:lastModifiedDate];
    lastModifiedStr = lastModifiedStr.length > 0 ? lastModifiedStr : @"";
    [mutableHeaders setValue:lastModifiedStr forKey:@"If-Modified-Since"];
 
    return mutableHeaders;
};

然后,加载图片的地方以前怎么写还是怎么写,但别忘了Option是SDWebImageRefreshCached

NSURL *imgURL = [NSURL URLWithString:@"http://handy-img-storage.b0.upaiyun.com/3.jpg"];
[[self imageView] sd_setImageWithURL:imgURL
                    placeholderImage:nil
                             options:SDWebImageRefreshCached];

更多参考:https://blog.csdn.net/lizhilin_vip/article/details/53185423

17.MVC、MVVM

传统的MVC
image.png
Cocoa 版本MVC
image.png
MVVM
7A12F6A8-D0F4-40BB-B0BB-AFDAE484911C.png

更多参考:https://www.jianshu.com/p/70f1902c0f54

18.tableview列表中的cell复用有倒计时的情况

19.超大图显示解决方案

占用内存过大的图片用UIImageView直接设置image展示不了,因为内存占用很大,可能导致崩溃问题。

方案一:使用UIScrollView

步骤:
1、通过循环体把大长图裁剪成多个等份,等份划分可以用长图的高度对屏幕高度进行相除,份数为除数+1,这样每一个等份就是当前屏幕大小的一张小图。

2、渲染出每一个等份为一张小图,将其放置在scrollView当前屏幕显示的部分中。

3、需要考虑两件事情,当前正显示在屏幕中的小图的上一张图,需要将其内存释放掉,而对于其下一张图,需要预先获取将其渲染出来,防止滑动不流畅的效果。

缺点:各种计算尺寸及滑动显示处理很麻烦。

方案二:使用UITableView

针对方案一中,需要考虑到当前正显示在屏幕中的小图的上一张图将其内存释放掉,对于其下一张图预先获取将其渲染,这个步骤就是我们的cell的重用机制。

步骤:
1、通过循环体把大长图裁剪成多个等份,小图个数 = 长图高度 / Row行高 + 1
2、分割图片保存到沙盒
3、从沙盒中获取到缓存后的图片进行展示

优点:
1、和重用机制的共同点是都只初始化当前可见部分的对象的内存,而其余的对象始终重用这部分内存,只是绘制的内容改变了,这样就解决了我们长图造成的内存峰值崩溃问题。
2、通过UITableView,系统帮我们自动实现了重用机制,不用我们再去考虑上一张图的内存释放掉,下一张图的预先渲染问题,大大简化了问题的复杂度。
缺点:
1、因为该方案是加载多个小图片段,那么便不能实现类似缩略图那种放大缩小的功能了。
2、存在布局计算、裁剪、缓存等操作,耗时。

方案三:相对较完美WKWebview

跳出IOS编程的惯有思维,从IOS原生到借助H5来实现,复杂的问题丢给它解决,既解决了问题又简化了代码,就像数学题,简便的方法很难想到,容易想到的方法实现起来却很复杂。

其基本思路是将image包装为HTML代码,再使用WKWebView加载HTML代码。

方案四:CATiledLayer

利用CATiledLayer绘制的内存峰值最低,绘制后内存增量最小,也充分利用了CPU的能力。

更多可以参考:https://www.jianshu.com/p/89b2c892e161

20.Swift 关键字weakunowned

weakunowned 类似,不同点是 unowned 是永远有值的。weak可以声明可选型,很多时候我们不想声明一个可选型,可选型代表着风险,此时就可将属性声明成 unowned。因为unowned设置以后即使它原来引用的内容已经被释放了,它仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向 nil 。如果你尝试调用这个引用的方法或者访问成员属性的话,程序就会崩溃。而 weak 则友好一些,在引用的内容被释放后,标记为 weak 的成员将会自动地变成 nil (因此被标记为 @ weak 的变量一定需要是 Optional 值)。

class Example {
    class Example {
    var num = 10
    lazy var method:(Int) -> Int = {
        [unowned self] (i:Int) in
            return self.num + i
    }
}
class Example {
    var delegate:UITableViewDelegate?
    lazy var method:(Int) -> Void = {
        [weak delegate = self.delegate] (i: Int) in
        //操作delegate
    }
}

21.const、static区别

9999. NSNotification底层实现

9999.delegate底层实现

9999. Block底层实现

. runtime 如何实现 weak 属性(weak底层实现原理)

. KVO的底层实现

9999. NSCache,NSDictionary,NSArray的区别

. 谈谈iOS事件响应链

iOS App签名的原理

怎么防止反编译

哈希原理

9999. TCP和UDP的区别于联系

  • TCP为传输控制层协议,为面向连接、可靠的、点到点的通信;
  • UDP为用户数据报协议,非连接的不可靠的点到多点的通信;
  • TCP侧重可靠传输,UDP侧重快速传输。

999.TCP、socket、websocket区别

999.socket消息帧粘包,拆包及处理方法

9999. TCP连接的三次握手

第一次握手:客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;

第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开 TCP 连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)

9999. Scoket连接和HTTP连接的区别

  • HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

  • HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLSession(NSURLConnection废弃了)。

  • Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。

9999. HTTP协议的特点及HTTP请求GET和POST的区别

HTTP协议的特点:
  • HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。

  • HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。

  • HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。

  • GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。

GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。
以分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送,
如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。

  • POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.

  • GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。

9999. HTTP请求GET和POST的区别:

一、传输数据的大小:

GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。

二、安全性:

  • POST的安全性要比GET的安全性高;
  • 通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
  • HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。

9999. HTTP post的body体使用form-urlencoded和multipart/form-data的区别

一、application/x-www-form-urlencoded:

窗体数据被编码为名称/值对,这是标准且默认的编码格式。当action为get时候,客户端把form数据转换成一个字串append到url后面,用?分割。当action为post时候,浏览器把form数据封装到http body中,然后发送到server。

二、multipart/form-data:

multipart表示的意思是单个消息头包含多个消息体的解决方案。multipart媒体类型对发送非文本的各媒体类型是有用的。一般多用于文件上传。
multipart/form-data只是multipart的一种。目前常用的有以下这些类型(注:任何一种执行时无法识别的multipart子类型都被视为子类型"mixed")

9999. 谈一谈网络中的 session 和 cookie

因为 Http 无状态的特性,如果需要判断是哪个用户,这时就需要 Cookie 和 Session。

Cookie 存储在客户端,用来记录用户状态,区分用户。一般都是服务端把生成的 Cookie 通过响应返回给客户端,客户端保存。

Session 存储在网络端,需要依赖 Cookie 机制。服务端生成了 Session 后,返回给客户端,客户端 setCookie:sessionID,所以下次请求的时候,客户端把 Cookie 发送给服务端,服务端解析出 SessionID,在服务端根据 SessionID 判断当前的用户。

一、如何修改 Cookie?
  • 相同 Key 值得新的 Cookie 会覆盖旧的 Cookie.
  • 覆盖规则是 name path 和 domain 等需要与原有的一致
二、如何删除 Cookie?
  • 相同 Key 值得新的 Cookie 会覆盖旧的 Cookie.
  • 覆盖规则是 name path 和 domain 等需要与原有的一致
  • 设置 Cookie 的 expires 为过去的一个时间点,或者 maxAge = 0
三、如何保证 Cookie 的安全?
  • 对 Cookie 进行加密处理
  • 只在 Https 上携带 Cookie。
  • 设置 Cookie 为 httpOnly,防止跨站脚本攻击。

你可能感兴趣的:(常见的iOS面试题)