目录
1.解释self = [super init]方法
2.网络七层协议
3.OC中是否有二维数组,如何实现二维数组
4.@synthesize、@dynamic的理解
5.XIB与Storyboards及纯代码的优缺点
6.内存的使用和优化的注意事项
7.pushViewController、presentViewController原理及区别.
8.发现VC不走dealloc,如何检查原因。
9. 什么情况使用 weak 关键字,相比 assign 有什么不同?
10. objc中的类方法和实例方法有什么本质区别和联系?
11. _objc_msgForward函数是做什么的,直接调用它将会发生什么?
12.__block和__weak修饰符的区别:
13.iOS 中的事件的传递:响应链
14.应用程序的生命周期
15.动画原理与实现
16.类结构体的组成,isa指针指向了什么?
17.RunLoop有几种事件源?有几种模式?
18.lldb(gdb)常用的调试命令?
19.performSelector延时调用导致的内存泄露?
20.如何对真机的crash日志进行分析?
21.对象回收时Weak指针自动被置为nil的实现原理
22.远程推送
23.热修复
24.组件化开发
25.Instruments性能检测
26.加密
27.优化CPU消耗
28.单例
29.Objective-C堆和栈的区别?
30.关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用
31.关键字volatile有什么含意?并给出三个不同的例子。
32. 一个参数既可以是const还可以是volatile吗? 一个指针可以是volatile 吗?解释为什么。
33.static 关键字的作用:
34.线程与进程的区别和联系?
35.列举几种进程的同步机制,并比较其优缺点。
36. 进程之间通信的途径
37. 进程死锁的原因
38. 死锁的4个必要条件
39. 死锁的处理
40.cocoa touch框架
41.自动释放池是什么,如何工作
42. Objective-C的优缺点。
43.sprintf,strcpy,memcpy使用上有什么要注意的地方。
44.http和scoket通信的区别。
45.TCP和UDP的区别
46.请简要说明viewDidLoad和viewDidUnload何时调用
47. 简述内存分区情况
48.队列和栈有什么区别:
49.HTTP协议中,POST和GET的区别是什么?
50.iOS的系统架构
1.解释self = [super init]方法
容错处理,当父类初始化失败,会返回一个nil,表示初始化失败。由于继承的关系,子类是需要拥有父类的实例和行为,因此,我们必须先初始化父类,然后再初始化子类
2.网络七层协议
应用层:
1.用户接口、应用程序;
2.Application典型设备:网关;
3.典型协议、标准和应用:TELNET、FTP、HTTP
表示层:
1.数据表示、压缩和加密presentation
2.典型设备:网关
3.典型协议、标准和应用:ASCLL、PICT、TIFF、JPEG|MPEG
4.表示层相当于一个东西的表示,表示的一些协议,比如图片、声音和视频MPEG。
会话层:
1.会话的建立和结束;
2.典型设备:网关;
3.典型协议、标准和应用:RPC、SQL、NFS、X WINDOWS、ASP
传输层:
1.主要功能:端到端控制Transport;
2.典型设备:网关;
3.典型协议、标准和应用:TCP、UDP、SPX
网络层:
1.主要功能:路由、寻址Network;
2.典型设备:路由器;
3.典型协议、标准和应用:IP、IPX、APPLETALK、ICMP;
数据链路层:
1.主要功能:保证无差错的疏忽链路的data link;
2.典型设备:交换机、网桥、网卡;
3.典型协议、标准和应用:802.2、802.3ATM、HDLC、FRAME RELAY;
物理层:
1.主要功能:传输比特流Physical;
2.典型设备:集线器、中继器
3.典型协议、标准和应用:V.35、EIA/TIA-232.
3.OC中是否有二维数组,如何实现二维数组
OC中没有二维数组,可通过嵌套数组实现二维数组。
4.@synthesize、@dynamic的理解
@synthesize是系统自动生成getter和setter属性声明;@synthesize的意思是,除非开发人员已经做了,否则由编译器生成相应的代码,以满足属性声明;
@dynamic是开发者自已提供相应的属性声明,@dynamic意思是由开发人员提供相应的代码:对于只读属性需要提供setter,对于读写属性需要提供 setter 和getter。查阅了一些资料确定@dynamic的意思是告诉编译器,属性的获取与赋值方法由用户自己实现, 不自动生成。
5.XIB与Storyboards及纯代码的优缺点
优点:
XIB:在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观一些,而且类文件中就少了创建控件的代码,确实简化不少,通常每个XIB对应一个类。
Storyboard:在编译前提供了可视化界面,可拖控件,可加约束,在开发时比较直观,而且一个storyboard可以有很多的界面,每个界面对应一个类文件,通过storybard,可以直观地看出整个App的结构。
缺点:
XIB:需求变动时,需要修改XIB很大,有时候甚至需要重新添加约束,导致开发周期变长。XIB载入相比纯代码自然要慢一些。对于比较复杂逻辑控制不同状态下显示不同内容时,使用XIB是比较困难的。当多人团队或者多团队开发时,如果XIB文件被发动,极易导致冲突,而且解决冲突相对要困难很多。
Storyboard:需求变动时,需要修改storyboard上对应的界面的约束,与XIB一样可能要重新添加约束,或者添加约束会造成大量的冲突,尤其是多团队开发。对于复杂逻辑控制不同显示内容时,比较困难。当多人团队或者多团队开发时,大家会同时修改一个storyboard,导致大量冲突,解决起来相当困难。
手写代码:
优势:适合大型项目大规模使用,利于版本管理、追踪改动以及代码合并;最好的代码重用性
劣势:慢,开发周期长,维护代码复杂;自动布局AutoLayout困难
xib:
优势:开发速度快;在版本管理上和纯代码的差异并不是很大,易读易维护
劣势:xib中的设置往往并非最终设置,UI设计会被代码所覆盖;
storyboard:
优势:可以看到每个ViewController的布局样式,也可以明确地知道各个ViewController之间的转换关系;代码量少,开发周期短;关键是已经成为新建项目时候的默认配置,代表着苹果以后的方向和重心
劣势:很难多人协作;ViewController的重用和自定义的view的处理;
6.内存的使用和优化的注意事项
- 重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用;
- 尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能;
- 不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的* 延迟加载,性能及内存就差了很多;
- 选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。
- gzip/zip压缩:当从服务端下载相关附件时,可以通过gzip/zip压缩后再下载,使得内存更小,下载速度也更快。
- 延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
- 数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
- 处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉
- 重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
- 避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要;
- 使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存;
- 正确选择图片加载方式
7.pushViewController、presentViewController原理及区别.
区别:present只能逐级返回,push所有视图由视图栈控制,可以返回上一级,也可以返回到根vc,其他vc。present一般用于不同业务界面的切换,push一般用于同一业务不同界面之间的切换。
8.发现VC不走dealloc,如何检查原因。
在一个项目中,如果ViewController使用完成之后,发现这个东东并没有释放掉,dealloc方法不走,看着那个内存蹭蹭的网上增,相信大家都知道如何去释放一个不用的ViewController,但是还是有些其他因素限制了内存释放。
如果你的VC中有NSTimer,那么就要注意了,因为当你[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];时,这个 target:self 就增加了VC的RetarnCountr如果你不将这个timer invalidate,就别想调用dealloc。
再然后,一个比较隐蔽的因素,你反过头去找找看,跟这个类有关的代理,嗯,对是代理,有没有强引用的属性啊?对,比如一个代理的delegate应
该是 assign 的现在是retain,就是这个,它会影响你不让你调用dealloc,不信,就试试吧。
最后,如果以上都没有问题的话,那么,真问题就来了。我就遇到了这种情况,在使用ASI进行网络请求的时候,因为需求原因,我使用属性将名为ASIFormDataRequest 的NSOperation 标记住了,就将上面的问题找了又找,就是不行,最后是将那个标记的属性置为 nil,才解决了这个不调用 dealloc的这个蛋疼问题。所以,如果你遇到了比较隐蔽的原因,那就去找找你自己控制不了的因素,就像这个第三方。如果你不了解它的运行机制,那就一定要注意这个库
PS:dealloc中的释放也是有顺序的,就好比创建时,先父类,再子类,释放的时候反过来,不然有几率会crash,至于原因。 子类是父类的继承,比较NB,以至于要杀死他们的时候应该先干掉比较牛B的子类。
9. 什么情况使用 weak 关键字,相比 assign 有什么不同?
什么情况使用 weak 关键字?
1)在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性
2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong。在下文也有论述:《IBOutlet连出来的视图属性为什么可以被设置成weak?》
不同点:
1)weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
2)assigin 可以用非OC对象,而weak必须用于OC对象
10. objc中的类方法和实例方法有什么本质区别和联系?
类方法:
类方法是属于类对象的
类方法只能通过类对象调用
类方法中的self是类对象
类方法可以调用其他的类方法
类方法中不能访问成员变量
类方法中不能直接调用对象方法
实例方法:
实例方法是属于实例对象的
实例方法只能通过实例对象调用
实例方法中的self是实例对象
实例方法中可以访问成员变量
实例方法中直接调用实例方法
实例方法中也可以调用类方法(通过类名)
11. _objc_msgForward函数是做什么的,直接调用它将会发生什么?
_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
12.__block和__weak修饰符的区别:
__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
__block对象可以在block中被重新赋值,__weak不可以。
13.iOS 中的事件的传递:响应链
简要说一下:
事件沿着一个指定的路径传递直到它遇见可以处理它的对象。 首先一个UIApplication 对象从队列顶部获取一个事件并分发(dispatches)它以便处理。 通常,它把事件传递给应用程序的关键窗口对象,该对象把事件传递给一个初始对象来处理。 初始对象取决于事件的类型。
触摸事件。 对于触摸事件,窗口对象首先尝试把事件传递给触摸发生的视图。那个视图被称为hit-test(点击测试)视图。 寻找hit-test视图的过程被称为hit-testing, 参见 “Hit-Testing Returns the View Where a Touch Occurred.”
运动和远程控制事件。 对于这些事件,窗口对象把shaking-motion(摇晃运动)或远程控制事件传递给第一响应者来处理。第一响应者请参见 “The Responder Chain Is Made Up of Responder Objects.”
iOS 使用hit-testing来找到事件发生的视图。 Hit-testing包括检查触摸事件是否发生在任何相关视图对象的范围内, 如果是,则递归地检查所有视图的子视图。在视图层次中的最底层视图,如果它包含了触摸点,那么它就是hit-test视图。等 iOS决定了hit-test视图之后,它把触摸事件传递给该视图以便处理。
14.应用程序的生命周期
一个应用程序有五种状态:
- Not running 未运行 ,程序没启动。
- Inactive 未激活 ,程序在前台运行,没有接收到事件。在程序没有事件需要处理时停留在这个状态。
- Active 激活,程序在前台运行而且接收到了事件。这也是前台的一个正常的模式
- Backgroud 后台,程序在后台而且能执行代码,大多数程序只能短暂停留这个状态,马上进入Suspended状态。
- Suspended 挂起,程序在后台不能执行代码。但程序不会被马上杀死,当系统内存不足时,在这个状态的程序占用的内存优先被回收。
切换状态时的回调:
在发生状态切换时,都会调用delegate对象对应的方法来响应App状态的改变。
- application:willFinishLaunchingWithOptions: 这个方法是你在启动时的第一次机会来执行代码
- application:didFinishLaunchingWithOptions: 这个方法允许你在显示app给用户之前执行最后的初始化操作
- applicationDidBecomeActive: app已经切换到active状态后需要执行的操作
- applicationWillResignActive: app将要从前台切换到后台时需要执行的操作
- applicationDidEnterBackground: app已经进入后台后需要执行的操作
- applicationWillEnterForeground: app将要从后台切换到前台需要执行的操作,但app还不是active状态
- applicationWillTerminate: app将要结束时需要执行的操作
接下来是App启动、切换和锁屏状态时调用delegate对象的方法
- App启动
App启动时,首先由not running状态切换到inactive状态,此时调用application:didFinishLaunchingWithOptions:方法;然后调用application:didFinishLaunchingWithOptions:方法,最后由inactive状态切换到active状态,此时调用applicationDidBecomeActive:方法。 - App无事件响应
由active状态切换到inactive状态,此时调用applicationWillResignActive:方法。 - 切换App
当切换到另一个App时,由状态active切换到inactive,此时调用applicationWillResignActive:方法;然后从inactive状态切换到running(background)状态,此时调用applicationDidEnterBackground:方法。 - 锁屏
当手机锁屏时,由状态active切换到inactive,此时调用applicationWillResignActive:;然后再由inactive状态切换到running(background)状态,此时调用applicationDidEnterBackground:方法。 - App响应中断
当一个基于警告式的中断发生时,比如有电话打进来了,这是程序会临时进入inactive状态,这用户可以选择如何处理这个中断。接着会调用applicationWillResignActive:方法,当中断来临时,你需要在这个方法中,停止timer或者周期性任务、停止视频,音乐播放、停止游戏运行。当程序回到active状态 , applicationDidBecomeActive: 会调用方法,恢复停止的操作。 - App转到后台运行
首先调用applicationWillResignActive:方法,程序即将进入后台运行,接着调用applicationDidEnterBackground: 方法,此时程序为background状态,系统允许程序继续运行一段时间,然后程序进入Suspended状态。 - App转到前台运行
系统唤醒程序,调用applicationWillEnterForeground: 方法,程序从background状态改为active状态,接着调用applicationDidBecomeActive:方法。当app处于挂起状态时,它是不能执行任何代码的。因此它不能处理在挂起期间发过来的通知,比如方向改变,时间改变,设置的改变还有其他影响程序展现的或状态的通知。在程序返回后台或前台时,程序要正确的处理这些通知。 - App终止
当App被系统终止(如内存不足、Crash)或者用户自行终止。系统会在应用程序终止之前调用applicationWillTerminate: 方法,来保存用户的一些重要数据以便下次启动时恢复到app原来的状态。
15.动画原理与实现
动画的实现
我们也可将 iOS 的动画分为两大类:
系统提供的 关键帧动画 实现方式;用户指定 关键 信息,系统实现动画过程,对用户而言操作起来会简单些。
逐帧动画 实现方式;用户自己 画 出每一帧画面,系统操作方法简单,但用户操作的工作量就会大一些。
逐帧动画实现方式
简单的说,要实现逐帧的方式,就是需要 周期性 的调用 绘制 方法,绘制每帧的动画对象。
这里说的 绘制,不光是指覆写 UIView 的 - drawRect:的方法来手动重绘视图,也包括修改 UIView 它的属性,比如位置、颜色等。
iOS 的动画都是基于 CALayer 的,iOS 的 UIView 背后都有一个对应的 CALayer 。对 UIView 的修改实际上都是对背后 CALayer 的修改。
但如果在逐帧绘制的方法中修改了一个自建的 CALayer,这个 CALayer 不是对应某个 UIView 的,需注意系统的 隐式动画 的影响,后面会提到这点。
而 周期性,就需要一个定时器来完成了,即 CADisplayLink。
CADisplayLink 与 NSTimer 比较类似,可以周期性的调用指定的方法。
之所以用 CADisplayLink,是因为它是基于屏幕刷新率的,即屏幕每次刷新时就会触发调用。
iPhone 的屏幕刷新率是 60 FPS。
关键帧动画实现方式
采用关键帧的方式来实现动画,要讲的内容相对逐帧的方式就多的多了。
还是用 UIView 移动的简单例子。
这里面有两个关键帧,起始帧和结束帧,除此之外还有2个关键信息:
起始帧,变化信息:坐标为 (0,0)
结束帧,变化信息:坐标为 (100,0)
动画时间,0.25秒
匀速运动
坐标 信息是 UIView 的一个属性(实际是对应到 CALayer 的属性),在动画实现里,我们只需要指定起始和结束的两个关键值就够了,中间的过渡值都有系统自动生成。
这里出现了两种值,一个是我们设定的,一个是系统生成的,所以要先在这里插入一个 模型层 和 展现层 的概念了
CALayer 的同一个属性值,会分别保存在模型层 modelLayer ,和展现层 presentationLayer 中。当我们修改属性值时,是修改的模型层的数值,动画时系统根据模型层的变化,生成的过渡值,是保存在展现层中的。
在 CALayer 的对象里能直接访问到这两层的信息。
而 CALayer 的底层实现实际不止这两层,但我们现在讨论动画的时候,可以只关心这两层。
动画实现
将动画加入 CALayer 的代码定义为:
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
接受的类型是 CAAnimation 类型,有下面这些子类:
CABasicAnimation,可设定起始结束两个关键帧的信息。
CAKeyframeAnimation,除首尾外,还可添加多个中间关键点。
CAAnimationGroup ,可组合多个动画,因为上面两种动画一次只能设置一个属性值。
CATransition,图层过渡动画,默认是淡入。比如修改一个 CALayer的背景色时,是从初始色慢慢淡入过渡到结束色。
可修改为新颜色把旧颜色顶出去等效果。还可使用 CIFilter 滤镜做过渡效果,一些开源 UIViewController 的过渡动画使用了这种方式。
动画中,除了属性值外,我们还设置了两个和时间有关的信息:动画时间0.25秒,运动方式是匀速运动。
动画持续时间很简单,是通过 CAAnimation 遵守的 CAMediaTiming 协议设定的。
匀速运动是通过设置 CAAnimation 的 timingFunction 实现的,这是一个 CAMediaTimingFunction 类的对象。
隐式动画
上面的过程,我们是 显式 的向一个 CALayer 添加了一个动画,所以这种方式叫做 显式动画。
对应的,还有 隐式动画,即系统自动添加上的动画。
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor greenColor].CGColor;
layer.frame = CGRectMake(0, 0, 100, 100);
[self.view.layer addSublayer:layer];
layer.frame = CGRectOffset(layer.frame, 100, 0);
这段代码里,我们没有添加 CAAnimation 动画,但 layer 不是直接变化到新的位置,而是有一个动画效果。
这就是 隐式动画 的效果。
动画事务
创建动画事务的目的是为了操作的原子性,保证动画的所有修改能同时生效。
CATransaction 就是动画事务的操作类。
在创建隐式动画的时候,系统也会隐式的创建一个动画事务,以保证所有的动画能同时进行。
除此之外,还可以显式的创建一个事务。
显式事务中可以定义事务中所有动画的运行时间和时间函数,此外,还有这个方法 + (void)setDisableActions:(BOOL)flag 能显式的关闭这个事务中的 action 查询操作。
关闭了查询也就是关闭了动画效果,属性值的变化就会立即生效,而没有动画效果了:
[CATransaction begin];
[CATransaction setDisableActions:YES];
///...
layer.frame = CGRectOffset(layer.frame, 100, 0);
///...
[CATransaction commit];
16.类结构体的组成,isa指针指向了什么?
在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。
Class 是一个 objc_class 结构类型的指针, id是一个 objc_object 结构类型的指针.
objc_class 的定义:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
各个参数的意思:
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
super_class:父类,如果该类已经是最顶层的根类,那么它为NULL。
version:类的版本信息,默认为0
info:供运行期使用的一些位标识。
instance_size:该类的实例变量大小
ivars:成员变量的数组
每一个对象本质上都是一个类的实例。其中类定义了成员变量和成员方法的列表。对象通过对象的isa指针指向类。
每一个类本质上都是一个对象,类其实是元类(meteClass)的实例。元类定义了类方法的列表。类通过类的isa指针指向元类。
所有的元类最终继承一个根元类,根元类isa指针指向本身,形成一个封闭的内循环。
17.RunLoop有几种事件源?有几种模式?
输入事件来源
Run loop接收输入事件来自两种不同的来源:输入源(input source)和定时源(timer source)。两种源都使用程序的某一特定的处理例程来处理到达的事件。
当你创建输入源,你需要将其分配给run loop中的一个或多个模式(什么是模式,下文将会讲到)。模式只会在特定事件影响监听的源。大多数情况下,run loop运行在默认模式下,但是你也可以使其运行在自定义模式。若某一源在当前模式下不被监听,那么任何其生成的消息只在run loop运行在其关联的模式下才会被传递。
输入源(input source)
传递异步事件,通常消息来自于其他线程或程序。输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)
基于端口的输入源
基于端口的输入源由内核自动发送。
Cocoa和Core Foundation内置支持使用端口相关的对象和函数来创建的基于端口的源。例如,在Cocoa里面你从来不需要直接创建输入源。你只要简单的创建端口对象,并使用NSPort的方法把该端口添加到run loop。端口对象会自己处理创建和配置输入源。
在Core Foundation,你必须人工创建端口和它的run loop源。我们可以使用端口相关的函数(CFMachPortRef,CFMessagePortRef,CFSocketRef)来创建合适的对象。下面的例子展示了如何创建一个基于端口的输入源,将其添加到run loop并启动:
voidcreatePortSource()
{
CFMessagePortRef port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("com.someport"),myCallbackFunc, NULL, NULL);
CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
while (pageStillLoading) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFRunLoopRun();
[pool release];
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
自定义输入源
自定义的输入源需要人工从其他线程发送。
为了创建自定义输入源,必须使用Core Foundation里面的CFRunLoopSourceRef类型相关的函数来创建。你可以使用回调函数来配置自定义输入源。Core Fundation会在配置源的不同地方调用回调函数,处理输入事件,在源从run loop移除的时候清理它。
除了定义在事件到达时自定义输入源的行为,你也必须定义消息传递机制。源的这部分运行在单独的线程里面,并负责在数据等待处理的时候传递数据给源并通知它处理数据。消息传递机制的定义取决于你,但最好不要过于复杂。创建并启动自定义输入源的示例如下:
voidcreateCustomSource()
{
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
while (pageStillLoading) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFRunLoopRun();
[pool release];
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
Cocoa上的Selector源
除了基于端口的源,Cocoa定义了自定义输入源,允许你在任何线程执行selector方法。和基于端口的源一样,执行selector请求会在目标线程上序列化,减缓许多在线程上允许多个方法容易引起的同步问题。不像基于端口的源,一个selector执行完后会自动从run loop里面移除。
当在其他线程上面执行selector时,目标线程须有一个活动的run loop。对于你创建的线程,这意味着线程在你显式的启动run loop之前是不会执行selector方法的,而是一直处于休眠状态。
NSObject类提供了类似如下的selector方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array;
定时源(timer source)
定时源在预设的时间点同步方式传递消息,这些消息都会发生在特定时间或者重复的时间间隔。定时源则直接传递消息给处理例程,不会立即退出run loop。
需要注意的是,尽管定时器可以产生基于时间的通知,但它并不是实时机制。和输入源一样,定时器也和你的run loop的特定模式相关。如果定时器所在的模式当前未被run loop监视,那么定时器将不会开始直到run loop运行在相应的模式下。类似的,如果定时器在run loop处理某一事件期间开始,定时器会一直等待直到下次run loop开始相应的处理程序。如果run loop不再运行,那定时器也将永远不启动。
创建定时器源有两种方法:
方法一:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self selector:@selector(backgroundThreadFire:) userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timerforMode:NSDefaultRunLoopMode];
方法二:
[NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:@selector(backgroundThreadFire:)
userInfo:nil
repeats:YES];
Cocoa中的预定义模式有:
- Default模式
定义:NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)
描述:默认模式中几乎包含了所有输入源(NSConnection除外),一般情况下应使用此模式。 - Connection模式
定义:NSConnectionReplyMode(Cocoa)
描述:处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。 - Modal模式
定义:NSModalPanelRunLoopMode(Cocoa)
描述:处理modal panels事件。 - Event tracking模式
定义:UITrackingRunLoopMode(iOS) NSEventTrackingRunLoopMode(cocoa)
描述:在拖动loop或其他user interface tracking loops时处于此种模式下,在此模式下会限制输入事件的处理。例如,当手指按住UITableView拖动时就会处于此模式。 - Common模式
定义:NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)
描述:这是一个伪模式,其为一组run loop mode的集合,将输入源加入此模式意味着在Common Modes中包含的所有模式下都可以处理。在Cocoa应用程序中,默认情况下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法想Common Modes中添加自定义modes。
Run loop的优点:
首先,NSRunLoop是一种更加高明的消息处理模式,他就高明在对消息处理过程进行了更好的抽象和封装,这样才能是的你不用处理一些很琐碎很低层次的具体消息的处理,在NSRunLoop中每一个消息就被打包在input source或者是timer source(见后文)中了。
其次,也是很重要的一点,使用run loop可以使你的线程在有工作的时候工作,没有工作的时候休眠,这可以大大节省系统资源。
18.lldb(gdb)常用的调试命令?
po:打印对象,会调用对象description方法。是print-object的简写
expr:可以在调试时动态执行指定表达式,并将结果打印出来,很有用的命令
print:也是打印命令,需要指定类型
bt:打印调用堆栈,是thread backtrace的简写,加all可打印所有thread的堆栈
br l:是breakpoint list的简写
19.performSelector延时调用导致的内存泄露
最后的最后才发现实际上是performSelector延时调用的问题,经查找资料,performSelector关于内存管理的执行原理是这样的执行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的时候,系统会将tableLayer的引用计数加1,执行完这个方法时,还会将tableLayer的引用计数减1,而在我的游戏里这个延时执行函数是被多次调用的,有时切换场景时延时函数已经被调用但还没有执行,这时tableLayer的引用计数没有减少到0,也就导致了切换场景dealloc方法没有被调用,出现了内存泄露。
所以最后我的解决办法就是取消那些还没有来得及执行的延时函数,代码很简单:
[NSObject cancelPreviousPerformRequestsWithTarget:self]
当然你也可以一个一个得这样用:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]
加上了这个以后,切换场景也就很顺利地执行了dealloc方法,至此问题解决!
最后在找资料时也发现了,延时调用实现长按钮的实现思路,记录下来以备后用:
在touchBegan里面
[self performSelector:@selector(longPressMethod:) withObject:nil afterDelay:longPressTime]
然后在end 或cancel里做判断,如果时间不够长按的时间调用:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(longPressMethod:) object:nil]
取消began里的方法
最后最后总结:
performSelector是一个很有用的函数,跟它打过不少交道,经过血与泪的教训,总结一下它的使用如下:
使用前先检测一下,
SEL testSelector = @selector(test:);
if([tester respondsToSelector:testSelector])
{
//如果响应就执行
[tester test:@"invoke test method"];
}
使用后,如果有必要,需要显示的调用cancelPreviousPerformRequestsWithTarget:selector:object: ,否则有可能产生内存泄露,而且这种内存泄露很难发现,因为它并不违反任何规则,所以一定要注意!
20.如何对真机的crash日志进行分析?
获取真机crash日志
连接真机
找到Xcode --> Window --> Devices
获取所有的crash日志文件
右键可以Export,就可以查看相关的crash的原因
分析crash日志
有如下3种方法
方法1 使用XCode
这种方法可能是最容易的方法了。
需要使用Xcode符号化 crash log,你需要下面所列的3个文件:
- crash报告(.crash文件)
- 符号文件 (.dsymb文件)
- 应用程序文件 (appName.app文件,把IPA文件后缀改为zip,然后解压,Payload目录下的appName.app文件), 这里的appName是你的应用程序的名称。
把这3个文件放到同一个目录下,打开Xcode的Window菜单下的organizer,然后点击Devices tab,然后选中左边的Device Logs。
然后把.crash文件拖到Device Logs或者选择下面的import导入.crash文件。
这样你就可以看到crash的详细log了。
方法2 使用命令行工具symbolicatecrash
有时候Xcode不能够很好的符号化crash文件。我们这里介绍如何通过symbolicatecrash来手动符号化crash log。
在处理之前,请依然将“.app“, “.dSYM”和 ".crash"文件放到同一个目录下。现在打开终端(Terminal)然后输入如下的命令:
exportDEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
然后输入命令:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrashappName.crashappName.app>appName.log
现在,符号化的crash log就保存在appName.log中了。
方法3 使用命令行工具atos
如果你有多个“.ipa”文件,多个".dSYMB"文件,你并不太确定到底“dSYMB”文件对应哪个".ipa"文件,那么,这个方法就非常适合你。
特别当你的应用发布到多个渠道的时候,你需要对不同渠道的crash文件,写一个自动化的分析脚本的时候,这个方法就极其有用。
这里先介绍一个概念:UUID
什么是UUID
每一个可执行程序都有一个build UUID来唯一标识。Crash日志包含发生crash的这个应用(app)的 build UUID以及crash发生的时候,应用加载的所有库文件的[build UUID]。
那如何知道crash文件的UUID呢?
可以用:
grep"appNamearmv"crash
或者
grep--after-context=2"BinaryImages:"crash
可以得到类似如下的结果:
appName.crash-0x4000-0x9e7fffappNamearmv7<8bdeaf1a0b233ac199728c2a0ebb4165>/var/mobile/Applications/A0F8AB29-35D1-4E6E-84E2-954DE7D21CA1/appName.crash.app/appName
(请注意这里的0x4000,是模块的加载地址,后面用atos的时候会用到)
如何找到app的UUID
可以使用命令:
xcrundwarfdump-–uuid
比如:
xcrundwarfdump--uuidappName.app/appName
结果如下:
UUID:8BDEAF1A-0B23-3AC1-9972-8C2A0EBB4165(armv7)appName.app/appNameUUID:5EA16BAC-BB52-3519-B218-342455A52E11(armv7s)appName.app/appName
这个app有2个UUID,表明它是一个fat binnary。
它能利用最新硬件的特性,又能兼容老版本的设备。
对比上面crash文件和app文件的UUID,发现它们是匹配的:
8BDEAF1A-0B23-3AC1-9972-8C2A0EBB4165
用atos命令来符号化某个特定模块加载地址
命令是:
atos[-oAppName.app/AppName][-lloadAddress][-archarchitecture]
亲测,下面3种都可以:
xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-l0x4000-archarmv7xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-archarmv7
xcrunatos-oappName.app/appName-archarmv7
(注:这3行选任意一行执行都可以达到目的,其中0x4000是模块的加载地址,从上面的章节可以找到如何得到这个地址。)
文章开头提到crash文件中有如下两行,
3appName0x000f462a0x4000+9846184appName0x00352aee0x4000+3468014
在执行了上面的:
xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-l0x4000-archarmv7
之后,输入如下地址:
0x00352aee
(crash文件中的第4行:4 appName 0x00352aee 0x4000 + 3468014)
可以得到结果:
-UIScrollView(UITouch)touchesEnded:withEvent:(UIScrollView+UITouch.h:26)
这样就找到了应用种到底是哪个模块导致的crash问题。
21.对象回收时Weak指针自动被置为nil的实现原理
Runtime维护着一个Weak表,用于存储指向某个对象的所有Weak指针;
Weak表是Hash表,Key是所指对象的地址,Value是Weak指针地址的数组;
在对象被回收的时候,经过层层调用,会最终触发下面的方法将所有Weak指针的值设为nil。
runtime源码,objc-weak.m 的 arr_clear_deallocating 函数。
Weak指针如何注册到Weak表中、如何维护hash表可以参考objc-weak.m中的其它源码。
从实现中可以看出,Weak指针的使用涉及到Hash表的增删改查,有一定的性能开销。
Weak指针的实际应用:
iOS 8 特有iOS相关的漏洞
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
iOS 8 的UIScrollView的delegate属性
简单来说,方法首先根据对象地址获取所以Weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从Weak表中删除。
22.远程推送
当app不在前台时,app可能将无法接收到服务器的消息,这时苹果通过自己的服务器来和iOS设备来通信,将最新消息传递到iOS设备,然后iOS系统将指定消息投递给对应的app。
iOS推送通知的基本原理:
苹果的推送服务通知是由自己专门的推送服务器APNs (Apple Push Notification service)来完成的,其过程是 APNs 接收到我们自己的应用服务器发出的被推送的消息,将这条消息推送到指定的 iOS 的设备上,然后再由 iOS设备通知到我们的应用程序,我们将会以通知或者声音的形式收到推送回来的消息。 iOS 远程推送的前提是,装有我们应用程序的 iOS 设备,需要向 APNs 服务器注册,注册成功后,APNs 服务器将会给我们返回一个 devicetoken,我们获取到这个 token 后会将这个 token 发送给我们自己的应用服务器。当我们需要推送消息时,我们的应用服务器将消息按照指定的格式进行打包,然后结合 iOS 设备的 devicetoken 一起发给 APNs 服务器。我们的应用会和 APNs 服务器维持一个基于 TCP 的长连接,APNs 服务器将新消息推送到iOS 设备上,然后在设备屏幕上显示出推送的消息。
流程中包含四次交互--四次握手:
iOS设备在刚激活时将首先向APNs服务器打招呼--请求tls连接,也就是说建立一个基于tls协议的长连接(这种连接可以在保证双发的身份可信的情况下,对连接后续的通信过程进行加密,保证通信信息的安全保密性。)
APNs服务器收到请求后,将反馈给iOS设备一个APNs服务器的证书;
iOS设备收到证书后验证通过,将反馈给APNs服务器自己的证书和私钥,二者都是设备激活过程中获得并存在设备的钥匙串中的(并非apple开发者网站上建立的证书)。
APNs服务器接收到后,通过发来的证书和私钥验证连接可靠,握手成功。
针对第三个步骤:app发送device token给自己的服务器
这一步是需要开发者自己来保证安全的一步,一般我们可以模拟苹果的机制,对device token进行对称加密,或者与服务器协商好撒盐和md5的方式,保证通信过程中即使被截获也无法被破解。当然为了提高安全级别,可以加入认证授权的操作,甚至模仿苹果的tls方式来进一步保证安全性。
针对第四个步骤:provider发送消息给APNs服务器
这一步安全性通常有两种方式来保证:
第一种是经典的做法,也是与设备和APNs服务器相同的做法,即通过建立tls长连接来校验身份,四次握手后开始发送消息。与第一步差别在于第三次握手时传给APNs服务器的apple开发者网站注册的推送证书和私钥。这样APNs服务器可以通过证书判断当前通过信的对方是可信的服务器,连接建立成功,provider便可以给PNs服务器发送推送消息,要求其转发。
针对第五个步骤:APNs服务器推送消息到设备
这一步的安全性其实是基于上面注册推送服务时已经建立好的基于tls协议的长连接,这时APNs服务器根据provider的请求中device token使用秘钥解码得到设备的id和app的bundle id, 这样通过设备的id 判断这个设备在线并且可以信任,直接将推送消息发给设备上的对应的app,app中可以通过api来获取到最新一条推送的内容:
didReceiveRemoteNotification 或 applicationdidFinishLaunchingWithOptions
第二个接口对应app已经挂起后用户点击推送的消息弹窗后的响应。
但是设备如果不在线呢?
关于这种情况,官方说法是有一种QoS保障: 当设备不在线时,如果收到很多条推送,只保留最后一条推送一定时间,如果超时则舍弃。
推送的过程经过如下步骤:
1.首先,我们的设备安装了具有推送功能的应用(应用程序要用代码注册消息推动),我们的 iOS设备在有网络的情况下会连接APNs推送服务器,连接过程中,APNS 服务器会验证devicetoken,连接成功后维持一个基于TCP 的长连接;
2.Provider(我们自己的应用服务器)收到需要被推送的消息并结合被推送的 iOS设备的devicetoken一起打包发送给APNS服务器;
3.APNS服务器将推送信息推送给指定devicetoken的iOS设备;
4.iOS设备收到推送消息后通知我们的应用程序并显示和提示用户(声音、弹出框)
23.热修复
4种热修复方式
1.Dynamic Framework
区别于静态库的编译时链接,动态库是在程序运行时动态加载的,并且可以多个程序共享,动态库中包含修复的的代码即可实现热修复。
这种技术问题在于虽然apple从xcode6开放了用户自己建立动态framework的权利(参考iOS中的SDK开发),但是为了保证系统安全性(可能多个程序公用同一个动态库,存在兼容性问题),apple只允许系统自己的动态库出现,而用户自己开发的这种动态库只能用于企业分发的app,无法用在上架appstore的app中。
2.CodePush
CodePush是微软推出的一种热修复技术,但主要用户对app中ReactNative(ReactNative是facebook提供的一种开源框架,能够用JavaScript脚本就可以写出App的界面,使用JS语法进行跨平台开发)部分的热修复
3.Lua+wax
Wax 阿里开发,lua脚本,支持arm64,普及少
2013年之前wax虽然有调试麻烦、线程不安全等问题,但已经是当时不错的热修复方案,但是12年推出的64位的iphone5s,让一直以来只支持32位的wax陷入尴尬,Lua字节码也有32位与64位编译区分,所以原来的Wax中的stdlib库在64位无法运行,阿里从13年接手了长期无人维护的wax,修改了原有的Lua字节码打包逻辑使其能在64位正常运行。另外,阿里完善了block调用、lua调试等方面,让wax方案更加易用。
Lua是一个在大量的游戏中使用的简洁、轻量、可扩展的脚本语言,用于实现游戏的可配置和可更新。但Lua需要预先绑定很多C函数才可在脚本中使用,所以单独使用Lua无法做到高复用性。Wax的核心逻辑是替换函数,并且连接了Lua与Objective-C runtime,使得Lua可以调用和替换任意类的方法,甚至新增类、方法。这样一来就能在app不发布新版的情况下,通过远程下载脚本的方式修复线上app里的bug、甚至新增一些功能。
使用时,工程需要集成Lua解释器和Wax框架。
4.JSPatch
与Lua+wax方案的想法相同,微信团队(bang)利用Objective-C Runtime的特性及iOS7系统开始支持的JavaScript脚本运行,开发了自己的JavaScript热修复方案--JSPatch。
JSPatch中JavaScript脚本运行的基础是JavaScriptCore,JavaScriptCore是一种JavaScript引擎,主要为webkit提供脚本处理能力,可以将JavaScript的能力更轻便地、高性能地带给原生的iOS应用,可以实现oc和JavaScript两种语言的互转。
JSPatch能做到通过JS调用和改写OC方法:通过类名和方法名反射得到相应的类和方法,也可以替换某个类的方法为新的实现,或新注册一个类,为类添加方法。JSPatch 的原理就是:JS传递字符串给OC,OC通过 Runtime 接口调用和替换OC方法。实际使用时,我们一般通过下发js脚本,在app启动时判断是否需要执行脚本,如果有故障需要修复,就执行指定的js脚本。如果我们自己实现js脚本的下发,需要自己开发平台接口,保证脚本传输的安全性和高并发能力。JSPatch善解人意的提供了这样的JSPatch平台,并且开发了OC转JS工具,便于将待修复的OC代码转为热修复的JS代码。
热修复需要注意:
苹果未来将禁用APP内部的“动态分发”功能,苹果禁的JSPatch / wax/ rollout热修复框架
要有及时的线上产品故障报警,包含crash的报警和核心业务出错的报警,开发及时定位到问题;
对于已经通过热修复解决的问题,下个版本要替换为新的native代码;
不要使用热修复添加业务模块或大量非修复性的逻辑代码,将给维护带来很大麻烦;
热修复对应的补丁要充分考虑相关业务影响,测试充分,避免热修复带来新问题;
24.组件化开发
组件化开发,就是将一个臃肿,复杂的单一工程的项目, 根据功能或者属性进行分解,拆分成为各个独立的功能模块或者组件 ; 然后根据项目和业务的需求,按照某种方式, 任意组织成一个拥有完整业务逻辑的工程。这就是所谓的组件化开发。
组件化开发的优点
既然针对上述问题提到了组件化开发,那就要必要交代一下组件化模块化开发的好处。这样在进行对比的时候,可以更加清楚的定位我们想要解决的问题。开判断组件化开发是不是我们需要的团队开发模式。
1、组件之间相互独立。各组件开发成员之间的代码想相互独立编写的,独立编译,独立运行和独立测试的。
2、资源的重复里用,尤其是功能性,工具性的代码,可以很轻松的重复里用
3、迭代的效率提高。通过迭代进行功能的增减,只需要进行组件的拆分和组合。很方便也很高效
组件化开发需要注意的问题
新项目在进行组件化拆分的时候;或者老项目就行组件化重构的时候需要考虑一下几个问题。比较对于耦合度很高的老项目,解耦并不是一件容易的事情。
1、 组件拆分的依据,即要把哪些内容划分成为一个组件?
可以按照以下几个方面进行拆分
① 基础组件
全局常量、常用宏、常用的分类、常用三方框架的隔离封装、还有一些比较常用的小功能类
② 功能组件
图片轮播器、图文菜单、视频中的弹幕、相机、录像、二维码、下载功能、个性定制的提示框等等,都可以封装在一个组件中
③ 业务模块
例如电商的购物车,订单管理、下单流程、个人中心
再例如视频或者直播的会员管理、视频播放全屏,右下角小屏幕,缓存等等
2、组件化存在方式
一直在说组件化,到底什么是组件呢。组件的存在方式又是什么呢?
组件形式: 每个组件都是以pod库的形式存在
组件内部:组件内部按照自己喜欢的开发模式以文件夹的形式进行划分
组件测试:每个组件对单独对应一个demo,用来完成该组件的功能测试,这样测试机能被解耦开
3、组件的组合方式
既然组件的存在方式是以每个pod库的形式存在的。那么我们组合组件的方法就是通过利用CocoaPods的方式添加安装各个组件。
25.Instruments性能检测
Time Profiler:分析代码的执行时间,找出导致程序变慢的原因。
Zombies:检查是否访问了僵尸对象,但是这个工具只能从上往下检查,不智能
Allocations:用来检查内存分配,写算法的那批人也用这个来检查
Leaks:检查内存,看是否有内存泄露
Cocoa Layout:自动布局
Core Animation:核心动画
FPS:一秒钟渲染多少帧 Frame Per Second = FPS。
Core Animation给我们提供了周期性的FPS,并且考虑到了发生在程序之外的动画,界面滑动FPS可以进行测试。一般FPS是60左右,过于低的话需要进行优化。
26.加密
常见加密方案
编码方案 Base64
哈希(散列)函数 MD5(消息摘要算法)
SHA1
SHA256
对称加密算法 DES
AES
非对称加密算法 RSA
HTTPS HTTP+SSL协议
对称加密的特点
加密/解密使用相同的密钥
加密和解密的过程是可逆的
经典算法
DES 数据加密标准
AES 高级加密标准
提示:
加密过程是先加密,再base64编码
解密过程是先base64解码,再解密
非对称加密的特点
使用 公钥 加密,使用 私钥 解密
使用 私钥 加密,使用 公钥 解密(私钥签名,公钥验签)
公钥是公开的,私钥保密
加密处理安全,但是性能极差
经典算法-->RSA
RSA——非对称加密,会产生公钥和私钥,公钥在客户端,私钥在服务端。公钥用于加密,私钥用于解密。
AES——对称加密,直接使用给定的秘钥加密,使用给定的秘钥解密。(加密解密使用相同的秘钥)
MD5——一种单向的加密方式,只能加密,不能解密
对不同的数据加密,得到的结果是定长的,MD5对不同的数据进行加密,得到的结果都是 32 个字符长度的字符串
消息认证机制(HMAC)简单说明
原理
消息的发送者和接收者有一个共享密钥
发送者使用共享密钥对消息加密计算得到MAC值(消息认证码)
消息接收者使用共享密钥对消息加密计算得到MAC值
比较两个MAC值是否一致
使用
客户端需要在发送的时候把(消息)+(消息·HMAC)一起发送给服务器
服务器接收到数据后,对拿到的消息用共享的KEY进行HMAC,比较是否一致,如果一致则信任
Base64编码——对字节数组转换成字符串的一种编码方式
特点:可以将任意的二进制数据进行Base64编码
结果:所有的数据都能被编码为并只用65个字符(A~Z a~z 0~9 + / =)就能表示的文本文件。
注意:对文件进行base64编码后文件数据的变化:编码后的数据~=编码前数据的4/3,会大1/3左右。
27.优化CPU消耗
1.对象创建
原因:对象的创建会分配内存、调整属性,甚至还有文件读写等操作,比较消耗 CPU 资源。那么使用轻量级的对象,可能会对性能有所优化。比如 CALayer 比 UIView 要轻量许多,那么不需要响应触摸事件的对象,用 CALayer 比较合适。通过 sb 创建视图对象时,其资源消耗要比直接使用代码创建对象要大的多。在性能敏感的界面中,sb 不是一个好的选择。
解决方案:
(1) 尽量推迟对象创建的时间,并把对象的创建分配到多个任务中去(推荐使用懒加载)。
(2) 如果对象可以复用,而且复用的代价比释放、创建新对象要小,那么这类对象要尽量放在一个缓存池中复用。
2.对象调整
原因:对象调整也是经常消耗 CPU 资源的地方,在这里特别说明一下 CALayer,CALayer 内部并没有属性,当调用属性方法的时候,它内部是通过运行时为对象临时添加一个方法,并把对应的值保存在内部的一个字典当中,同时还会通知 delegate、创建动画等,非常消耗资源。UIView 的关于显示的相关属性(frame/bounds/transform)等实际上都是 CALayer 的属性映射而来的,所以对 UIView 的这些属性进行调整的时候,消耗的资源要远大于一般的属性。
解决方案:当视图层次调整的时候,消耗的资源要远大于一般的属性,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。
3.对象销毁
原因:对象销毁虽然消耗资源不多,但累计起来也是不容忽视的。通常当容器类持有大量对象时,其销毁时的资源消耗也就相当明显。
解决方案:对象的释放也尽量放到后台线程中去执行。这里有个坑:把对象捕捉到 block 中,然后扔到后台去随便发个消息以避免编译器警告,就可以让对象在后台销毁了。
4.布局计算
视图布局的计算是 App 中常见的消耗 CPU 资源的地方,如果能在后台线程中提前计算好视图布局,并对布局进行缓存的话,那么这个地方就不会出现任务性能问题了。
不论通过何种技术对视图进行布局,最终都会落到 UIView.frame/bounds/center 等属性的调整上,所以尽量提前计算调整好布局,在需要时一次性调整好对应属性,而不要多次、频繁的计算和调整这些属性。
5.Autolayout
Autolayout 是苹果集成的技术,在大部分情况下也可以很好的提高开发效率,但是 Autolayout 对复杂视图来说常常会产生严重的性能问题。随着视图的增长, Autolayout 带来的 CPU 消耗会呈指数级上升。如果不想手动调整 frame 等属性,可以使用第三方 SDK。
6.文本计算
如果一个界面中包含大量的文本,文本的宽高计算会占用很大一部分资源,并且不可避免。如果没有特殊要求,可以参考下 UILabel 内部的实现方式:[NSAttributeString boundingRectWithSize:context:]来计算文本宽高,用 [NSAttributrString drawWithRect:options:context:] 来绘制文本。尽管这两个方法性能不错,但是扔需要放到后台线程以避免阻塞主线程。
如果你使用 CoreText 绘制文本,那就可以先生成 CoreText 排版对象,然后自己计算,并且 CoreText 对象还可以保留以供稍后绘制使用。
7.文本渲染
屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版,绘制为 bitmap 图显示的。常见的文本控件(UILabel、UITextView等),其排版和绘制都是在主线程中进行的。当显示大量文本的时候,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或者最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但带来的优势也非常大,CoreText 对象创建好以后,能直接获取文本宽高等信息,避免了多次计算(调整 UILabel 时计算一遍,UILabel 绘制时内部在计算一遍);CoreText 占用内存较少,可以缓存下来以备多次渲染。
8.图片的解码
当你用 UIImage 或者 CGImageSource 的那几个方法去创建图片的时候,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 CPU 前,CGImage 中的数据才会得到解码,这一步是发生在主线程的,并且不可避免。如果想绕开这个机制,常见的做法就是在后台线程先把图片绘制到 CGBitmapContent 中,然后从 Bitmap 直接创建图片。目前常见的网络图片库都有这个功能
9.图片的绘制
图片的绘制通常是指那些以 CG 开头的方法把图像放到画布中,然后从画布中创建图片并显示的一个过程,最常见的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程去执行
28.单例
单例的意思就是一个特殊的类,而不是一个实例,单例是全局都可以使用的唯一的一个类
系统单例:
1、UIApplication(应用程序实例)
2、NSNotificationCenter(消息中心):
3、NSFileManager(文件管理):
4、NSUserDefaults(应用程序设置):
5、NSURLCache(请求缓存):
6、NSHTTPCookieStorage(应用程序cookies池):
1.单例模式的要点:
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
2.单例模式的优点:
1.安全性和唯一性:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。单例类也可以防止他人复制(copy),保留(retain)或释放(release)实例。如果您发现需要,您可以创建自己的单例。例如,如果您有一个类为应用程序中的其他对象提供声音,则可以将其设为单例。
2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
创建步骤
1、为你的单例类声明一个静态的实例(声明静态全局变量),并且初始化它的值为nil; eg:
static TestSingleton *testSingleton = nil;
这样,在获取实例的方法中,只有在静态实例为nil的时候,产生一个你的类的实例,这个实例通常被称为共享的实例;
2、重写allocWithZone方法,用于确定:不能够使用其他的方法创建我们类的实例,限制用户只能通过获取实例的方法得到这个类的实例。所以我们在allocWithZone方法中直接返回共享的类实例;
3、写+(instancetype)shareSingleton的函数体
创建方法
一、传统方法
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
if (singleton == nil){
singleton = [[self alloc] init];
}
return singleton;
}
二、推荐方法(GCD)
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
//给单例加一个线程锁
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[Singleton alloc] init];
});
return singleton;
三、线程加锁方法(不推荐使用)
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
@synchronized(self) {
singleton = [[Singleton alloc] init];
}
return singleton;
}
注:“线程加锁方法”这样性能不是很好,因为每次调用+ (instancetype)sharedSingleton函数都会付出取锁的代价
吞吞吐吐:
这样写的话是保证了线程安全,但通过自带的alloc或者new来进行实例化,还是不能保证该对象只被创建一次,如何避免呢?他就回答不上了,其实很简单:
.h头文件:
@interface TestSingleton : NSObject
@property (nonatomic, copy)NSString *testStr;
+ (TestSingleton *)shareinstance;
@end
.m文件:
+ (TestSingleton *)shareinstance {
static TestSingleton *testSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//testSingleton = [self singletonAlloc]; //后面使用该创建方法
testSingleton = [self new];//[[self alloc]init];
});
return testSingleton;
}
//我们需要重载alloc、new方法
+ (instancetype)singletonAlloc
{
return [super alloc];
}
+ (instancetype)alloc
{
//加断言,使用alloc会蹦,并且reason提示
NSAssert(NO, @"不要用alloc,要使用singletonAlloc方法创建");
return nil;
}
+ (instancetype)new
{
return [self alloc];
}
+ (TestSingleton *)shareinstance {
static TestSingleton *testSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
testSingleton = [[self singletonAlloc]init];
});
return testSingleton;
}
重载了alloc、allocWithZone和new方法,在alloc方法中加上断言来提醒,让用户不能使用alloc创建实例。
普通类允许调用者根据需要创建尽可能多的类的实例,而对于单例类,每个进程只能有一个类的实例,保证了单例数据的唯一性,安全性
29.Objective-C堆和栈的区别?
答: 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
30.关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用
答:
const 意味着"只读",下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
结论:
关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。
如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
1).欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初
始化,因为以后就没有机会再去改变它了;
2).对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指
定为 const;
3).在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
4).对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
5).对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。
31.关键字volatile有什么含意?并给出三个不同的例子。
答:一个定义为 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
并行设备的硬件寄存器(如:状态寄存器)
一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
多线程应用中被几个任务共享的变量
32. 一个参数既可以是const还可以是volatile吗? 一个指针可以是volatile 吗?解释为什么。
答:1).是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2).是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
33.static 关键字的作用:
答:
1).函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
2).在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3).在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明
它的模块内;
4).在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5).在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
34.线程与进程的区别和联系?
答:
1). 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性
2). 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
3). 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
4.)线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
5). 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
35.列举几种进程的同步机制,并比较其优缺点。
答: 原子操作 信号量机制 自旋锁 管程,会合,分布式系统
36. 进程之间通信的途径
答:共享存储系统消息传递系统管道:以文件系统为基础
37. 进程死锁的原因
答:资源竞争及进程推进顺序非法
38. 死锁的4个必要条件
答:互斥、请求保持、不可剥夺、环路
39. 死锁的处理
答:鸵鸟策略、预防策略、避免策略、检测与解除死锁
40.cocoa touch框架
答:iPhone OS 应用程序的基础 Cocoa Touch 框架重用了许多 Mac 系统的成熟模式,但是它更多地专注于触摸的接口和优化。
UIKit 为您提供了在 iPhone OS 上实现图形,事件驱动程序的基本工具,其建立在和 Mac OS X 中一样的 Foundation 框架上,包括文件处理,网络,字符串操作等。
Cocoa Touch 具有和 iPhone 用户接口一致的特殊设计。有了 UIKit,您可以使用 iPhone OS 上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。
各色俱全的框架 除了UIKit 外,Cocoa Touch 包含了创建世界一流 iPhone 应用程序需要的所有框架,从三维图形,到专业音效,甚至提供设备访问 API 以控制摄像头,或通过 GPS 获知当前位置。
Cocoa Touch 既包含只需要几行代码就可以完成全部任务的强大的 Objective-C 框架,也在需要时提供基础的 C 语言 API 来直接访问系统。这些框架包括:
Core Animation:通过 Core Animation,您就可以通过一个基于组合独立图层的简单的编程模型来创建丰富的用户体验。
Core Audio:Core Audio 是播放,处理和录制音频的专业技术,能够轻松为您的应用程序添加强大的音频功能。
Core Data:提供了一个面向对象的数据管理解决方案,它易于使用和理解,甚至可处理任何应用或大或小的数据模型。
下面是 Cocoa Touch 中一小部分可用的框架:
音频和视频:Core Audio ,OpenAL ,Media Library ,AV Foundation
数据管理 :Core Data ,SQLite
图形和动画 :Core Animation ,OpenGL ES ,Quartz 2D
网络:Bonjour ,WebKit ,BSD Sockets
用户应用:Address Book ,Core Location ,Map Kit ,Store Kit
41.自动释放池是什么,如何工作
答:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放.它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
42. Objective-C的优缺点。
答:objc优点:
1). Cateogies
2). Posing
3). 动态识别
4).指标计算
5).弹性讯息传递
6).不是一个过度复杂的 C 衍生语言
7).Objective-C 与 C++ 可混合编程
objc缺点:
1).不支援命名空间
2).不支持运算符重载
3).不支持多重继承
4).使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
43.sprintf,strcpy,memcpy使用上有什么要注意的地方。
答:
1). sprintf是格式化函数。将一段数据通过特定的格式,格式化到一个字符串缓冲区中去。sprintf格式化的函数的长度不可控,有可能格式化后的字符串会超出缓冲区的大小,造成溢出。
2).strcpy是一个字符串拷贝的函数,它的函数原型为strcpy(char *dst, const char *src
将src开始的一段字符串拷贝到dst开始的内存中去,结束的标志符号为 ‘\0',由于拷贝的长度不是由我们自己控制的,所以这个字符串拷贝很容易出错。
3). memcpy是具备字符串拷贝功能的函数,这是一个内存拷贝函数,它的函数原型为memcpy(char dst, const char src, unsigned int len);将长度为len的一段内存,从src拷贝到dst中去,这个函数的长度可控。但是会有内存叠加的问题。
44.http和scoket通信的区别。
答:HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。
HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。
HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。
http是客户端用http协议进行请求,发送请求时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配合(当然也非绝对)。 http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取http长连接 技术)。iphone主要使用类是NSUrlConnection。
scoket是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方 都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是CFSocketRef。
45.TCP和UDP的区别
答: TCP全称是Transmission Control Protocol,中文名为传输控制协议,它可以提供可靠的、面向连接的网络数据传递服务。传输控制协议主要包含下列任务和功能:
- 确保IP数据报的成功传递。
- 对程序发送的大块数据进行分段和重组。
- 确保正确排序及按顺序传递分段的数据。
- 通过计算校验和,进行传输数据的完整性检查。
TCP连接的三次握手:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN+RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次状态。
UDP为用户数据报协议,非连接的不可靠的点到多点的通信;
TCP提供的是面向连接的、可靠的数据流传输,而UDP提供的是非面向连接的、不可靠的数据流传输。
简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般
46.请简要说明viewDidLoad和viewDidUnload何时调用
答:viewDidLoad在view从nib文件初始化时调用,loadView在controller的view为nil时调用。此方法在编程实现view时调用,view控制器默认会注册memory warning notification,当view controller的任何view没有用的时候,viewDidUnload会被调用,在这里实现将retain的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。
简述视图控件器的生命周期。
loadView 尽管不直接调用该方法,如多手动创建自己的视图,那么应该覆盖这个方法并将它们赋值给试图控制器的 view 属性。
viewDidLoad 只有在视图控制器将其视图载入到内存之后才调用该方法,这是执行任何其他初始化操作的入口。
viewDidUnload 当试图控制器从内存释放自己的方法的时候调用,用于清楚那些可能已经在试图控制器中创建的对象。
viewVillAppear 当试图将要添加到窗口中并且还不可见的时候或者上层视图移出图层后本视图变成顶级视图时调用该方法,用于执行诸如改变视图方向等的操作。实现该方法时确保调用 [super viewWillAppear:
viewDidAppear 当视图添加到窗口中以后或者上层视图移出图层后本视图变成顶级视图时调用,用于放置那些需要在视图显示后执行的代码。确保调用 [super viewDidAppear:] 。
47. 简述内存分区情况
答:
1).代码区:存放函数二进制代码
2).数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变量、静态变量、常量
3).堆区:通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释放
4).栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数
48.队列和栈有什么区别:
答:队列和栈是两种不同的数据容器。从”数据结构”的角度看,它们都是线性结构,即数据元素之间的关系相同。
队列是一种先进先出的数据结构,它在两端进行操作,一端进行入队列操作,一端进行出列队操作。
栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作。
49.HTTP协议中,POST和GET的区别是什么?
答:GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。
GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头<requestline>中。以?分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送,
如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体<request-body>中.
GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。
传输数据的大小:
GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。
安全性:
POST的安全性要比GET的安全性高;
通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
1).GET 方法
GET 方法提交数据不安全,数据置于请求行,客户端地址栏可见;
GET 方法不可以设置书签
2).POST 方法
POST 方法提交数据安全,数据置于消息主体内,客户端不可见
POST 方法可以设置书签
50.iOS的系统架构
答: iOS的系统架构分为( 核心操作系统层 theCore OS layer )、( 核心服务层theCore Services layer )、( 媒体层 theMedia layer )和( Cocoa 界面服务层 the Cocoa Touch layer )四个层次。