iOS面试珠玑

iOS基础类

这里包含了去哪儿,滴滴,蚂蚁金服,美团,今日头条,快手以及其它公司的一些面试题,大部分面试官问的重复的问题不少,整体来讲就是如下的面试题。面试

  1. 简述iOS中的内存管理方式
  • iOS的内存管理用的是引用计数的方法,分为MRC(手动引用计数)和ARC(自动引用计数)。算法

  • MRC:开发者手动地进行retain和release操做,对每一个对象的retainCount进行+1,-1操做,当retainCount为0时,系统会自动释放对象内存。数据库

  • ARC:开发者经过声明对象的属性为strong,weak,retain,assign来管理对象的引用计数,被strong和retain修饰的属性变量系统会自动对所修饰变量的引用计数进行自增自减操做,一样地,retainCount为0时,系统会释放对象内存。api


  1. block的分类,__block的做用,block循环引用产生的缘由及解决办法
  • blcok分为全局blcok,堆block,栈block。
  • 在 MRC下:只要没有访问外部变量,就是全局block。访问了外部变量,就是栈block。显示地调用[block copy]就是堆block。
  • 在 ARC下:只要没有访问外部变量,就是全局block。若是访问了外部变量,那么在访问外部变量以前存储在栈区,访问外部变量以后存储在堆区。
  • __block的做用:将外部变量的传递形式由值传递变为指针传递,从而能够获取而且修改外部变量的值。一样,外部变量的修改,也会影响block函数的输出。
  • block循环引用问题:当一个类的对象持有block,block里面又引用了这个对象,那么就是一个循环引用的关系。能够用strong-weak-dance的方法解除循环引用。

3.深拷贝与浅拷贝数组

  • 深拷贝就是开辟一块新的内存空间来存储原来内存空间的内容,对象指针指向新的内存空间。浅拷贝只是从新生成一个指针,指向的仍是原来的内存空间。xcode

  • copy方法:若是是非可扩展类对象,则是浅拷贝。若是是可扩展类对象,则是深拷贝。浏览器

  • mutableCopy方法:不管是可扩展类对象仍是不可扩展类对象,都是深拷贝。缓存

  • 注意:深拷贝和深复制不一样,深拷贝的内存空间的元素仍是指向原地址,可是深复制会开辟新的内存空间从新复制子元素。安全


4.iOS中常见的属性和默认的对象属性服务器

  • 常见属性: atomic, nonatomic, assign, retain, strong, weak, copy, readonly, readwrite, unsafe_unretained, getter=, setter= 等。

  • 默认属性: 继承于NSObject类的对象:(atomic, strong), 非继承于NSObject类的对象:(atomic, assign)

  • 属性意义:

    atomic:原子性的,在执行setter和getter方法时能够保证访问变量的线程安全。
    nonatomic:非原子性的,没法保证访问变量的线程安全性,可是变量访问效率会提升。
    assign:主要用于修饰非继承于NSObject类型的对象,例如int, double, NSInteger等,当该对象被其余对象引用时,该对象的引用计数不会自加1。
    retain:通常状况下等同于ARC环境下的strong修饰符,可是在修饰block对象时,retain至关于assign,而strong至关于copy。
    strong:主要用于继承于NSObject类的对象,当strong修饰的对象被其余对象引用时,引用计数会自加1。
    weak:主要用于继承于NSObject类的对象,当weak修饰的对象被其余对象引用时,引用计数不会自加1,且retainCount为0时,指向该对象的指针将会置nil,指向堆栈的底部0x00000000,防止野指针的出现。
    unsafe_unretained:主要用于继承于NSObject类型的对象,当retainCount为0时,指向该对象的指针不会置nil,所以可能会出现野指针,可是效率方面会比weak要高。
    copy:主要适用于NSArray, NSDictionary, NSString, Block等类型的对象,开辟一块新的内存存储原来内存中的元素,对象指针指向新内存的地址。
    readonly: 只读属性
    readwrite: 读写属性
    getter = : 修改读属性名称
    setter = : 修改写属性名称

5.哪些属性须要声明成copy,为何?

  • NSDictionary, NSArray, NSString, Block须要声明为copy属性修饰,block用copy修饰是由于block只有拷贝到堆上才能保证调用block的时候,block没有被系统提早释放掉。NSDictionary, NSArray, NSString用copy修饰的缘由直接上代码(以NSString举例)
@interface ViewController ()
@property (nonatomic, copy)NSString *aStr;
@property (nonatomic, strong)NSString *bStr;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
// [self buildUI];
    [self testCode];
}

- (void)testCode{
    
    NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"%@", @"abc"];
    self.aStr = mutableStr;
    self.bStr = mutableStr;
    [mutableStr appendString:@"123"];
    NSLog(@"copy修饰的字符串:%@", self.aStr);
    NSLog(@"strong修饰的字符串:%@", self.bStr);
}
复制代码

打印结果:

2018-05-22 21:13:58.344883 TTFeedBackDemo[1224:245923] copy修饰的字符串:abc
2018-05-22 21:13:58.345004 TTFeedBackDemo[1224:245923] strong修饰的字符串:abc123
复制代码

用了copy属性修饰以后,能够防止这些类型的对象被引用而且改变内容。


  1. 通知,代理,block,KVO的使用场景分别是什么,有什么区别?
  • 通知: 适用于毫无关联的页面之间或者系统消息的传递,属于一对多的信息传递关系。例如系统音量的改变,系统状态的改变,应用模式的设置和改变,都比较适合用通知去传递信息。

  • 代理: 一对一的信息传递方式,适用于相互关联的页面之间的信息传递,例如push和present出来的页面和原页面之间的信息传递。

  • block: 一对一的信息传递方式,效率会比代理要高(毕竟是直接取IMP指针的操做方式)。适用的场景和代理差很少,都是相互关联页面之间的页面传值。

  • KVO: 属性监听,监听对象的某一属性值的变化情况,当须要监听对象属性改变的时候使用。例如在UIScrollView中,监听contentOffset,既能够用KVO,也能够用代理。可是其余一些状况,好比说UIWebView的加载进度,AVPlayer的播放进度,就只能用KVO来监听了,不然获取不到对应的属性值。


  1. 简述对OC中的isa指针的认识
  • isa指针:首先,贴出NSObject.h文件,你们宏观感觉一下。

    Ojective-C语言是基于C语言的封装,它实现了将面向过程的语言向面向对象的语言的转变。而OC中绝大部分类又是继承于NSObject类的,因此研究清楚NSObject类的构成,对于理解OC语言颇有帮助。

    NSObject类引入了两个头文件:#include <objc/objc.h>#include <objc/NSObjCRuntime.h>,第一个头文件引入的是objc结构体的构成方式即isa指针,第二个头文件引入的是Runtime消息查找机制。
    接下来又引入了三个类的声明:@class NSString, NSMethodSignature, NSInvocation;
    NSMethodSignature和NSInvacation和OC的方法转发机制有关,而NSString是与NSObject的+ (NSString *)description方法有关。

    isa指针: 代码以下

    @interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
    }
    复制代码

    NSObject对象其实包含了一个Class类型的isa指针。 这个和id类型是同样的,代码以下:

    struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    复制代码

    即id类型实际上是objc_object指针类型的别称。objc_object结构体一样也是包含了一个指向Class类型的指针。 继续看一下,Class isa指针的内部结构,以下

    struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;//指向元类的Class指针
    
    #if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;//指向父类的Class指针
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;//类名
    long version                                             OBJC2_UNAVAILABLE;//类的版本信息,默认为0
    long info                                                OBJC2_UNAVAILABLE;//运行期使用的一些位标识
    long instance_size                                       OBJC2_UNAVAILABLE;//该类的实例变量大小
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;//属性列表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;//方法列表
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;//缓存方法列表
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;//协议列表
    #endif
    
    } OBJC2_UNAVAILABLE;
    
    typedef struct objc_class *Class;
    复制代码

    Class其实就是一个objc_class类型的结构体指针。objc_class的结构体变量构成见上图,下面是objc_class类的super_class指针和isa元类指针的具体指向关系,请你们分清对象,对象的类,元类,根元类这些概念。


  1. 简述OC中的消息转发机制
  • 当objc_msgSend方法调用找不到响应的函数名称时就会进行消息转发,主要分为3步:

    一、动态方法解析

    调用方法+(BOOL)resolveInstanceMethod:(SEL)sel(实例方法动态解析)和+ (BOOL)resolveClassMethod:(SEL)sel(类方法动态解析)。

    二、备援接收者

    调用方法 - (id)forwardingTargetForSelector:(SEL)aSelector

    三、彻底转发

    调用方法- (void)forwardInvocation:(NSInvocation *)anInvocation- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

    具体的转发流程见下图:


  1. 响应链原理
  • 当用户触摸屏幕时,触碰屏幕产生事件UIEvent并存入UIApplication中的事件队列中, 而且在整个视图结构中自上而下的进行分发,以下图所示:

  • 这里着重介绍两个方法

//判断当前点击事件是否存在最优响应者(First Responder)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
复制代码
//判断当前点击是否在控件的Bounds以内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
复制代码

第一个方法返回一个能够响应event触摸事件的UIView,第二个方法判断触摸点位在不在可相应范围以内的BOOL值。因此会衍生出一些问题,好比说“如何让一个父视图之外的子视图响应点击事件”,“如何只让一个UIView的圆形区域响应触摸事件”等等,在此因为篇幅限制,再也不一一展开详述。下面这幅图简述了系统查找响应事件控件的流程。


  1. 手写一个block结构体声明
  • 这就是考察一名iOS开发人员的写码基本功了,你们加深印象便可: typedef void(^RBBlogDemoHandler)(void);

  1. RunLoop原理,RunLoop与线程的关系
  • RunLoop其实就是一个do-while循环,Runloop的存在保证了程序一直在前台运行。RunLoop和线程是一一对应的关系,即开启一个线程,就会建立一个RunLoop对线程进行处理和管理,直到Runloop中没有须要处理的item,RunLoop才会进入休眠状态,若是休眠一段时间没有被唤醒的话,RunLoop将会被销毁掉。RunLoop的执行逻辑见下图:

  1. GCD与NSOperation两种管理多线程方式的异同点
  • GCD是用C语言实现的,而NSOperation是用OC实现的。
  • NSOperation能够设置最大线程并发数,能够设置线程依赖关系,能够设置线程的优先级。
  • GCD方式管理多线程是一种对开发者很是友好的开发方式,开发者只须要关注同步异步,串行并发这些线程关系就能够轻松地进行线程管理了。另外,线程中执行的任务是经过Block的形式调用的,因此执行效率也是很是高的。

  1. GCD的经常使用api
  • dispatch_sync:同步线程函数
  • dispatch_async:异步线程函数
  • dispatch_group_async:群线程函数
  • dispatch_barrier_async:栅栏函数,阻塞所属的队列的线程
  • dispatch_barrier_sync:栅栏函数,阻塞当前线程
  • dispatch_apply:该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待所有处理执行结束。

  1. GCD中的同步异步,串行并发的概念,GCD常见的线程死锁问题
  • 同步:针对于线程而言的概念,阻塞当前线程,不执行结束,当前线程就不会继续往下执行。
  • 异步:针对于线程而言的概念,不阻塞当前线程,当前线程会继续往下执行。
  • 串行:针对于队列而言,串行队列遵照FIFO(first-in-first-out)原则,只能一个任务一个任务地顺序执行。
  • 并发:针对于队列而言,并发队列能够在同一时间执行多个任务。
  • 相似于
dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"%@", @"123");
    });
复制代码

这样的函数,就是典型的GCD死锁函数。 由于dispatch_sync阻塞的当前的线程,而当前线程是main_queue,也就是说是一个串行线程,当前线程只有先执行NSLog(@"%@", @"123")才能继续运行下去,可是当前线程又被阻塞掉了,没法向下继续执行,因此这就是一个死锁的GCD执行函数了。


  1. iOS中经常使用的线程锁有哪些,分别具备哪些特色?
  • @synchronized 关键字加锁 互斥锁,性能较差不推荐使用
  • NSLock 互斥锁 不能屡次调用 lock方法,会形成死锁
  • NSRecursiveLock递归锁,NSRecursiveLock类定义的锁能够在同一线程屡次lock,而不会形成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操做。只有全部的锁住和解锁操做都平衡的时候,锁才真正被释放给其余线程得到。
  • NSConditionLock条件锁,顾名思义,这个锁对象有一个condition属性,只有condition的值相同,才能获取到该锁,而且执行解锁操做。
  • POSIX互斥锁,POSIX是Unix/Linux平台上提供的一套条件互斥锁的API。用法和特色与NSLock相似。
  • dispatch_semaphore信号量实现加锁,当信号量为0时,线程将会被卡死,经过信号量的增减来达到控制线程个数的目的。
  • OSSpinLock自旋锁,用法相似于NSLock,能够自动检查线程锁是否已经打开,效率比较高,可是被证实不是线程安全的。
  • GCD线程阻断dispatch_barrier_async/dispatch_barrier_syncdispatch_barrier_async/dispatch_barrier_sync在必定的基础上也能够作线程同步,会在线程队列中打断其余线程执行当前任务。两个的区别是dispatch_barrier_async阻塞的是当前队列的线程,而dispatch_barrier_sync阻塞的是任务所插入队列的线程。
  • 各类线程锁的执行效率对好比下图:

  1. Father的子类Son,分别写出NSStringFromClass([self class]),NSStringFromClass([super class]),NSStringFromClass(self.superClass)的打印值
  • [self class] : Son
  • [super class] : Son
  • self.superClass : Father
  • 第一个就不用解释了,打印本类的类名。
  • [super class]找到NSObject中class方法之后,reciever不变。其实是由于super只是一个“编译器指示符”,它和self指向的是相同的receiver。这里要深入理解super所指代的上下文环境。
  • self.superClass是取到了本类的superClass指针,因此打印的是父类的类名。

  1. KVO的原理
  • 例如,A类的实例的name属性被B类的实例监听了。这时,OC的runtime机制生成了一个KVONotifying_A的类来替代原来的A类,重写了+ (Class)class方法,返回[A Class],从而把本身假装成A类。重写了A类属性name的setter方法加入了NSObject的两个方法:willChangeValueForKey:(值改变以前)和didChangevlueForKey:(值改变以后)。在一个被观察属性发生改变以前,willChangeValueForKey:必定会被调用,这就会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而observeValueForKey:ofObject:change:context:也会被调用。

  1. runtime的机制和应用
  • Objective-C是一门动态强类型语言,它会将一些工做放在代码运行时才处理而并不是编译时。也就是说,有不少类和成员变量在咱们编译的时是不知道的,而在运行时,咱们所编写的代码会转换成完整的肯定的代码运行。所以,咱们还须要一个运行时系统(Runtime system)来处理编译后的代码。例如OC中的类对象,都是在程序的运行期才知道这个对象是哪一个类的对象,该类具备哪些特征,等等。咱们执行的函数例如:[foo doSomeThing],实际上是经过函数objc_msgSend(foo, doSomeThing)来调用的,编译器将会在foo类的方法树中进行查找。
  • 具体的应用:JSON转Model,Model转数据库语言,方法交换Method Swizzing,消息转发,等等。

19.MJExtension, MJRefresh, SDWebImage的实现原理

  • MJExtension:经过运行时,拿到Model对应的PropertyName,而后经过KVC,将字典中的值传入Model。
  • MJRefresh:全部的View继承于父类MJRefreshComponent,经过监听SCrollView的contentOffset来判断Refresh状态,从而触发各类状态的函数方法。
  • SDWebImage:SDWebImageManager拿到须要请求的URL,会进行三级缓存的查找:NSCache, Memery磁盘,服务器。SDWebImageDownLoader会下载图片对应的Data,开启CGRefImage上下文进行图片异步渲染绘制获得UIImage而后传入UIImageView。

  1. NSTimer计时器是准确的吗,为何?
  • NSTimer计时器不是准确的。
  • 缘由:定时器被添加在主线程中,因为定时器在一个RunLoop中被检测一次,因此若是在这一次的RunLoop中作了耗时的操做,当前RunLoop持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。

  1. 类的分类和类的扩展的区别,类的分类的实现原理。
  • 类的分类能够动态添加方法(运行时),类的扩展能够添加更多的属性变量(编译期)。  
  • 类的分类的实现原理:在运行时过程当中,本类的方法加载完毕以后,会查询是否有类的分类,若是有类的分类,就会再去加载类的分类的方法,把这些方法所有存储到objc_class结构体的methodLists数组中。注意,这里的类的分类的方法是插入到数组的第一个元素位置的,也就是说类的分类的方法会覆盖本类的同名方法。若是有多个类的分类都包含同名函数,那么最后一个被加载进compile sources的类的分类文件中的方法将会覆盖其余的同名方法。

  1. iOS动态关联属性(objc_setAssociatedObject,objc_getAssocicatedObject)的实现原理
  • AssociationsManager 是顶级的对象,维护了一个从spinlock_t 锁到AssociationsHashMap哈希表的单例键值对映射;
  • AssociationsHashMap是一个无序的哈希表,维护了从对象地址到 ObjectAssociationMap的映射;
  • ObjectAssociationMap 是一个 C 中的 map ,维护了从 key 到 ObjcAssociation 的映射,即关联记录;
  • ObjcAssociation是一个C的类,表示一个具体的关联结构,主要包括两个实例变量,_policy 表示关联策略,_value 表示关联对象。

  1. Masonry的抗压缩属性和抗拉伸属性
  • 通常状况下,用Masonry设置两个平行的自适应Label(左面为Label1,右面为Label2),那么效果图应该以下:
  • 可是我想达到这样的效果,应该怎么设置呢?
  • 能够设置抗拉伸属性优先级,代码以下:
[label1 setContentHuggingPriority:UILayoutPriorityRequired
                          forAxis:UILayoutConstraintAxisHorizontal];
[label2 setContentHuggingPriority:UILayoutPriorityDefaultLow
                          forAxis:UILayoutConstraintAxisHorizontal];
复制代码
  • 当文字过多的时候,通常效果以下:
  • 那么我想实现这样的效果,应该怎么办呢?
  • 能够设置抗压缩属性,代码以下:
[label1 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                        forAxis:UILayoutConstraintAxisHorizontal];
[label2 setContentCompressionResistancePriority:UILayoutPriorityRequired
                                        forAxis:UILayoutConstraintAxisHorizontal];
复制代码
  1. 加密的种类,对称加密和非对称加密
  • 加密种类:
  • 对称加密算法:MD5,DES,AES。
  • 非对称加密算法:RSA等加密方式。
  • 对称加密:对称加密是最快速、最简单的一种加密方式,加密与解密用的是一样的密钥,这种方法在密码学中叫作对称加密算法。
  • 非对称加密:非对称加密为数据的加密与解密提供了一个很是安全的方法,它使用了一对密钥,公钥和私钥。私钥只能由一方安全保管,不能外泄,而公钥则能够发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则须要另外一个密钥。

  1. 解释一下七层网络结构,三次握手协议和四次挥手协议
  • 七层网络协议:

    由低到高:物理层、数据链路层、网络层、传输层、表示层、会话层、应用层。
  • 三次握手协议:

    第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。
  • 四次挥手协议:

    1.第一次挥手:主机1(可使客户端,也能够是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
    2.第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我也没有数据要发送了,能够进行关闭链接了;
    3.第三次挥手:主机2向主机1发送FIN报文段,请求关闭链接,同时主机2进入CLOSE_WAIT状态;
    4.第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,而后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段之后,就关闭链接;此时,主机1等待2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,主机1也能够关闭链接了。

  1. http和https的区别
  • https在http的基础上增长了SSL数据传输安全性认证层。具体关系以下图:
  • https协议须要到ca申请证书。http是超文本传输协议,信息是明文传输,https 则是具备安全性的ssl加密传输协议。http和https使用的是彻底不一样的链接方式用的端口也不同,前者是80,后者是443。http的链接很简单,是无状态的。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。

  1. https双向验证原理
  • 1.浏览器将本身支持的一套加密规则发送给网站。
  • 2.网站从中选出一组加密算法与HASH算法,并将本身的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
  • 3.浏览器得到网站证书以后浏览器要作如下工做:
    a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),若是证书受信任,则浏览器栏里面会显示一个小锁头,不然会给出证书不受信的提示。
    b) 若是证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
    c) 使用约定好的HASH算法计算握手消息,并使用生成的随机数对消息进行加密,最后将以前生成的全部信息发送给网站。
  • 4.网站接收浏览器发来的数据以后要作如下的操做:
    a) 使用本身的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
    b) 使用密码加密一段握手消息,发送给浏览器。
  • 5.浏览器解密并计算握手消息的HASH,若是与服务端发来的HASH一致,此时握手过程结束,以后全部的通讯数据将由以前浏览器生成的随机密码并利用对称加密算法进行加密。
  • 具体的流程见下图:


  1. HTTP经常使用的头部字段,常见的返回状态码和意义
  • 常见的头部字段:

    host头域
    Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,不然系统会以400状态码返回。
    Referer头域
    Referer头域容许客户端指定请求uri的源资源地址,这能够容许服务器生成回退链表,可用来登录、优化cache等。他也容许废除的或错误的链接因为维护的目的被追踪。若是请求的uri没有本身的uri地址,Referer不能被发送。若是指定的是部分uri地址,则此地址应该是一个相对地址。
    User-Agent头域
    User-Agent头域的内容包含发出请求的用户信息,User Agent也简称UA。用较为普通的一点来讲,是一种向访问网站提供你所使用的浏览器类型、操做系统及版本、CPU类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识。
    Cache-Control头域
    Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另外一个消息处理过程当中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
    Date头域
    Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,须要知道用户所在的时区。
  • 返回的状态码:

    200(成功) 服务器已成功处理了请求。一般,这表示服务器提供了请求的网页。
    201(已建立) 请求成功且服务器已建立了新的资源。
    202(已接受) 服务器已接受了请求,但还没有对其进行处理。
    203(非受权信息) 服务器已成功处理了请求,但返回了可能来自另外一来源的信息。
    204(无内容) 服务器成功处理了请求,但未返回任何内容。
    205(重置内容) 服务器成功处理了请求,但未返回任何内容。与 204 响应不一样,此响应要求请求者重置文档视图(例如清除表单内容以输入新内容)。
    206(部份内容) 服务器成功处理了部分 GET 请求。
    300(多种选择) 服务器根据请求可执行多种操做。服务器可根据请求者 来选择一项操做,或提供操做列表供其选择。
    301(永久移动) 请求的网页已被永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置。您应使用此代码通知搜索引擎蜘蛛网页或网站已被永久移动到新位置。
    302(临时移动) 服务器目前正从不一样位置的网页响应请求,但请求者应继续使用原有位置来进行之后的请求。会自动将请求者转到不一样的位置。但因为搜索引擎会继续抓取原有位置并将其编入索引,所以您不该使用此代码来告诉搜索引擎页面或网站已被移动。
    303(查看其余位置) 当请求者应对不一样的位置进行单独的 GET 请求以检索响应时,服务器会返回此代码。对于除 HEAD ##### 304(未修改) 自从上次请求后,请求的网页未被修改过。服务器返回此响应时,不会返回网页内容。若是网页自请求者上次请求后再也没有更改过,您应当将服务器配置为返回此响应。因为服务器能够告诉 搜索引擎自从上次抓取后网页没有更改过,所以可节省带宽和开销。
    305(使用代理) 请求者只能使用代理访问请求的网页。若是服务器返回此响应,那么,服务器还会指明请求者应当使用的代理。
    307(临时重定向) 服务器目前正从不一样位置的网页响应请求,但请求者应继续使用原有位置来进行之后的请求。会自动将请求者转到不一样的位置。但因为搜索引擎会继续抓取原有位置并将其编入索引,所以您不该使用此代码来告诉搜索引擎某个页面或网站已被移动。
    400(错误请求) 服务器不理解请求的语法。
    401(身份验证错误) 此页要求受权。您可能不但愿将此网页归入索引。
    403(禁止) 服务器拒绝请求。
    404(未找到) 服务器找不到请求的网页。例如,对于服务器上不存在的网页常常会返回此代码。例如:www.0631abc.com/20100aaaa,就…
    405(方法禁用) 禁用请求中指定的方法。
    406(不接受) 没法使用请求的内容特性响应请求的网页。
    407(须要代理受权) 此状态码与 401 相似,但指定请求者必须受权使用代理。若是服务器返回此响应,还表示请求者应当使用代理。
    408(请求超时) 服务器等候请求时发生超时。
    409(冲突) 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。服务器在响应与前一个请求相冲突的 PUT 请求时可能会返回此代码,以及两个请求的差别列表。
    410(已删除) 请求的资源永久删除后,服务器返回此响应。该代码与 404(未找到)代码类似,但在资源之前存在而如今不存在的状况下,有时会用来替代 404 代码。若是资源已永久删除,您应当使用 301 指定资源的新位置。
    411(须要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
    412(未知足前提条件) 服务器未知足请求者在请求中设置的其中一个前提条件。
    413(请求实体过大) 服务器没法处理请求,由于请求实体过大,超出服务器的处理能力。
    414(请求的 URI 过长) 请求的 URI(一般为网址)过长,服务器没法处理。
    415(不支持的媒体类型) 请求的格式不受请求页面的支持。
    416(请求范围不符合要求) 若是页面没法提供请求的范围,则服务器会返回此状态码。
    417(未知足指望值) 服务器未知足"指望"请求标头字段的要求。
    500(服务器内部错误) 服务器遇到错误,没法完成请求。
    501(还没有实施) 服务器不具有完成请求的功能。例如,当服务器没法识别请求方法时,服务器可能会返回此代码。
    502(错误网关) 服务器做为网关或代理,从上游服务器收到了无效的响应。
    503(服务不可用) 目前没法使用服务器(因为超载或进行停机维护)。一般,这只是一种暂时的状态。
    504(网关超时) 服务器做为网关或代理,未及时从上游服务器接收请求。
    505(HTTP 版本不受支持) 服务器不支持请求中所使用的 HTTP 协议版本。

  1. @class和import以及include的区别
  • import会引入整个.h头文件。
  • @class只是告诉编译器该类中可使用这个class类名。
  • include和import的做用相似,可是可能形成重复引用的问题,通常用判断宏定义是否存在的方式来防止循环引用。

  1. weak对象的管理方式
  • weak是弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为nil。runtime维护了一个weak表,用于存储指向某个对象的全部weak指针。weak表实际上是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

  • 一、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

  • 二、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的做用是更新指针指向,建立对应的弱引用表。

  • 三、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取全部weak指针地址的数组,而后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。


  1. iOS的retain和release的操做是在编译期仍是运行时进行的
  • retain和release是在编译期由编译器自动生成的代码,例如:
- (void) setUserName:(UITextField *)userName { 
    [_userName release]; 
    _userName = [userName retain]; 
}
复制代码

  1. +(void)load方法和+(void)initial方法的异同
  • +(void)load方法:
  • 对于加入运行期系统的类及分类,一定会调用此方法,且仅调用一次。 iOS会在应用程序启动的时候调用load方法,在main函数以前调用 执行子类的load方法前,会先执行全部超类的load方法,顺序为父类->子类->分类 在load方法中使用其余类是不安全的,由于会调用其余类的load方法,而若是关系复杂的话,就没法判断出各个类的载入顺序,类只有初始化完成后,类实例才能进行正常使用 load 方法不听从继承规则,若是类自己没有实现load方法,那么系统就不会调用,无论父类有没有实现(跟下文的initialize有明显区别) 尽量的精简load方法,由于整个应用程序在执行load方法时会阻塞,即,程序会阻塞直到全部类的load方法执行完毕,才会继续 load 方法中最经常使用的就是方法交换method swizzling。
  • +(void)initial方法:
  • 在首次使用该类以前由运行期系统调用,且仅调用一次 惰性调用,只有当程序使用相关类时,才会调用 运行期系统会确保initialize方法是在线程安全的环境中执行,即,只有执行initialize的那个线程能够操做类或类实例。其余线程都要先阻塞,等待initialize执行完。若是类未实现initialize方法,而其超类实现了,那么会运行超类的实现代码,并且会运行两次。initialize方法是线程安全的,能够用来设置内部数据,好比,某个全局状态,如数组、字典等没法在编译期初始化,能够放在initialize里面。

  1. UIViewController的生命周期方法调用顺序
  • 当一个视图控制器被建立,并在屏幕上现实的时候。代码的执行顺序:

    1.alloc       建立对象,分配空间。
    2.init       初始化对象,初始化数据。
    3.loadView    从nib载入视图,一般这一步不须要去干涉。除非你没有使用xib文件建立视图
    4.viewDidLoad   载入完成,能够进行自定义数据以及动态的建立其余空间。
    5.viewWillAppear  视图将出如今屏幕以前。
    6.viewDidAppear  视图在屏幕上渲染完成。
  • 当一个视图被移除屏幕而且销毁的时候执行顺序:

    1.viewWillDisappear  视图被移除以前。
    2.viewDidDisappear   视图被移除以后。
    3.dealloc        销毁视图。

  1. iOS中各种控件的继承树关系
  • 一张图告诉你全部的继承关系:

  1. 如何化解NSTimer的循环引用关系
  • 首先要理解NSTimer为何会引发循环引用:NSTimer和使用Timer的ViewController相互持有。
  • 解决办法有两个:
    1. 在ViewContoller的viewWillDisappear生命周期中注销Timer。
    2. 引入第三方NSObject(如HWWeakTimer)管理和持有Timer,让Timer持有第三方的成员变量。这样就打破了互相引用的循环关系。

  1. 怎样管理第三方SDK,CocoaPods和Carthage的异同
  • iOS中通常使用CocoaPods和Carthage来管理第三方SDK。

  • 二者的比较:

    1. Carthage只支持iOS 8及以上版本使用。
    2. CocoaPods默认会自动建立并更新你的应用程序和全部依赖的Xcode workspace。Carthage使用xcodebuild来编译框架的二进制文件,但如何集成它们将交由用户本身判断。CocoaPods的方法更易于使用,但Carthage更灵活而且是非侵入性的。
    咱们建立Carthage的缘由是想要一种尽量简单的工具——一个只关心本职工做的依赖管理器,而不是取代部分Xcode的功能,或者须要让框架做者作一些额外的工做。CocoaPods提供的一些特性很棒,但因为附加的复杂性,它们将不会被包含在Carthage当中。

  1. -(BOOL)isKindOfClass和-(BOOL)isMemberOfClass的区别
  • -(BOOL) isKindOfClass: classObj 判断是不是这个类或者这个类的子类的实例

  • -(BOOL) isMemberOfClass: classObj 判断是不是这个类的实例


  1. 数据持久化的几种方式和对应的应用场景
  • plist文件(属性列表):即直接拖拽plist文件到程序目录当中。由NSBundle获取本地plist资源。存储一些本地的,且不会改变的数据到程序当中。
  • preference(偏好设置):即NSUserDefaults,存储一些小型数据,设置参数,开关属性等等。
  • NSKeyedArchiver(归档):存储一些不涉及增删改查的字典数组或者NSObject等,存储的对象必定要遵循NSCoder和NSDecoder协议。
  • SQLite 3:存储一些涉及增删改查的字段数据。
  • CoreData:效率比较高,存储一些涉及增删改查的且体积很是大的数据。

  1. 如何实现一个完整的单例
  • 实现了单例的初始化以后,必定要重写+(id) allocWithZone:(struct _NSZone *)zone,-(id) copyWithZone:(NSZone *)zone,-(id) mutablecopyWithZone:(NSZone *)zone这三个方法,代码以下:
#import "Singleton.h"
@interface Singleton()<NSCopying,NSMutableCopying>
@end
 
@implementation Singleton
 
static Singleton* _instance = nil;
 
+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
        //不是使用alloc方法,而是调用[[super allocWithZone:NULL] init] 
        //已经重载allocWithZone基本的对象分配方法,因此要借用父类(NSObject)的功能来帮助出处理底层内存分配的杂物
    }) ;
     
    return _instance ;
}
 
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
 
-(id) copyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;//return _instance;
}
 
-(id) mutablecopyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;
}
@end
复制代码

  1. iOS的系统单例有哪些?
  • [UIScreen mainScreen] (应用程序窗口)

  • [UIDevice currentDevice] (当前设备)

  • [UIApplication sharedApplication] (应用程序实例)

  • [NSNotificationCenter defaultCenter] (消息中心):

  • [NSFileManager defaultManager] (文件管理):

  • [NSUserDefaults standardUserDefaults] (应用程序设置):

  • [NSURLCache sharedURLCache] (请求缓存):

  • [NSHTTPCookieStorage sharedHTTPCookieStorage] (应用程序cookies池)


  1. APP启动主要流程
  • APP启动主要流程: 点击icon -> 加载动态连接库等 -> 映像文件加载imageLoader -> runtime -> load -> main -> delegate。

  1. iOS的沙盒机制
  • 出于安全考虑,iPhone对于安装在上面的应用程序有所限制,这个限制就是应用程序只能在为该改程序建立的文件系统中读取文件,不能够去其它地方访问,此区域被成为沙盒,因此全部的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。整体来讲沙盒就是一种独立、安全、封闭的空间。沙盒(sandbox)的核心内容是:sandbox对应用程序执行各类操做的权限限制。

  • 沙盒的特色:

    1. 每一个应用程序都有本身的存储空间。
    2. 每一个应用程序都不能够翻过本身的围墙去访问别的存储空间的内容。(已经越狱的除外)
    3. 在访问别人沙盒内的数据时须要访问权限。
  • 应用程序沙盒目录下有三个文件夹Documents、Library(下面有Caches和Preferences目录)、tmp。

    Documents:保存应用运行时生成的须要持久化的数据iTunes会自动备份该目录。苹果建议将在应用程序中浏览到的文件数据保存在该目录下。
    Library/Caches:通常存储的是缓存文件,例如图片视频等,此目录下的文件不会再应用程序退出时删除,在手机备份的时候,iTunes不会备份该目录。
    Library/Preferences:保存应用程序的全部偏好设置iOS的Settings(设置),咱们不该该直接在这里建立文件,而是须要经过NSUserDefault这个类来访问应用程序的偏好设置。iTunes会自动备份该文件目录下的内容。
    tmp:临时文件目录,在程序从新运行的时候,和开机的时候,会清空tmp文件夹。

以上是Fabric本身总结的一些面试心经,有兴趣的朋友能够加我微信共同讨论:justlikeitRobert。各位老铁看了我这篇文章以后,若是找到了满意的工做,别忘了和Fabric一块儿分享成功喜悦,一分也是爱,哈哈!