iOS基础 (iOS面试题一)

若是你不知道你接下来须要补充学习哪一部分知识,作点面试题吧。javascript

基础部分

一、线程和进程有什么区别

进程是一个程序执行的实例,是资源分配的最小单位

线程是进程中的一个实例,是操做系统能够识别的最小执行和调度单位
复制代码

那么,线程和进程与堆、栈之间的关系?php

栈是线程独有的,保存其运行状态和局部自动变量,栈空间是线程安全的,栈被自动分配到进程的内存空间,栈内存无需开发管理,系统自动管理

堆在操做系统初始化进程的时候分配,运行过程能够要求更多额外的堆内存,可是须要返回,否则呢就是内存泄露
复制代码

二、说一下线程之间的通讯

例如在多线程并发条件下,为了让线程之间能够更方便的共同完成一个任务,须要一些协调通讯,采起的通讯方式就是 等待、唤起。

也就是  wait()  和 notify()、 notifyAll()
复制代码

三、当用一个不存在的key来查找两个不一样长度的字典,那么哪一个效率会高?

表面上看多是同样快,由于字典底层都用了哈希表,查找的时间复杂度为 O(1),(最差的时候是O(n))都是同样的,可是可能会因为两个哈希表的负载因子不一样,却是查找的时间也是不一样的。
复制代码

四、什么是指针常量和常量指针

指针常量是 常量,指针修饰它,这个常量的值是一个指针 int a; int *const b = &a;

常量指针本质是指针,常量修饰它  const int *p; 
复制代码

五、不借用第三个变量,如何交换两个变量的值?

算术运算

int a,b;
a=10;b=12;
a=b-a; //a=2;b=12
b=b-a; //a=2;b=10
a=b+a; //a=12;b=10

位运算 异或

int a=10,b=12; //a=1010^b=1100;
a=a^b; //a=0110^b=1100;
b=a^b; //a=0110^b=1010;
a=a^b; //a=1100=12;b=1010;


栈实现

int exchange(int x,int y) { 
stack S; 
push(S,x); 
push(S,y); 
x=pop(S); 
y=pop(S); 
}
复制代码

六、用递归算法求1到n的和

func add(n: Int) -> Int {
    var sum = 0
    if n > 0 {
        sum = n + add(n: n - 1)
    } else {
        sum = 0
    }
    return sum
}
复制代码

七、100个数字,求最大值的时间复杂度

须要一轮遍历   O(n)
复制代码

八、http 的 POST 和 GET 啥区别?

GET请求的数据会附在URL以后

POST把提交的数据则放置在是HTTP包的包体中

GET请求URL受浏览器影响 因此有长度限制

POST没有,通常服务器会作POST数据长度的限制

POST的数据传输不是直接拼接URL 因此相对安全一些

复制代码

九、http和https的区别,说一下http和https的请求过程?

http + ssl/tls = https

主要介绍一下,ssl的验证过程  保证安全和数据完整性
复制代码

十、如何用HTTP实现长链接?

web端:
Connection:keep-alive

服务器在闲置时候会向客户端发生侦测包,默认闲置时间是2个小时

移动端:
基于tcp的长链接,socket编程技术

复制代码

十二、聊下HTTP post的body体使用form-urlencoded和multipart/form-data的区别。

application/x-www-form-urlencoded:窗体数据被编码为名称/值对。这是标准的编码格式。

multipart/form-data:窗体数据被编码为一条消息,页上的每一个控件对应消息中的一个部分
复制代码

1三、通讯底层原理

OSI采用了分层的结构化技术,共分七层:

物理层:为设备间的数据通讯提供传输媒体和互连设备,光纤、无线信道等等

数据链路层:为网络层提供数据传送服务的,包括链路链接的创建、拆除和分离;对帧的收发顺序控制

网络层:数据传送的单位是分组或者包,网络层在给两个不一样地理位置的主机之间提供

传输层:定义了一些传输数据的协议和端口号,TCP, UDP;主要从下层接收的数据进行分段和传输,到达目的地后再重组

会话层:经过传输层创建数据传输通道,主要在你的系统之间发起会话或者接受会话请求(IP、MAC、主机名称)

表示层:可确保一个系统的应用层所发送的信息能够被另外一个系统的应用层读取,主要作的就是把应用层提供的信息变换为可以共同理解的形式,提供字符代码,数据格式,控制信息格式,加密等的统一表示。

应用层:为用户的应用程序提供网络服务

TCP/IP 采用四层结构:

网络接口层:硬件、帧头帧尾的添加

网络互联层:肯定目标计算机的IP地址

传输层:TCP,肯定如何传输

应用层:app
复制代码

1四、介绍一下XMPP?

XMPP是一种以XML为基础的开放式实时通讯协议。

XMPP 是一种很相似于http协议的一种数据传输协议,它的过程就如同“解包装–〉包装”的过程,用户只须要明白它接受的类型,并理解它返回的类型,就能够很好的利用xmpp来进行数据通信。基于可扩展标记语言(XML)的协议 

XMPP基本结构:客户端 服务器 网关 

通讯可以在这三者的任意两个之间双向发生。服务器同时承担了客户端信息记录,链接管理和信息的路由功能。网关承担着与异构即时通讯系统的互联互通,异构系统能够包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端经过TCP/IP链接到单服务器,而后在之上传输XML。


XMPP核心协议通讯的基本模式就是先创建一个stream,而后协商一堆安全之类的东西,中间通讯过程就是客户端发送XML Stanza,一个接一个的。服务器根据客户端发送的信息以及程序的逻辑,发送XML Stanza给客户端。可是这个过程并非一问一答的,任什么时候候都有可能从一方发信给另一方。通讯的最后阶段是关闭流,关闭TCP/IP链接。


客户端1  <--> XMPP服务器  <--> 客户端2

两个客户端能够分别和服务器通讯,可是客户端之间的通讯必须通过服务器

用于一些即时通讯
复制代码

1五、ssl / tls证书 做用

保障通讯双方的可靠性,通讯的安全和数据的完整性
复制代码

https和ssl在握手方向有什么区别?css

一个是链接握手,一个是安全校验握手,描述一下二者握手过程
复制代码

具体原理见参考中的 网络知识整理。html

1六、socket链接和 http 链接区别

Http是基于Tcp的,而Socket是一套编程接口让咱们更方便的使用Tcp/Ip协议;

Http是应用层协议,在Tcp/Udp上一层。

一、Http是基于"请求-响应"的,服务器不能主动向客户端推送数据,只能借助客户端请求到后向客户端推送数据,而Sokcet双方随时能够互发数据;

二、Http不是持久链接的,Socket用Tcp是持久链接;

三、Http基于Tcp,Socket能够基于Tcp/Udp;

四、Http链接是经过Socket实现的;

五、Http链接后发送的数据必须知足Http协议规定的格式:请求头、请求头和请求体,而Socket链接后发送的数据没有格式要求。
复制代码

Socket的实现原理及 Socket之间是如何通讯的java

网络上的两个程序经过一个双向的通讯链接实现数据的交换,这个链接的一端称为一个socket。

创建网络通讯链接至少要一对端口号(socket)。

socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员作网络开发所用的接口,这就是Socket编程接口;

HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通讯的能力。

socket分为客户端和服务端,客户端发送链接请求,服务端等待链接请求

当服务端socket监听到客户端socket的请求时,就响应客户端套接字的请求,创建一个新的线程,把服务端套接字描述发送给客户端,一旦客户端确认了此描述,双方正式创建链接,而服务端socket继续处于监听状态,等待其余链接请求
复制代码

1七、说一下HTTP协议以及常用的code码的含义。

一些常见的状态代码为:

200 - 服务器成功返回网页
300 - 重定向之类
404 - 请求的网页不存在
503 - 服务器暂时不可用
复制代码

1八、网络拥塞控制、tcp的慢启动

不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增长拥塞窗口的大小。

简单来讲 拥塞控制就是防止过多的数据注入网络中,这样能够使网络中的路由器或链路不致过载。

原理:

请求发送,每次按窗口数发送数据,收到一个确认就把窗口值加一,逐渐递增,这就是慢开始算法

当网络拥塞,窗口从新回 1 最大慢开始门限变为出现问题的网络拥塞窗口值的一半 这就是拥塞避免算法

而后再次循环。
复制代码

1九、TCP 三次握手、四次挥手,为何 断开链接是4次挥手呢

由于TCP链接的时候,最后一次握手表示收到服务器确认的请求能够携带须要发给服务器的数据,三次是最短可能

四次挥手是确保客户端 没有消息要发给服务端,服务端也没有消息要发给客户端了,也能够不用四次,可是就会增长空等待的资源浪费

复制代码

20、聊一聊二叉树搜索(Binary search tree)

复制代码

参考

网络知识整理ios

leetCodegit

剑指offer程序员

OC 部分

extern的做用

告诉编译器,这个全局变量在本文件找不到就去其余文件去找。若有必要须要使用#import "x.h"这样编译器才知道到哪里去找。使用extern前要保证对应变量被编译过,想要访问全局变量能够使用extern关键字(全局变量定义不能有static修饰)。github

好比 A文件中 我声明的全局变量 NSInteger age = 10; 可是属性也不能直接获取。 以下在B文件中能够获取到 :

extern NSInteger age;
age ++;

NSLog(@"%d",age); // 11

若是不想让age被找到,声明为static

复制代码

const的做用

常量定义,修饰一个常量

int a = 1;
int b = 2;

int const *p = &a
// 若是const修饰的是*p,那么*p的值是不能改变的,也就是p中存放的a的地址中的值没法改变,可是p的值是能够改变的(也就是p此时能够改变指向)
p = &b;
printf("---");
printf("%p",&b);
printf("---");
printf("%p",p);
printf("---");
printf("%d",*p);

//输出 ---0x7ffeea7e89f8---0x7ffeea7e89f8---2


int *const p = &a;
// 若是const修饰的是p,那么p的值是不能改变的,也就是p中存放的a的地址没法改变(p是int类型的指针变量)。可是*p是能够变化的,咱们并无用const去修饰*p,因此能够经过*p去改变a的值
*p = b;

复制代码

static的做用

static NSInteger staticValue = 0;

static关键字修饰局部变量:

当static关键字修饰局部变量时,只会初始化一次且在程序中只有一分内存

关键字static不能够改变局部变量的做用域,但可延长局部变量的生命周期(直到程序结束才销毁)

static关键字修饰全局变量:

当static关键字修饰全局变量时,做用域仅限于当前文件,外部类是不能够访问到该全局变量的(即便在外部使用extern关键字也没法访问)

若是须要直接访问  须要引用头文件

复制代码

宏定义

宏定义属于预编译指令,在程序运行以前已经编译好了的

#define M_PI 3.14159265358979323846264338327950288

#define SELF(x) x //NSLog(@"Hello %@",SELF(name));

#define PLUS(x,y) x + y //printf("%d",PLUS(3,2));

#define MIN(A,B) A < B ? A : B // int a = MIN(1,2);

#define NSLog(format, ...) do { \ fprintf(stderr, "<%s : %d> %s\n", \ [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \ __LINE__, __func__); \ (NSLog)((format), ##__VA_ARGS__); \ fprintf(stderr, "-------\n"); \ } while (0)

复制代码

一、block分几种?分别是怎么样产生的?block的实质是什么?

在内存角度来看,block分为 全局 、栈 和 堆 三种类型,

有强引用的block就属于堆内存block, 

只用到外部局部变量、成员属性变量、没有强指针引用的block属于栈block

只引用全局变量或静态变量的block,生命周期和程序生命周期同样的block就是全局block

block的实质是一个对象,一个结构体
复制代码

二、__block修饰的变量为何能在block里面能改变其值?

__block修饰符标记后,block就会访问标记变量自己内存地址,而未标记对象则访问截获拷贝后的变量的内存地址
复制代码

三、block应该用copy关键字仍是strong关键字?

block 使用 copy 是从 MRC 遗留下来的“传统”

在 MRC 中,方法内部的 block 是在栈区的,使用 copy 能够把它放到堆区。

在 ARC 中写不写都行

对于 block 使用 copy 仍是 strong 效果是同样的,但写上 copy 也无伤大雅,还能时刻提醒咱们:编译器自动对 block 进行了 copy 操做。若是不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操做”,他们有可能会在调用以前自行拷贝属性值。这种操做多余而低效。
复制代码

四、@property 的本质是什么?

@property = ivar + getter + setter;

“属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)

“属性” (property)做为 Objective-C 的一项特性,主要的做用就在于封装对象中的数据。 Objective-C 对象一般会把其所须要的数据保存为各类实例变量。实例变量通常经过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。

复制代码

五、ivar、getter、setter 是如何生成并添加到类中的

引伸一个问题:@synthesize 和 @dynamic 分别有什么做用?web

完成属性(@property)定义后,编译器会自动编写访问这些属性所需的方法,此过程叫作“自动合成”(autosynthesis)。

咱们也能够在类的实现代码里经过 @synthesize 语法来指定实例变量的名字。
@synthesize lastName = _myLastName;

或者经过 @dynamic 告诉编译器:属性的 settergetter 方法由用户本身实现,不自动生成。

@property有两个对应的词,

一个是@synthesize(合成实例变量),一个是@dynamic。

若是@synthesize@dynamic都没有写,那么默认的就是 @synthesize var = _var;

// 在类的实现代码里经过 @synthesize 语法能够来指定实例变量的名字。(@synthesize var = _newVar;)
1. @synthesize 的语义是若是你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2. @dynamic 告诉编译器,属性的settergetter方法由用户本身实现,不自动生成(如,@dynamic var)。
复制代码

五、用@property声明的 NSString / NSArray / NSDictionary 常用 copy 关键字,为何?若是改用strong关键字,可能形成什么问题?

@property 声明 NSStringNSArrayNSDictionary 常用 copy 关键字,是由于他们有对应的可变类型:NSMutableStringNSMutableArrayNSMutableDictionary,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。

1. 由于父类指针能够指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本。
2. 若是咱们使用是 strong ,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性。

//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。

复制代码

这里还有一个引伸问题:

NSMutableArray 若是用 copy修饰了会出现什么问题?

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArray0 addObject:]: unrecognized selector sent to instance 0x600000a100c0'

因为使用的是copy属性,自己的可变属性默认有一个不可变的拷贝 NSArray ,因此咱们用这个可变数组去添加元素的时候,找不到对应方法而发生crash。
复制代码

六、浅拷贝和深拷贝的区别?

浅拷贝(copy):只复制指向对象的指针,而不复制引用对象自己。
深拷贝(mutableCopy):复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。

只有对不可变对象进行copy操做是指针复制(浅复制),其它状况都是内容复制(深复制)

复制代码

八、如何让本身的类用copy修饰符

若想令本身所写的对象具备拷贝功能,则需实现 NSCopying 协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopyingNSMutableCopying 协议。
具体步骤:
    1. 需声明该类听从 NSCopying 协议
    2. 实现 NSCopying 协议的方法。
        // 该协议只有一个方法: 
        - (id)copyWithZone:(NSZone *)zone;
        // 注意:使用 copy 修饰符,调用的是copy方法,其实真正须要实现的是 “copyWithZone” 方法。

复制代码

九、ViewController生命周期

按照执行顺序排列:
1. initWithCoder:经过nib文件初始化时触发。
2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每一个对象。     

//若是不是nib初始化 上面两个换成 initWithNibName:bundle:

3. loadView:开始加载视图控制器自带的view。
4. viewDidLoad:视图控制器的view被加载完成。  
5. viewWillAppear:视图控制器的view将要显示在window上。
6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
9. viewDidAppear:视图控制器的view已经展现到window上。 
10. viewWillDisappear:视图控制器的view将要从window上消失。
11. viewDidDisappear:视图控制器的view已经从window上消失。
复制代码

十、OC的反射机制

1). class反射
    经过类名的字符串形式实例化对象。
        Class class = NSClassFromString(@"student"); 
        Student *stu = [[class alloc] init];
    将类名变为字符串。
        Class class =[Student class];
        NSString *className = NSStringFromClass(class);
2). SEL的反射
    经过方法的字符串形式实例化方法。
        SEL selector = NSSelectorFromString(@"setName");  
        [stu performSelector:selector withObject:@"Mike"];
    将方法变成字符串。
        NSStringFromSelector(@selector*(setName:));
复制代码

十一、self 和 super

self 是类的隐藏参数,指向当前调用方法的这个类的实例。
super是一个Magic Keyword,它本质是一个编译器标示符,和self是指向的同一个消息接收者。
不一样的是:super会告诉编译器,调用class这个方法时,要去父类的方法,而不是本类里的。

复制代码

十二、id 和 NSObject*的区别

id是一个 objc_object 结构体指针,定义是
typedef struct objc_object *id
id能够理解为指向对象的指针。全部oc的对象 id均可以指向,编译器不会作类型检查,id调用任何存在的方法都不会在编译阶段报错,固然若是这个id指向的对象没有这个方法,该崩溃仍是会崩溃的。

NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法不然就要作强制类型转换。

不是全部的OC对象都是NSObject的子类,还有一些继承自NSProxyNSObject *可指向的类型是id的子集。

复制代码

引伸: id 和 instancetype 的区别

instancetype的做用,就是使那些非关联返回类型的方法返回所在类的类型!

相同点:
均可以做为方法的返回类型

不一样点:
instancetype能够返回和方法所在类相同类型的对象,id只能返回未知类型的对象
instancetype只能做为返回值,不能像id那样做为参数
复制代码

1三、NSDictionary的实现原理是什么?

一:字典原理

NSDictionary(字典)是使用hash表来实现key和value之间的映射和存储的

方法:- (void)setObject:(id)anObject forKey:(id)aKey;

Objective-C中的字典NSDictionary底层实际上是一个哈希表



复制代码

引伸:字典的查询工做原理

字典的工做原理 ?怎100w个中是怎么快速去取value?


复制代码

1四、大家的App是如何处理本地数据安全的(好比用户名的密码)?

本地尽可能不存储用户隐私数据、敏感信息

使用如AES256加密算法对数据进行安全加密后再存入SQLite中

或者数据库总体加密

存放在keychain里面

向Keychain中存储数据时,不要使用kSecAttrAccessibleAlways,而是使用更安全的kSecAttrAccessibleWhenUnlocked或kSecAttrAccessibleWhenUnlockedThisDeviceOnly选项。 

AES  DES
复制代码

1五、遇到过BAD_ACCESS的错误吗?你是怎样调试的?

90%的错误来源在于对一个已经释放的对象进行release操做, 或者说对一个访问不到的地址进行访问,多是因为些变量已经被回收了,亦多是因为使用栈内存的基本类型的数据赋值给了id类型的变量。

例如:

id x_id = [self performSelector:@selector(returnInt)];
    
- (int)returnInt { return 5; }

上面经过id去接受int返回值,int是存放在栈里面的,堆内存地址如何找获得,天然就是 EXC_BAD_ACCESS。

复制代码

处理方法

一、xcode能够用僵尸模式打印出对象 而后经过对象查找对应的代码位置

一、Edit Scheme - Diagnositics - Memory Management 勾选 Zombie ObjectsMalloc Stack

二、会打印出 
cyuyan[7756:17601127] *** -[UIViewController respondsToSelector:]: message sent to deallocated instance 0x7fe71240d390

这句开启僵尸模式后打出来的输出,包含了咱们须要的 进程pid、崩溃地址,终端经过下面命令查看堆栈日志来找到崩溃代码

三、查找日志
sudo malloc_history 7756 0x7fe71240d390
复制代码

二、在 other c flags中加入-D FOR_DEBUG(记住请只在Debug Configuration下加入此标记)。这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。重写一个object的respondsToSelector方法,打印报错前的

#ifdef _FOR_DEBUG_ 
-(BOOL) respondsToSelector:(SEL)aSelector {  
    printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);  
    return [super respondsToSelector:aSelector];  
}  
#endif
复制代码

三、经过instruments的Zombies

引伸:怎么定位到野指针的地方。若是还没定位到,这个对象被提早释放了,怎么知道该对象在什么地方释放的

一种是多线程,一种是野指针。这两种Crash都带随机性,咱们要让随机crash变成不随机

把这一随机的过程变成不随机的过程。对象释放后在内存上填上不可访问的数据,其实这种技术其实一直都有,xcode的Enable Scribble就是这个做用。

一、Edit Scheme - Diagnositics - Memory Management 勾选 Malloc Scribble

暂时未解决

复制代码

1六、如何设计一个通知中心

单例设计一个NotificationCenter,NSPointerArray 保存 observer,对象销毁 observer自动变null

复制代码

1七、KVO、KVC的实现原理

KVC( 键值编码 )实现

1.KVC是基于runtime机制实现的

2、能够访问私有成员变量、能够间接修改私有变量的值

[object setValue:@"134567" forKey:@"uid"];

就会被编译器处理成:
// 首先找到对应sel
SEL sel = sel_get_uid("setValue:forKey:");
// 根据object->isa找到sel对应的IMP实现指针
IMP method = objc_msg_lookup (object->isa,sel);
// 调用指针完成KVC赋值
method(object, sel, @"134567", @"uid");

KVC键值查找原理

setValue:forKey:搜索方式

1、首先搜索setKey:方法.(key指成员变量名, 首字母大写)
2、上面的setter方法没找到, 若是类方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的顺序搜索成员名。(这个类方法是NSKeyValueCodingCatogery中实现的类方法, 默认实现为返回YES)
3、若是没有找到成员变量, 调用setValue:forUnderfinedKey:

valueForKey:的搜索方式

1、首先按getKey, key, isKey的顺序查找getter方法, 找到直接调用. 若是是BOOLint等内建值类型, 会作NSNumber的转换.
2、上面的getter没找到, 查找countOfKey, objectInKeyAtindex, KeyAtindexes格式的方法. 若是countOfKey和另外两个方法中的一个找到, 那么就会返回一个能够响应NSArray全部方法的代理集合的NSArray消息方法.
3、还没找到, 查找countOfKey, enumeratorOfKey, memberOfKey格式的方法. 若是这三个方法都找到, 那么就返回一个能够响应NSSet全部方法的代理集合.
4、仍是没找到, 若是类方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey, key, iskey的顺序搜索成员名.
5、再没找到, 调用valueForUndefinedKey.

复制代码

KVO实现 键值观察、观察者模式的一种应用

简答

1.KVO是基于runtime机制实现的

2.当某个类的属性对象第一次被观察时,系统就会在运行期动态地建立该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

3.若是原类为Person,那么生成的派生类名为NSKVONotifying_Person

4.每一个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

5.键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变以前, willChangeValueForKey:必定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

深刻

1.Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态建立一个新的名为:?NSKVONotifying_A的新类,该类继承自对象A的本类,且KVO为NSKVONotifying_A重写观察属性的setter?方法,setter?方法会负责在调用原?setter?方法以前和以后,通知全部观察对象属性值的更改状况。

2.NSKVONotifying_A类剖析:在这个过程,被观察对象的 isa 指针从指向原来的A类,被KVO机制修改成指向系统新建立的子类 NSKVONotifying_A类,来实现当前类属性值改变的监听;

3.因此当咱们从应用层面上看来,彻底没有意识到有新的类出现,这是系统“隐瞒”了对KVO的底层实现过程,让咱们误觉得仍是原来的类。可是此时若是咱们建立一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,由于系统在注册监听的时候动态建立了名为NSKVONotifying_A的中间类,并指向这个中间类了。

4.(isa 指针的做用:每一个对象都有isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。因此对象注册为观察者时,isa指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。)?于是在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。

5.子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的先后分别调用2个方法: 被观察属性发生改变以前,willChangeValueForKey:被调用,通知系统该 keyPath?的属性值即将变动;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath?的属性值已经变动;以后,?observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter?方法这种继承方式的注入是在运行时而不是编译时实现的。

复制代码

1九、category为何不能添加属性?

category 它是在运行期决议的,由于在运行期,对象的内存布局已经肯定,若是添加实例变量就会破坏类的内部布局,这对编译型语言来讲是灾难性的。

extension看起来很像一个匿名的category,可是extension和有名字的category几乎彻底是两个东西。 extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一块儿造成一个完整的类,它伴随类的产生而产生,亦随之一块儿消亡。extension通常用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,因此你没法为系统的类好比NSString添加extension。

可是category则彻底不同,它是在运行期决议的。 
就category和extension的区别来看,咱们能够推导出一个明显的事实,extension能够添加实例变量,而category是没法添加实例变量的。

那为何 使用Runtime技术中的关联对象能够为类别添加属性。

其缘由是:关联对象都由AssociationsManager管理,AssociationsManager里面是由一个静态AssociationsHashMap来存储全部的关联对象的。这至关于把全部对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不一样对象的指针地址必定是不一样的),而这个map的value又是另一个AssociationsHashMap,里面保存了关联对象的kv对。

如合清理关联对象?

runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,若是有,会调用_object_remove_assocations作关联对象的清理工做。(详见Runtime的源码)
复制代码

Objective-C Associated Objects 的实现原理

20、说一下runloop和线程的关系

runloop与线程是一一对应的

runloop是来管理线程的

线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚建立时并无 RunLoop,若是你不主动获取,那它一直都不会有。RunLoop 的建立是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)
复制代码

2一、说一下autoreleasePool的实现原理

autoreleasePool是一个延时release的机制, 在自动释放池被销毁或耗尽时,会向池中的全部对象发送release消息,释放全部autorelease对象。

ARC下,咱们使用@autoreleasepool{}来使用一个自动释放池

AutoreleasePool并无单独的结构,而是由若干个AutoreleasePoolPage做为结点以双向链表的形式组合而成。整个链表以堆栈的形式运做。
一、每个指针表明一个加入到释放池的对象 或者是哨兵对象,哨兵对象是在 @autoreleasepool{} 构建的时候插入的

二、当自动释放池 pop的时候,全部哨兵对象以后的对象都会release

三、链表会在一个Page空间占满时进行增长,一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,链接链表,后来的autorelease对象在新的page加入。


主线程:

主线程runloop中注册了两个Observer,回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一个oberver监听 当从休眠状态即将进入loop的时候 ,这个时候,构建自动释放池

第二个oberver监听 当准备进入休眠状态的时候,调用 objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并建立新池


子线程:

runloop默认不开启,不会自动建立自动释放池,在须要使用自动释放池的时候,须要咱们手动建立、添加自动释放池,此时若是全部的异步代码都写在自动释放池中,也能够理解为当子线程销毁的时候,自动释放池释放

复制代码

自动释放池(sunnyxx)

自动释放池

2二、说一下简单工厂模式,工厂模式以及抽象工厂模式?

简单工厂模式:根据外部信息就能够决定建立对象,全部产品都经过工厂判断就建立,体系结构很明显,缺点就是集中了全部的产品建立逻辑,耦合过重。

工厂模式:产品的各自建立逻辑下发到各自的工厂类中,必定程度达到解耦合。 多态性,产品构建逻辑能够具体到对应的产品工厂类中,更加清晰。 当我须要新产品的时候,只须要添加一个新的产品工厂,实现抽象工厂的产品产出方法,产出对应的产品。不影响客户逻辑。

抽象工厂模式:当有多个产品线,须要多个工厂分别生产不一样的产品线产品,这个时候咱们抽象出工厂逻辑,产品也抽象出产品类型,工厂抽象类只须要构建返回抽象产品的方法便可,更深程度的解耦。具体的什么工厂产什么产品逻辑下发到实际工厂实现。 即便添加新产品也不影响抽象工厂和抽象产品的逻辑。

复制代码

2三、如何设计一个网络请求库

网络请求库须要的功能:

一、在任意位置发起请求

二、请求表单的建立 (url拼接、参数填充、http请求方法确认)

三、UI-Loading

四、数据解析

五、异常处理

六、结果提示

本身分装的 一个 API 网络请求库 
复制代码

2四、说一下多线程,你日常是怎么用的?

经常使用的有 GCD 和 NSOperationNSThread 

NSThread 用于获取当前线程等操做

GCD 和 NSOperation 实现多线程操做不须要本身管理线程,操做简单

GCD block的使用方式比NSOperation 适合简单操做,NSOperation 对象级操做方法更多,更复杂操做适用
复制代码

2五、说一下UITableViewCell的卡顿你是怎么优化的?

通常简单的UITableViewCell都不会卡顿,TableView自己有Cell重用机制,但一些复杂的自适应高度的cell比较容易产生卡顿。

1、避免cell的过多从新布局,差异太大的cell之间不要选择重用。

2、提早计算并缓存cell的高度,内容

3、尽可能减小动态添加View的操做

4、减小全部对主线程有影响的无心义操做

5、cell中的图片加载用异步加载,缓存等

6、局部更新cell

7、减小没必要要的渲染时间,好比少用透明色之类的
复制代码

2八、什么是ARC?(ARC是为了解决什么问题诞生的?)

ARC全称是 Automatic Reference Counting,是Objective-C的内存管理机制。简单地来讲,就是代码中自动加入了retain/release,原先须要手动添加的用来处理内存管理的引用计数的代码能够自动地由编译器完成了。

ARC的使用是为了解决对象retain和release匹配的问题。之前手动管理形成内存泄漏或者重复释放的问题将不复存在。

之前须要手动的经过retain去为对象获取内存,并用release释放内存。因此之前的操做称为MRC (Manual Reference Counting)。

复制代码

2九、请解释如下keywords的区别: assign vs weak, _block vs _weak

weakassign都是引用计数不变,两个的差异在于,weak用于object type,就是指针类型,而assign用于简单的数据类型,如int BOOL 等。

assign看起来跟weak同样,其实不能混用的,assign的变量在释放后并不设置为nil(和weak不一样),当你再去引用时候就会发生错误,崩溃,EXC_BAD_ACCESS.

assign 能够修饰对象么? 能够修饰,编译器不会报错,可是访问过程当中对象容易野指针

__block 用于标记须要在block内部修改的变量,__weak 用于防止引用循环
复制代码

30、使用atomic必定是线程安全的吗?

atomic只能保证操做也就是存取属性的时候的存取方法是线程安全的,并不能保证整个对象就是线程安全的。

好比NSMutableArray 设置值得时候是线程安全的,可是经过objectAtIndex访问的时候就再也不是线程安全的了。仍是须要锁来保证线程的安全。

复制代码

3一、描述一个你遇到过的retain cycle例子

VC中一个强引用block里面使用self

代理使用强引用

sqllite多线程抢写入操做
复制代码

3二、+(void)load; +(void)initialize; 有什么用处?方法分别在何时调用的?

+(void)load;

当类对象被引入项目时, runtime 会向每个类对象发送 load 消息。
load 方法会在每个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类。
因为 load 方法会在类被 import 时调用一次,而这时每每是改变类的行为的最佳时机,在这里能够使用例如 method swizlling 来修改原有的方法。
load 方法不会被类自动继承。

+(void)initialize;

也是在第一次使用这个类的时候会调用这个方法,也就是说 initialize 也是懒加载

总结:

在 Objective-C 中,runtime 会自动调用每一个类的这两个方法
1.+load 会在类初始加载时调用
2.+initialize 会在第一次调用类的类方法或实例方法以前被调用
这两个方法是可选的,且只有在实现了它们时才会被调用
二者的共同点:两个方法都只会被调用一次

复制代码

3三、谈一谈消息发送 或者 对runtime的理解, 说一下工做中是如何使用runtime的?看过runtime源码吗?

runtime是 oc 语言特性,方法调用采用消息发送的方式,直到项目运行阶段才能最终肯定,而且还能够动态添加成员变量与方法。

项目中用的多的runtime应该是方法实现的替换,动态属性的添加,KVO,performSelector,消息转发之类

复制代码

3四、如何高性能的给UIImageView加个圆角?

如何高性能的给 UIImageView 加个圆角?

很差的解决方案:使用下面的方式会强制Core Animation提早渲染屏幕的离屏绘制, 而离屏绘制就会给性能带来负面影响,会有卡顿的现象出现。

self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;

正确的解决方案:使用绘图技术

- (UIImage *)circleImage {
    // NO表明透明
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    // 得到上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一个圆
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 将图片画上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    return image;
}
还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是经过绘图技术来实现的。

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
                       cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];

复制代码

3六、设计一个检测主线程卡顿的方案

卡顿的缘由就是耗时长,设计一个检测主线程方法执行时间过长的方案
复制代码

3七、说几个你在工做中使用到的线程安全的例子

多线程同时操做同一个数据源的时候

AFNetworking 对于session的构建等都是线程安全的
复制代码

3八、用过哪些锁?哪些锁的性能比较高?谈下Objective C都有哪些锁机制,你通常用哪一个?

经常使用的锁有 NSLock@synchronized代码块、信号量 dispatch_semaphore_t

信号量性能最高

@synchronized代码块 最方便
复制代码

3二、说一下静态库和动态库之间的区别

静态库 

.a.framework 结尾
是一个已经编译好了的集合,使用的时候链接器会把静态库合并到可执行文件中。


动态库  
.tbd.framework结尾

编译过程不会被连接到目标代码中, 只会将动态库头文件添加到目标app的可执行文件,程序运行的时候被添加在独立于app的内存区域。

复制代码

3六、说一下你对架构的理解? 技术架构如何搭建?

设计一个架构 须要考虑多个层次

1、代码风格、例如 代码整齐,一个类不能干两个事情,目录设定要清晰一眼就知道是干什么的,不要设置什么common module之类的目录,面向协议开发,瘦Controller啊等

2、规范业务块的分层,例如 MVC 或者 MVVM,统一的业务处理分层,让业务代码更清晰,耦合性也低

3、基础层的定义, 开发帮助库,例如 网络库,数据持久化库,路由库,要求易于扩展、易于测试,易于理解,让开发小伙伴上手快,接口方法设定要灵活,减小开发小伙伴的使用成本

4、组件化,一个架构自己也须要良好的封装,合理的组件化可让功能更清晰,耦合性也更低,

大的组件化就是项目层级,把不常改动的基础库沉底,好比放pod中,常常扩展的内容放在工程里面,独立的业务块能够经过工程的方式依赖

小的组件化就是UI方面,统一封装管理UI轮子,避免一个东西出现不少份的状况

复制代码

参考文章一

参考文章二

3七、为何必定要在主线程里面更新UI?

UIKit 不是线程安全的,容易产生UI更新上的混乱
复制代码

3九、讲讲你用Instrument优化动画性能的经历吧

core animation的使用

time profiler 的使用
复制代码

40、loadView是干吗用的?

self.view的初始化,根据xib初始化或者init初始化
复制代码

4一、viewWillLayoutSubView

controller layout触发的时候,开发者有机会去从新layout本身的各个subview。说UI熟悉的必定要知道。

当子View发生frame的变更的时候会触发layoutsubView,咱们能够在这个方法中提早作一些预处理
复制代码

4二、GCD里面有哪几种Queue?你本身创建过串行queue吗?背后的线程模型是什么样的?

两种queue,串行和并行。

main queue是串行,global queue是并行。

有些开发者为了在工做线程串行的处理任务会本身创建一个serial queue。背后是苹果维护的线程池,各类queue要用线程都是这个池子里取的。

复制代码

4三、用过coredata或者sqlite吗?读写是分线程的吗?遇到过死锁没?咋解决的?

sqlite 一个线程A操做写入、一个线程B操做读取,在第一个线程等待写入的过程当中也发起写入,写入操做在普通的事务操做 begin trancaction  commit transaction ,这种状况就会死锁

两个线程都争取写入操做,由于在A线程等待变成排他锁的过程当中处于待定锁状态,并不会拒绝B线程的保留锁的获取,致使B线程一直不释放共享锁,A就一直得不到排他锁,形成死锁。


单个线程能够死锁(main thread里dispatch_sync到main queue),

多个线程直接也能够死锁(A,B线程互相持有对方须要的资源且互相等待)。
复制代码

关于sqllite锁

4四、NSString如何计算字符的个数?

- (int)myStrLength:(NSString *)str {
    int length = 0;
    char * p_str = [str cStringUsingEncoding:NSUTF8StringEncoding];
    for (int i = 0; i < [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; i++) {
        if (*p_str) {
            p_str++;
            length++;
        } else {
            p_str++;
        }
    }
    return length;
}
复制代码

4五、PKI体系(其实就是CA证书验证体系)当中加密和签名有什么区别?

签名密钥对用于数据的完整性检测,保证防伪造与防抵赖,签名私钥的遗失,并不会影响对之前签名数据的验证,所以,签名私钥无须备份,所以,签名密钥不须要也不该该须要第三方来管理,彻底由持有者本身产生;

加密密钥对用于数据的加密保护,若加密私钥遗失,将致使之前的加密数据没法解密,这在实际应用中是没法接受的,加密私钥应该由可信的第三方(即一般所说的CA)来备份,以保证加密数据的可用性,所以,加密密钥对能够由第三方来产生,并备份。

一个加密 一个保证完整性
复制代码

4七、数据库建表的时候索引有什么用?

能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。

经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。
复制代码

4九、iOS下如何实现指定线程数目的线程池?

使用信号量

GCD的信号量机制(dispatch_semaphore)

信号量是一个整型值,有初始计数值;能够接收通知信号和等待信号。当信号量收到通知信号时,计数+1;当信号量收到等待信号时,计数-1;若是信号量为0,线程会阻塞,直到线程信号量大于0,才会继续下去。

使用信号量机制能够实现线程的同步,也能够控制最大并发数。如下是控制最大并发数的代码。


dispatch_queue_t workConcurrentQueue = dispatch_queue_create("cccccccc", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("sssssssss",DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(serialQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(workConcurrentQueue, ^{
    NSLog(@"thread-info:%@开始执行任务%d",[NSThread currentThread],(int)i);
    sleep(1);
    NSLog(@"thread-info:%@结束执行任务%d",[NSThread currentThread],(int)i);
    dispatch_semaphore_signal(semaphore);});
});
}
NSLog(@"主线程...!");

说明:从执行结果中能够看出,虽然将10个任务都异步加入了并发队列,但信号量机制控制了最大线程并发数,始终是3个线程在执行任务。此外,这些线程也没有阻塞线程。
复制代码

50、函数式编程当中的 first-class function是什么意思呢?

函数是一等公民

函数能像参数那样被传递到另外一个函数、从另外一个函数那像值同样被返回出来、函数能够赋值给变量或者存在数据结构中。
复制代码

51.遇到tableView卡顿嘛?会形成卡顿的缘由大体有哪些?

可能形成tableView卡顿的缘由有:

1.最经常使用的就是cell的重用, 注册重用标识符

若是不重用cell时,每当一个cell显示到屏幕上时,就会从新建立一个新的cell

若是有不少数据的时候,就会堆积不少cell。

若是重用cell,为cell建立一个ID,每当须要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,若是没有再从新建立cell

2.避免cell的从新布局

cell的布局填充等操做 比较耗时,通常建立时就布局好

如能够将cell单独放到一个自定义类,初始化时就布局好

3.提早计算并缓存cell的属性及内容

当咱们建立cell的数据源方法时,编译器并非先建立cell 再定cell的高度

而是先根据内容一次肯定每个cell的高度,高度肯定后,再建立要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提早估算高度告诉编译器,编译器知道高度后,紧接着就会建立cell,这时再调用高度的具体计算方法,这样能够方式浪费时间去计算显示之外的cell

4.减小cell中控件的数量

尽可能使cell得布局大体相同,不一样风格的cell能够使用不用的重用标识符,初始化时添加控件,

不适用的能够先隐藏

5.不要使用ClearColor,无背景色,透明度也不要设置为0

渲染耗时比较长

6.使用局部更新

若是只是更新某组的话,使用reloadSection进行局部更

7.加载网络数据,下载图片,使用异步加载,并缓存

8.少使用addView 给cell动态添加view

9.按需加载cell,cell滚动很快时,只加载范围内的cell

10.不要实现无用的代理方法,tableView只遵照两个协议

11.缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这二者同时存在才会出现“窜动”的bug。因此个人建议是:只要是固定行高就写预估行高来减小行高调用次数提高性能。若是是动态行高就不要写预估方法了,用一个行高的缓存字典来减小代码的调用次数便可

12.不要作多余的绘制工做。在实现drawRect:的时候,它的rect参数就是须要绘制的区域,这个区域以外的不须要进行绘制。例如上例中,就能够用CGRectIntersectsRectCGRectIntersectionCGRectContainsRect判断是否须要绘制image和text,而后再调用绘制方法。

13.预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,而后再绘制到屏幕;

14.使用正确的数据结构来存储数据。

复制代码

5三、让你设计一种机制检测UIViewController的内存泄漏,你会怎么作?Instrument是如何检测内存泄漏的

swizzle NavigationController 的 push 和 pop方法

pop了控制器后过几秒钟进行一遍判断,若是为nil表示已销毁,没有则表示内存泄露

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf assertNotDealloc];
});
复制代码

5四、经过[UIImage imageNamed:]生成的对象何时被释放?

这种图片加载方式带有图片缓存的功能,使用这种方式加载图片后,图片会自动加入系统缓存中,并不会当即释放到内存。一些资源使程序中常用的图片资源,
使用这种方式会加快程序的运行减小IO操做,但对于项目中只用到一次的图片,若是采用这种方案加载,会增致使程序的内存使用增长。

非缓存的加载方式
(UIImage *)imageWithContentsOfFile:(NSString *)path
(UIImage *)
:(NSData *)data
复制代码

5五、applicationWillEnterForeground和applicationDidBecomeActive都会在哪些场景下被调用?举例越多越好。

后台进入前台

通知中心回来

正常启动app
复制代码

5六、如何终止正在运行的工做线程?

block 中 return;

[thread cancle]
复制代码

5七、穷举iOS下全部的本地持久化方案。

plist

preference  NSUserDefault

NSKeyedArchiver

SQLite3

coreData

沙盒
复制代码

5八、项目中网络层如何作安全处理

一、尽可能使用https

https能够过滤掉大部分的安全问题。https在证书申请,服务器配置,性能优化,客户端配置上都须要投入精力,因此缺少安全意识的开发人员容易跳过https,或者拖到之后遇到问题再优化。https除了性能优化麻烦一些之外其余都比想象中的简单,若是没精力优化性能,至少在注册登陆模块须要启用https,这部分业务对性能要求比较低。

二、不要传输明文密码

不知道如今还有多少app后台是明文存储密码的。不管客户端,server仍是网络传输都要避免明文密码,要使用hash值。客户端不要作任何密码相关的存储,hash值也不行。存储token进行下一次的认证,并且token须要设置有效期,使用refresh token去申请新的token。

三、Post并不比Get安全

事实上,Post和Get同样不安全,都是明文。参数放在QueryString或者Body没任何安全上的差异。在Http的环境下,使用Post或者Get都须要作加密和签名处理。

四、不要使用301跳转

301跳转很容易被Http劫持攻击。移动端http使用301比桌面端更危险,用户看不到浏览器地址,没法察觉到被重定向到了其余地址。若是必定要使用,确保跳转发生在https的环境下,并且https作了证书绑定校验。

五、http请求都带上MAC

全部客户端发出的请求,不管是查询仍是写操做,都带上MAC(Message Authentication

Code)。MAC不但能保证请求没有被篡改(Integrity),还能保证请求确实来自你的合法客户端(Signing)。固然前提是你客户端的key没有被泄漏,如何保证客户端key的安全是另外一个话题。MAC值的计算能够简单的处理为hash(request

params+key)。带上MAC以后,服务器就能够过滤掉绝大部分的非法请求。MAC虽然带有签名的功能,和RSA证书的电子签名方式却不同,缘由是MAC签名和签名验证使用的是同一个key,而RSA是使用私钥签名,公钥验证,MAC的签名并不具有法律效应。

六、http请求使用临时密钥

高延迟的网络环境下,不经优化https的体验确实会明显不如http。在不具有https条件或对网络性能要求较高且缺少https优化经验的场景下,http的流量也应该使用AES进行加密。AES的密钥能够由客户端来临时生成,不过这个临时的AES

key须要使用服务器的公钥进行加密,确保只有本身的服务器才能解开这个请求的信息,固然服务器的response也须要使用一样的AES

key进行加密。因为http的应用场景都是由客户端发起,服务器响应,因此这种由客户端单方生成密钥的方式能够必定程度上便捷的保证通讯安全。

七、AES使用CBC模式

不要使用ECB模式,记得设置初始化向量,每一个block加密以前要和上个block的秘文进行运算。
复制代码

5九、假如Controller太臃肿,如何优化?

1.将网络请求抽象到单独的类中

方便在基类中处理公共逻辑;

方便在基类中处理缓存逻辑,以及其它一些公共逻辑;

方便作对象的持久化。

2.将界面的封装抽象到专门的类中

构造专门的 UIView 的子类,来负责这些控件的拼装。这是最完全和优雅的方式,不过稍微麻烦一些的是,你须要把这些控件的事件回调先接管,再都一一暴露回 Controller。

3.构造 ViewModel

借鉴MVVM。具体作法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。

4.专门构造存储类

专门来处理本地数据的存取。

5.整合常量

复制代码

60、M、V、C相互通信规则你知道的有哪些?

MVC 是一种设计思想,一种框架模式,是一种把应用中全部类组织起来的策略,它把你的程序分为三块,分别是:

M(Model):实际上考虑的是“什么”问题,你的程序本质上是什么,独立于 UI 工做。是程序中用于处理应用程序逻辑的部分,一般负责存取数据。

C(Controller):控制你 Model 如何呈如今屏幕上,当它须要数据的时候就告诉 Model,你帮我获取某某数据;当它须要 UI 展现和更新的时候就告诉 View,你帮我生成一个 UI 显示某某数据,是 Model 和 View 沟通的桥梁。

V(View):Controller 的手下,是 Controller 要使用的类,用于构建视图,一般是根据 Model 来建立视图的。

要了解 MVC 如何工做,首先须要了解这三个模块间如何通讯。

MVC通讯规则

http://cc.cocimg.com/api/uploads//20171127/1511752329535960.jpg

Controller to Model

能够直接单向通讯。Controller 须要将 Model 呈现给用户,所以须要知道模型的一切,还须要有同 Model 彻底通讯的能力,而且能任意使用 Model 的公共 API。

Controller to View

能够直接单向通讯。Controller 经过 View 来布局用户界面。

Model to View

永远不要直接通讯。Model 是独立于 UI 的,并不须要和 View 直接通讯,View 经过 Controller 获取 Model 数据

View to Controller

View 不能对 Controller 知道的太多,所以要经过间接的方式通讯。

Target

action。首先 Controller 会给本身留一个 target,再把配套的 action 交给 View 做为联系方式。那么 View

接收到某些变化时,View 就会发送 action 给 target 从而达到通知的目的。这里 View 只须要发送

action,并不须要知道 Controller 如何去执行方法。

代理。有时候 View 没有足够的逻辑去判断用户操做是否符合规范,他会把判断这些问题的权力委托给其余对象,他只需得到答案就好了,并不会管是谁给的答案。

DataSoure。View 没有拥有他们所显示数据的权力,View 只能向 Controller 请求数据进行显示,Controller 则获取 Model 的数据整理排版后提供给 View。

Model 访问 Controller

一样的 Model 是独立于 UI 存在的,所以没法直接与 Controller 通讯,可是当 Model 自己信息发生了改变的时候,会经过下面的方式进行间接通讯。

Notification & KVO一种相似电台的方法,Model 信息改变时会广播消息给感兴趣的人 ,只要 Controller 接收到了这个广播的时候就会主动联系 Model,获取新的数据并提供给 View。

从上面的简单介绍中咱们来简单归纳一下 MVC 模式的优势。

1.低耦合性

2.有利于开发分工

3.有利于组件重用

4.可维护性
复制代码

60、什么是MVVM,请设计View model须要考虑哪些?

M + V + VM , VM的做用主要用于简化Controller的负担,可是VM的设计中不能够没有C,其实应该是 M + V + C +VM , C 做为 关联 V 和 VM 的纽带, 最好不要直接关联VM。

复制代码

参考

持久化方式学习整理

App生命周期知识学习整理

线程知识整理

事件响应链

运行时、消息转发相关

block知识整理

UIWindow、UIApplication

专题(持续更新)

iOS开发基础

iOS开发进阶

Swift学习


给你们推荐一个优秀的iOS交流平台,平台里的伙伴们都是很是优秀的iOS开发人员,咱们专一于技术的分享与技巧的交流,你们能够在平台上讨论技术,交流学习。欢迎你们的加入(想要进入的可加小编微信)。 17512010526

做者:就叫yang 连接:https://www.jianshu.com/p/8ede4692978d 来源:简书
相关文章
相关标签/搜索