1.探究KVO的底层实现原理
https://www.jianshu.com/p/829864680648
· KVO是基于runtime机制实现的
· 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
· 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person
· 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
· 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey:和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context:也会被调用。
· 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类
附注: KVC底层实现原理(如下)
KVC运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.
KVC
主要通过isa-swizzling, 来实现其内部查找定位的. 默认的实现方法�由NSOject提供isa指针, 如其名称所指,(就是is a kind of的意思),指向分发表对象的类. 该分发表实际上包含了指向实现类中的方法的指针, 和其它数据。
具体主要分为三大步
第一步:寻找该属性有没有setsetter方法?有,就直接赋值
第二步:寻找有没有该属性带下划线的成员属性?有,就直接赋值
第三步:寻找有没有该属性的成员属性?有,就直接赋值
或者这么说
1、首先搜索setKey:方法.(key指成员变量名, 首字母大写)
2、上面的setter方法没找到, 如果类方法accessInstanceVariablesDirectly返回YES. 那么按_key, _isKey,key, iskey的顺序搜索成员名.(NSKeyValueCodingCatogery中实现的类方法, 默认实现为返回YES)
3、如果没有找到成员变量, 调用setValue:forUnderfinedKey:
·
iOS 开发深入浅出Rumtime运行时之消息发送机制详解
· Class 是指向类结构体的指针,该类结构体含有一个指向其父类类结构的指针,该类方法的链表,该类方法的缓存以及其他必要信息。
· NSObject 的class 方法就返回这样一个指向其类结构的指针。每一个类实例对象的第一个实例变量是一个指向该对象的类结构的指针,叫做isa。通过该指针,对象可以访问它对应的类以及相应的父类。
· Method含义注意这里所说的方法链表里面存储的是Method 类型的。图一中selector 就是指Method 的SEL, address 就是指Method 的IMP。
· 一个方法Method,其包含一个方法选标SEL – 表示该方法的名称,一个types – 表示该方法参数的类型,一个IMP - 指向该方法的具体实现的函数指针。
SEL 的含义:
它是一个指向objc_selector 指针,表示方法的名字/签名
· 我们就很清楚地知道IMP 的含义:IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self 指针), 调用方法的选标SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说IMP 是消息最终调用的执行代码,是方法真正的实现代码。我们可以像在C语言里面一样使用这个函数指针。
2消息调用过程
https://blog.csdn.net/kuangdacaikuang/article/details/53445960
消息函数objc_msgSend
编译器会将消息转换为对消息函数objc_msgSend的调用
id objc_msgSend(id self, SEL op, ...);
SEL和IMP的动态绑定
objc_msgSend消息函数做了动态绑定所需要的一切工作:
1,它首先找到SEL 对应的方法实现IMP。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。
2,然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传递给方法实现IMP。
3,最后,将方法实现的返回值作为该函数的返回值返回
通过SEL查找IMP的过程
objc_msgSend 会根据方法选标SEL 在类结构的方法列表中查找方法实现IMP
1,首先去该类的方法cache 中查找,如果找到了就返回它;
2,如果没有找到,就去该类的方法列表中查找。如果在该类的方法列表中找到了,则将IMP 返回,并将它加入cache中缓存起来。根据最近使用原则,这个方法再次调用的可能性很大,缓存起来可以节省下次调用再次查找的开销。
3,如果在该类的方法列表中没找到对应的IMP,在通过该类结构中的super_class指针在其父类结构的方法列表中去查找,直到在某个父类的方法列表中找到对应的IMP,返回它,并加入cache 中;
4,如果在自身以及所有父类的方法列表中都没有找到对应的IMP,则看是不是可以进行动态方法决议(后面有专文讲述这个话题);
5,如果动态方法决议没能解决问题,进入下面要讲的消息转发流程。
动态方法处理–总结
1,首先判断是否实现了resolveInstanceMethod,如果没有实现,返回NULL,进入下一步处理;
2,如果实现了,调用resolveInstanceMethod,获取返回值;
3,如果返回值为YES,表示resolveInstanceMethod声称它已经供了selector 的实现,因此再次查找method list,如果依然找到对应的IMP,则返回该实现,否则示警告信息,返回NULL,进入下一步处理;
4,如果返回值为NO,返回NULL,进入下一步处理(消息转发机制);
3.iOS消息转发机制
https://www.jianshu.com/p/60f19a844f18
消息转发分为两个阶段。第一阶段先征询接收者所属的类,看其是否能动态添加方法,已处理当前这个“未知的选择子”,这叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”。如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了。此时运行期系统就会请求接收者以其他手段来处理与消息相关的方法调用。细分为两步:首先,让接收者看看有没有其他对象能处理这条消息。如果有,则运行期系统会把消息转给那个接收者,于是消息转发结束。如果没有这个“备援接收者”,则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节封装到NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。
动态方法解析:
对象在收到无法解读的消息之后,首先将调用所属类的下列类方法:
该方法的参数就是那个未知的选择子,其返回值为布尔类型,表示这个类是否能新增一个实例方法用以处理此选择子。在继续往下执行转发机制之前,本类有一个机会处理此选择子方法。假如是一个类方法,那么将会调用另外一个方法,如图2:
need-to-insert-img
备援接收者:
need-to-insert-img
当前接收者还有第二次机会处理未知的选择子,在这一步中,运行期系统会询问是否能将该消息转发给其他的接收者处理。对应的方法如图3:
参数为未知的选择子,如当前接收者能找到备援对象,则将其返回,找不到就返回nil。通过此方案,我们可以用“组合”来模拟出“多重继承”的某些特性。在一个对象内部,可能还有一系列其他对象,该对象可以经由此方法将能够处理某选择子的相关内部对象返回,这样的话,在外界看来好像是该对象亲自处理了这些消息。
need-to-insert-img
完整的消息转发:
如果转发已经到了这一步,那么唯一能做的就是启动完整的消息转发机制了。首先创建NSIvocation对象,把尚未处理的那条消息有关的细节全部封到其中。此对象包含选择子、目标、参数。在出发NSIvocation对象时,”消息派发系统“将亲自出马,把消息指派给目标对象。会调用图4的方法来转发消息:
这个方法的实现可以写的很简单,只需要改变调用目标,使消息在新目标上得以调用即可。然而这样实现出来的方法与”备援接收者“反感所实现的方法等效,所以很少有人采用这么简单的实现方式。比较有用的实现方式为:在触发消息前,先以某种方式改变消息内容,比如追加另外一个参数,或者是改换选择子等等。实现此方法时若发现不应该由本类处理,则需要调用超类的同名方法。这样的话,集成体系中的每个类都有机会处理此调用请求,直至NSObject。如果最后调用了NSObject类的方法,那么该方法还会继而调用”doesNotRecognizeSelector:“以抛出异常,此异常表明选择子最终未能得到处理。
tcp和udp的区别
1、TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
2、为什么UDP有时比TCP更有优势?
UDP以其简单、传输快的优势,在越来越多场景下取代了TCP,如实时游戏。
(1)网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。
(2)TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于TCP内置的系统协议栈中,极难对其进行改进。
采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大,基于UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。
七层模型
应用层
网络服务与最终用户的一个接口。
协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)
格式有,JPEG、ASCll、DECOIC、加密格式等
建立、管理、终止会话。(在五层模型里面已经合并到了应用层)
对应主机进程,指本地主机与远程主机正在进行的会话
定义传输数据的协议端口号,以及流控和差错校验。
协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层
进行逻辑地址寻址,实现不同网络之间的路径选择。
协议有:ICMP IGMP IP(IPV4 IPV6)ARP RARP
建立逻辑连接、进行硬件地址寻址、差错校验 [2] 等功能。(由底层网络定义协议)
将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
建立、维护、断开物理连接。(由底层网络定义协议)
响应链
https://www.jianshu.com/p/05cbcd774f45
https://www.jianshu.com/p/2e074db792ba
响应链是什么时候创建的:当一个view被add到superView上的时候,他的nextResponder属性就会被指向它的superView,当controller被初始化的时候,self.view(topmost view)的nextResponder会被指向所在的controller,而controller的nextResponder会被指向self.view的superView,这样,整个app就通过nextResponder串成了一条链,也就是我们所说的响应链。所以响应链就是一条虚拟的链,并没有一个对象来专门存储这样的一条链,而是通过UIResponder的属性串连起来
事件处理的整个流程总结:
1.触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中
2.UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口,这时候执行事件传递流程 找到一个最合适的视图来处理触摸事件。(这时候如果某一个view上添加了手势,且该手势能响应对应事件,则走手势的响应,根据手势的设置来决定是否阻断下面的步骤,但是事件传递过程依旧。如没有或者不能响应则继续走下面步骤)
3.最合适的view会调用自己的touches方法处理事件 如果需要的话可以把事件顺着响应者链条向上抛。
何通过视图(view)获取该视图所在的控制器(viewController)
方法函数
need-to-insert-img
• 如何扩大view的响应范围
http://www.cocoachina.com/bbs/read.php?tid=285433
#import
@interface UIButton (EnlargeTouchArea)
- (void)setEnlargeEdgeWithTop:(CGFloat) top right:(CGFloat) right bottom:(CGFloat) bottom left:(CGFloat) left;
@end
#import "UIButton+EnlargeTouchArea.h"
#import
@implementation UIButton (EnlargeTouchArea)
static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;
- (void) setEnlargeEdgeWithTop:(CGFloat) top right:(CGFloat) right bottom:(CGFloat) bottom left:(CGFloat) left
{
objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (CGRect) enlargedRect
{
NSNumber* topEdge = objc_getAssociatedObject(self, &topNameKey);
NSNumber* rightEdge = objc_getAssociatedObject(self, &rightNameKey);
NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);
NSNumber* leftEdge = objc_getAssociatedObject(self, &leftNameKey);
if (topEdge && rightEdge && bottomEdge && leftEdge)
{
return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,
self.bounds.origin.y - topEdge.floatValue,
self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,
self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
}
else
{
return self.bounds;
}
}
- (UIView*) hitTest:(CGPoint) point withEvent:(UIEvent*) event
{
CGRect rect = [self enlargedRect];
if (CGRectEqualToRect(rect, self.bounds))
{
return [super hitTest:point withEvent:event];
}
return CGRectContainsPoint(rect, point) ? self : nil;
}
Apple的iOS人机交互设计指南中指出,按钮点击热区应不小于44x44pt,否则这个按钮就会让用户觉得“很难用”,因为明明点击上去了,却没有任何响应。
但我们有时做自定义Button的时候,设计图上的给出按钮尺寸明显要小于这个数。例如我之前做过的自定义Slider上的Thumb只有12x12pt,做出来后我发现自己根本点不到按钮……
这个问题在WWDC 2012 Session 216视频中提到了一种解决方式。它重写了按钮中的pointInside方法,使得按钮热区不够44×44大小的先自动缩放到44×44,再判断触摸点是否在新的热区内。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect bounds = self.bounds;
//
若原热区小于44x44,则放大热区,否则保持原大小不变
CGFloat widthDelta = MAX(44.0 - bounds.size.width, 0);
CGFloat heightDelta = MAX(44.0 - bounds.size.height, 0);
bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
return CGRectContainsPoint(bounds, point);
}
https://blog.csdn.net/kuangdacaikuang/article/details/78891379#8-app-groups
1、 URL Scheme
这个是iOS app通信最常用到的通信方式,App1通过openURL的方法跳转到App2,并且在URL中带上想要的参数,有点类似http的get请求那样进行参数传递。这种方式是使用最多的最常见的,使用方法也很简单只需要源App1在info.plist中配置LSApplicationQueriesSchemes,指定目标App2的scheme;然后在目标App2的info.plist中配置好URL types,表示该app接受何种URL scheme的唤起。
2、Keychain
iOS系统的Keychain是一个安全的存储容器,它本质上就是一个sqllite数据库,它的位置存储在/private/var/Keychains/keychain-2.db,不过它所保存的所有数据都是经过加密的,可以用来为不同的app保存敏感信息,比如用户名,密码等。iOS系统自己也用keychain来保存VPN凭证和Wi-Fi密码。它是独立于每个App的沙盒之外的,所以即使App被删除之后,Keychain里面的信息依然存在。
3、UIPasteboard
顾名思义,UIPasteboard是剪切板功能,因为iOS的原生控件UITextView,UITextField 、UIWebView,我们在使用时如果长按,就会出现复制、剪切、选中、全选、粘贴等功能,这个就是利用了系统剪切板功能来实现的。而每一个App都可以去访问系统剪切板,所以就能够通过系统剪贴板进行App间的数据传输了。
4、UIDocumentInteractionController
UIDocumentInteractionController主要是用来实现同设备上app之间的共享文档,以及文档预览、打印、发邮件和复制等功能。它的使用非常简单.
5、local socket
这种方式不太常见,也是很容易被iOS开发者所忽略但是特别实用的一种方法。它的原理很简单,一个App1在本地的端口port1234进行TCP的bind和listen,另外一个App2在同一个端口port1234发起TCP的connect连接,这样就可以建立正常的TCP连接,进行TCP通信了,那么就想传什么数据就可以传什么数据了。
6、AirDrop
通过AirDrop实现不同设备的App之间文档和数据的分享;
iOS SDK中封装好的类在App之间发送数据、分享数据和操作数据;
8、App Groups
App Group用于同一个开发团队开发的App之间,包括App和Extension之间共享同一份读写空间,进行数据共享。同一个团队开发的多个应用之间如果能直接数据共享,大大提高用户体验。
为什么同一个内存地址,不同程序读出的数值不一样?(C语言程序)
https://zhidao.baidu.com/question/616215866008338412.html
说实话你没造成内存访问违规算你运气好。你试着输7ffe0000看看?不访问违规才怪。根据你输入的地址,这个地方一般都是进程的主线程堆栈区域,或者进程的动态堆区域。这个地方的所有内存页面都是可读可写的。而为什么同一地址在不同程序中有不同的值?因为在windows系统中你所看到的所有地址都是虚拟地址。每个进程都有完全独立的4GB虚拟地址空间,A进程的0x00300000被映射到物理页面1上,B进程的0x00300000被映射到物理页面2上。虽然他们的虚拟地址相同,但是被映射的物理页面时完全不同的。
不同进程有自己的虚拟内存空间所以不同进程的同一个虚拟地址指向的不是同一块物理内存建议看看虚拟内存相关的资料
iOS程序执行顺序和UIViewController 的生命周期(整理)
https://www.jianshu.com/p/d60b388b19f5
一. iOS程序的启动执行顺序
程序启动顺序图
need-to-insert-img
layoutSubviews如何手动调用ViewWillLayoutSubView
https://blog.csdn.net/jeffasd/article/details/51321763
http://www.cocoachina.com/ios/20160530/16522.html
layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews
但是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
在苹果的官方文档中强调:
//当autoresizing属性达不到你想要的 你就需要重写该方法
You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.
layoutSubviews, 当我们在某个类的内部调整子视图位置时,需要调用。
反过来的意思就是说:如果你想要在外部设置subviews的位置,就不要重写。
刷新子对象布局
-layoutSubviews方法:这个方法,默认没有做任何事情,需要子类进行重写
-setNeedsLayout
方法:标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
-layoutIfNeeded
方法:如果,有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)
如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局
在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]
重绘
-drawRect:(CGRect)rect方法:重写此方法,执行重绘任务
-setNeedsDisplay
方法:标记为需要重绘,异步调用drawRect
-setNeedsDisplayInRect:(CGRect)invalidRect
方法:标记为需要局部重绘
sizeToFit会自动调用sizeThatFits方法;
sizeToFit不应该在子类中被重写,应该重写sizeThatFits
sizeThatFits传入的参数是receiver当前的size,返回一个适合的size
sizeToFit可以被手动直接调用
sizeToFit和sizeThatFits方法都没有递归,对subviews也不负责,只负责自己
———————————-
layoutSubviews对subviews重新布局
layoutSubviews方法调用先于drawRect
setNeedsLayout在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews
layoutIfNeeded方法如其名,UIKit会判断该receiver是否需要layout.根据Apple官方文档,layoutIfNeeded方法应该是这样的
layoutIfNeeded遍历的不是superview链,应该是subviews链
drawRect是对receiver的重绘,能获得context
setNeedDisplay在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iPhone device的刷新频率是60hz,也就是1/60秒后重绘
3种类型的block
_NSConcreteGlobalBlock:全局的静态block,类似函数。如果block里不获取任何外部变量。或者的变量是全局作用域时,如成员变量、属性等;这个时候就是Global类型
_NSConcreteStackBlock:保存在栈中的block,栈都是由系统管理内存,当函数返回时会被销毁。__block类型的变量也同样被销毁。为了不被销毁,block会将block和__block变量从栈拷贝到堆。
_NSConcreteMallocBlock:保存在堆中的block,堆内存可以由开发人员来控制。当引用计数为0 时会被销毁。
Block的原理及使用
https://www.jianshu.com/p/07a478c78f8ehttps://www.cnblogs.com/dahe007/p/6067591.html
AutoreleasePool的原理和实现
AutoreleasePool的内存结构就是一个双向链表。
一个线程的autoreleasepool就是一个指针栈。栈中存放的指针指向加入需要release的对象或者POOL_SENTINEL(哨兵对象,用于分隔autoreleasepool)。栈中指向POOL_SENTINEL的指针就是autoreleasepool的一个标记。当autoreleasepool进行出栈操作,每一个比这个哨兵对象后进栈的对象都会release。这个栈是由一个以page为节点双向链表组成,page根据需求进行增减。
autoreleasepool
对应的线程存储了指向最新page(也就是最新添加autorelease对象的page)的指针。
线程死锁的四个条件
一. 什么是死锁?
如果一个进程集合里面的每个进程都在等待这个集合中的其他一个进程(包括自身)才能继续往下执行,若无外力他们将无法推进,这种情况就是死锁,处于死锁状态的进程称为死锁进程
二. 死锁产生的原因?
1.因竞争资源发生死锁 现象:系统中供多个进程共享的资源的数目不足以满足全部进程的需要时,就会引起对诸资源的竞争而发生死锁现象
(1)可剥夺资源和不可剥夺资源:可剥夺资源是指某进程在获得该类资源时,该资源同样可以被其他进程或系统剥夺,不可剥夺资源是指当系统把该类资源分配给某个进程时,不能强制收回,只能在该进程使用完成后自动释放
(2)竞争不可剥夺资源:系统中不可剥夺资源的数目不足以满足诸进程运行的要求,则发生在运行进程中,不同的进程因争夺这些资源陷入僵局。
举例说明: 资源A,B;进程C,D
资源A,B都是不可剥夺资源:一个进程申请了之后,不能强制收回,只能进程结束之后自动释放。内存就是可剥夺资源
进程C申请了资源A,进程D申请了资源B。
接下来C的操作用到资源B,D的资源用到资源A。但是C,D都得不到接下来的资源,那么就引发了死锁。
(3)竞争临时资源
2.进程推进顺序不当发生死锁
三. 产生死锁的四个必要条件?
(1)互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
(2)请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
(3)不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
(4)环路等待条件:是指进程发生死锁后,必然存在一个进程--资源之间的环形链
四. 处理死锁的基本方法
1.预防死锁:通过设置一些限制条件,去破坏产生死锁的必要条件
2.避免死锁:在资源分配过程中,使用某种方法避免系统进入不安全的状态,从而避免发生死锁
3.检测死锁:允许死锁的发生,但是通过系统的检测之后,采取一些措施,将死锁清除掉
4.解除死锁:该方法与检测死锁配合使用
数据库索引的作用优点和缺点
https://blog.csdn.net/u013310119/article/details/52527632
为什么要创建索引呢?这是因为,创建索引可以大大提高系统的性能。第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。第二,可以大大加快 数据的检索速度,这也是创建索引的最主要的原因。第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。第四,在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
也许会有人要问:增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢?这种想法固然有其合理性,然而也有其片面性。虽然,索引有许多优点,但是,为表中的每一个列都增加索引,是非常不明智的。这是因为,增加索引也有许多不利的一个方面。
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。一般来说,应该在这些列上创建索引,例如:
在经常需要搜索的列上,可以加快搜索的速度; 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;在经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度;在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的; 在经常需要排序的列上创 建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
同样,对于有些列不应该创建索引。一般来说,不应该创建索引的的这些列具有下列特点:
第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因 为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。第二,对于那 些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。 第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。 第四,当修改性能远远大于检索性能时,不应该创建索 引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因 此,当修改性能远远大于检索性能时,不应该创建索引。
创建索引的方法和索引的特征创建索引的方法 创 建索引有多种方法,这些方法包括直接创建索引的方法和间接创建索引的方法。直接创建索引,例如使用CREATE INDEX语句或者使用创建索引向导,间接创建索引,例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。虽然,这两种方法都可以创建索引,但 是,它们创建索引的具体内容是有区别的。
HTTPS的工作原理:
http://www.chinaz.com/web/2017/0224/663236.shtml
①. 客户端将它所支持的算法列表和一个用作产生密钥的随机数发送给服务器;
②. 服务器从算法列表中选择一种加密算法,并将它和一份包含服务器公用密钥的证书发送给客户端;该证书还包含了用于认证目的的服务器标识,服务器同时还提供了一个用作产生密钥的随机数;
③. 客户端对服务器的证书进行验证(有关验证证书,可以参考数字签名),并抽取服务器的公用密钥;然后,再产生一个称作pre_master_secret的随机密码串,并使用服务器的公用密钥对其进行加密(参考非对称加/解密),并将加密后的信息发送给服务器;
④. 客户端与服务器端根据pre_master_secret以及客户端与服务器的随机数值独立计算出加密和MAC密钥(参考DH密钥交换算法)。
⑤. 客户端将所有握手消息的MAC值发送给服务器;
⑥. 服务器将所有握手消息的MAC值发送给客户端。
队列和线程的关系
多线程中的队列有:串行队列,并发队列,全局队列,主队列。
执行的方法有:同步执行和异步执行。那么两两一组合会有哪些注意事项呢?
提到多线程,也就是四种,pthread,NSthread,GCD,NSOperation
其中phtread是跨平台的。GCD和NSOperation都是常用的,后者是基于前者的。
但是两者区别:GCD的核心概念是将一个任务添加到队列,指定任务执行的方法,然后执行。NSOperation则是直接将一个操作添加到队列中。
iOS 中的各种锁
http://www.cocoachina.com/ios/20161129/18216.html
NSLock
NSLock实现了最基本的互斥锁,遵循了NSLocking 协议,通过lock 和unlock 来进行锁定和解锁。其使用也非常简单
1
2
3
4
5
- (void)doSomething {
[self.lock lock];
//TODO: do your stuff
[self.lock unlock];
}
由于是互斥锁,当一个线程进行访问的时候,该线程获得锁,其他线程进行访问的时候,将被操作系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而却确保了线程安全。但是如果连续锁定两次,则会造成死锁问题。那如果想在递归中使用锁,那要怎么办呢,这就用到了NSRecursiveLock递归锁。
NSRecursiveLock
递归锁,顾名思义,可以被一个线程多次获得,而不会引起死锁。它记录了成功获得锁的次数,每一次成功的获得锁,必须有一个配套的释放锁和其对应,这样才不会引起死锁。只有当所有的锁被释放之后,其他线程才可以获得锁
1
2
3
4
5
6
7
8
9
10
11
12
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
void MyRecursiveFunction(int value)
{
[theLock lock];
if (value != 0)
{
--value;
MyRecursiveFunction(value);
}
[theLock unlock];
}
MyRecursiveFunction(5);
NSCondition
NSCondition 是一种特殊类型的锁,通过它可以实现不同线程的调度。一个线程被某一个条件所阻塞,直到另一个线程满足该条件从而发送信号给该线程使得该线程可以正确的执行。比如说,你可以开启一个线程下载图片,一个线程处理图片。这样的话,需要处理图片的线程由于没有图片会阻塞,当下载线程下载完成之后,则满足了需要处理图片的线程的需求,这样可以给定一个信号,让处理图片的线程恢复运行。
- (void)download {
[self.condition lock];
//TODO: 下载文件代码
if (donloadFinish) { // 下载结束后,给另一个线程发送信号,唤起另一个处理程序
[self.condition signal];
[self.condition unlock];
}
}
- (void)doStuffWithDownloadPicture {
[self.condition lock];
while (!donloadFinish) {
[self.condition wait];
}
//TODO: 处理图片代码
[self.condition unlock];
}
NSConditionLock
NSConditionLock 对象所定义的互斥锁可以在使得在某个条件下进行锁定和解锁。它和NSCondition 很像,但实现方式是不同的。
当两个线程需要特定顺序执行的时候,例如生产者消费者模型,则可以使用NSConditionLock 。当生产者执行执行的时候,消费者可以通过特定的条件获得锁,当生产者完成执行的时候,它将解锁该锁,然后把锁的条件设置成唤醒消费者线程的条件。锁定和解锁的调用可以随意组合,lock和unlockWithCondition: 配合使用lockWhenCondition: 和unlock 配合使用。
- (void)producer {
while (YES) {
[self.conditionLock lock];
NSLog(@"have something");
self.count++;
[self.conditionLock unlockWithCondition:1];
}
}
- (void)consumer {
while (YES) {
[self.conditionLock lockWhenCondition:1];
NSLog(@"use something");
self.count--;
[self.conditionLock unlockWithCondition:0];
}
}
当生产者释放锁的时候,把条件设置成了1。这样消费者可以获得该锁,进而执行程序,如果消费者获得锁的条件和生产者释放锁时给定的条件不一致,则消费者永远无法获得锁,也不能执行程序。同样,如果消费者释放锁给定的条件和生产者获得锁给定的条件不一致的话,则生产者也无法获得锁,程序也不能执行。
pthread_mutex
POSIX 互斥锁是一种超级易用的互斥锁,使用的时候,只需要初始化一个pthread_mutex_t 用pthread_mutex_lock 来锁定pthread_mutex_unlock 来解锁,当使用完成后,记得调用pthread_mutex_destroy 来销毁锁。
1
2
3
4
5
pthread_mutex_init(&lock,NULL);
pthread_mutex_lock(&lock);
//do your stuff
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);
pthread_rwlock
读写锁,在对文件进行操作的时候,写操作是排他的,一旦有多个线程对同一个文件进行写操作,后果不可估量,但读是可以的,多个线程读取时没有问题的。
· 当读写锁被一个线程以读模式占用的时候,写操作的其他线程会被阻塞,读操作的其他线程还可以继续进行。
· 当读写锁被一个线程以写模式占用的时候,写操作的其他线程会被阻塞,读操作的其他线程也被阻塞。
· POSIX Conditions
· POSIX 条件锁需要互斥锁和条件两项来实现,虽然看起来没什么关系,但在运行时中,互斥锁将会与条件结合起来。线程将被一个互斥和条件结合的信号来唤醒。
· 首先初始化条件和互斥锁,当ready_to_go 为flase 的时候,进入循环,然后线程将会被挂起,直到另一个线程将ready_to_go 设置为true 的时候,并且发送信号的时候,该线程会才被唤醒。
OSSpinLock
自旋锁,和互斥锁类似,都是为了保证线程安全的锁。但二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得此锁的线程将会被阻塞,直到该锁被释放。但自选锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看是否该锁被释放。所以,此锁比较适用于锁的持有者保存时间较短的情况下。
1
2
3
4
5
6
// 初始化
spinLock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&spinLock);
// 解锁
OSSpinLockUnlock(&spinLock);
然而,YYKit 作者@ibireme 的文章也有说这个自旋锁存在优先级反转问题,具体文章可以戳 不再安全的OSSpinLock。
os_unfair_lock
自旋锁已经不在安全,然后苹果又整出来个os_unfair_lock_t (╯‵□′)╯︵┻━┻
这个锁解决了优先级反转问题。
1
2
3
4
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
dispatch_semaphore
信号量机制实现锁,等待信号,和发送信号,正如前边所说的看门人一样,当有多个线程进行访问的时候,只要有一个获得了信号,其他线程的就必须等待该信号释放。
1
2
3
4
5
- (void)semphone:(NSInteger)tag {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
// do your stuff
dispatch_semaphore_signal(semaphore);
}
@synchronized
一个便捷的创建互斥锁的方式,它做了其他互斥锁所做的所有的事情。
1
2
3
4
5
6
7
- (void)myMethod:(id)anObj
{
@synchronized(anObj)
{
// Everything between the braces is protected by the @synchronized directive.
}
}
如果你在不同的线程中传过去的是一样的标识符,先获得锁的会锁定代码块,另一个线程将被阻塞,如果传递的是不同的标识符,则不会造成线程阻塞。
• assign可以用于OC对象吗
assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,永远不会使用了,就不会产生野指针
• copy和strong的区别
说到底,其实就是不同的修饰符,对应不同的setter方法,
1. strong
对应的setter方法,是将_property先release(_property release),然后将参数retain(property retain),最后是_property = property。
2. copy
对应的setter方法,是将_property先release(_property release),然后拷贝参数内容(property copy),创建一块新的内存地址,最后_property = property。
objc_storeWeak函数以把obj的地址作为键值,obj_weak的地址作为值存放到weak表(weak是一个hash表)中。
释放对象时,废弃对象的同时,程序的动作是怎样的呢?对象通过objc_release释放。
1. objc_release
2. 因为引用计数为0所以执行dealloc
3. _objc_rootDealloc
4. object_dispose
5. objc_destructInstance
6. objc_clear_deallocating
而,调用objc_clear_deallocating的动作如下:
1. 从weak表中获取废弃对象的地址为键值的记录。
2. 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
3. 从weak表中删除记录
4. 从引用计数表中删除废弃对象的地址作为键值的记录
根据以上步骤,前面说的如果附有__weak修饰符的变量所引用的对象被废弃,则将nil赋值给这个变量,这个功能即被实现。
关于:1.指针与对象;2.深浅拷贝(复制);3.可变与不可变对象;4.copy与mutableCopy的一些理解
https://www.cnblogs.com/SoulKai/p/6289153.html
pod install与pod update的区别
https://blog.csdn.net/ShorewB/article/details/52207569
引言
许多使用cocoapod的人认为pod install只在第一次使用CocoaPod设置项目的时候使用,pod update是在设置完项目之后使用的,但事实并非如此。
这篇指南将会说明什么时候应该使用pod install,什么时候应该使用pod update。
如果你觉得这篇指南太过于冗长看不下去,那么先给出干货:
· 当你需要向向你的项目中安装新的pod库时使用pod install。即使之前你已经有一个Podfile并且执行了pod install,即使你是在向一个已经使用了CocoaPods的项目中添加或移除pod库。
· 只有当你想要更新pod库的版本时才使用pod update。
pod install一般是你第一次想要为项目添加pod的时候使用的,它同样也使用在你为Podfile文件添加或移除pod库的时候。
· 每次pod install命令运行的时候,pod install会为每一个它安装的pod库在Podfile.lock文件中写入其版本号。Podfile.lock文件追踪每一个安装的pod库的版本号,并锁定这些版本号。
· 当你运行pod install是,它将只解决不在Podfile.lock中的pod库依赖关系
· 对于在Podfile.lock文件中的pod库,pod install会只下载Podfile.lock文件中指定的版本,而不会去检查这个库是否有更新的版本。
· 对于不在Podfile.lock文件中的pod库,pod install会搜索这个pod库在Podfile文件中指定的版本
每当你运行pod outdated命令时,CocoaPods会列出所有在Podfile.lock中的有新版本的pod库。这意味着当你对这些pod使用pod update PODNAME时,他们会更新(只要新版本仍然遵守你在Podfile中做的类似于pod 'MyPod', '~>x.y'这样的限制)
当你运行了pod update PODNAME命令,CocoaPods会在不考虑Podfile.lock中版本的情况下试着去查找PODNAME的最新版本。pod update PODNAME命令会将相应的pod更新到最新的版本(新版本仍然遵守你在Podfile中做的限制)
setNeedsLayout和layoutIfNeeded的区别
setNeedsDisplay会调用自动调用drawRect方法
setNeedsLayout会默认调用layoutSubViews
Fiddler抓取https原理?
首先fiddler截获客户端浏览器发送给服务器的https请求, 此时还未建立握手。
第一步,fiddler向服务器发送请求进行握手, 获取到服务器的CA证书, 用根证书公钥进行解密, 验证服务器数据签名, 获取到服务器CA证书公钥。
第二步,fiddler伪造自己的CA证书, 冒充服务器证书传递给客户端浏览器, 客户端浏览器做跟fiddler一样的事。
第三步, 客户端浏览器生成https通信用的对称密钥, 用fiddler伪造的证书公钥加密后传递给服务器, 被fiddler截获。
第四步,fiddler将截获的密文用自己伪造证书的私钥解开, 获得https通信用的对称密钥。
第五步,fiddler将对称密钥用服务器证书公钥加密传递给服务器, 服务器用私钥解开后建立信任, 握手完成, 用对称密钥加密消息, 开始通信。
第六步,fiddler接收到服务器发送的密文, 用对称密钥解开, 获得服务器发送的明文。再次加密, 发送给客户端浏览器。
第七步, 客户端向服务器发送消息, 用对称密钥加密, 被fidller截获后, 解密获得明文。
由于fiddler一直拥有通信用对称密钥, 所以在整个https通信过程中信息对其透明。
hash方法与判等的关系?
https://blog.csdn.net/hx_lei/article/details/53885798
hash方法主要是用于在Hash Table查询成员用的, 那么和我们要讨论的isEqual()有什么关系呢?
为了优化判等的效率, 基于hash的NSSet和NSDictionary在判断成员是否相等时, 会这样做
· Step 1: 集成成员的hash值是否和目标hash值相等, 如果相同进入Step 2, 如果不等, 直接判断不相等
· Step 2: hash值相同(即Step 1)的情况下, 再进行对象判等, 作为判等的结果
简单地说就是
hash值是对象判等的必要非充分条件
https://blog.csdn.net/zh_98/article/details/78217400?locationNum=7&fps=1
Bitmap位图结构
BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息
◆ 位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
◆ 位图数据(bitmap data):图像数据区
need-to-insert-img
• 可变数组的实现原理
• https://www.jianshu.com/p/3c77756a86ab
•
使用环形缓冲区
互斥锁和自旋锁的区别
自旋锁(Spin lock)
自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是 否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。其作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远 高于互斥锁。虽然它的效率比互斥锁高,但是它也有些不足之处:
1
、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低。
2
、在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如copy_to_user()、copy_from_user()、kmalloc()等。
因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。
两种锁的加锁原理
互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。
互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞(blocking),Core0 会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而自旋锁则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。
两种锁的区别
互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。
两种锁的应用
互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑
1 临界区有IO操作
2 临界区代码复杂或者循环量大
3 临界区竞争非常激烈
4 单核处理器
至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。
iOS 集合的深复制与浅复制
· 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
· 深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
· 完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。
集合的浅复制(shallow copy)
集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。
集合的深复制(deep copy)
集合的深复制有两种方法。可以用initWithArray:copyItems: 将第二个参数设置为YES即可深复制,如
1. NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];
如果你用这种方法深复制,集合里的每个对象都会收到copyWithZone: 消息。如果集合里的对象遵循NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。
集合的单层深复制(one-level-deep copy)
如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝
• TCP为什么是三次握手和四次挥手
• 第一次a->b:b确认a的发信机和自己的收信机是没有问题的,可以正常发送和接受消息
• 第二次b->a:b回应,a收到了。这时a可以确认的是,自己和b的收发信机都是好的。此时b并不知道a是否收到回应,即不确定a的收信机以及自己的发信机是否完好第三次a->b:a对b的回应进行回应。这时a很清楚,双方收发信机都是好的,自己的这次回应b肯定能收到(正常情况下),这个回应的目的只是消除b对a的收信机和b自己的发信机的担心。然后,a不必等b的再次回应就可以正式发信了。
• 第三次 ACK 丢了没有太大问题。只要b后面接收到a的数据包过来,就可以确认连接已建好。如果a不发送别的数据包,那么b会超时重传第二次的握手信息。
•
• 让我们想一想,如果是两次的话,a发送请求,b应答并分配资源若b的应答没有到达a端,a认为连接未建立,而b认为建立了a会在一段时间内保留分配的资源如果大量a这样请求,b会崩溃。
• 至于为啥不是四次?既然三次已经足够了,为啥还要再来一次?简单而粗暴的理由~
• 2. 现在是关于四次挥手,在连接完成之后呢,需要关闭连接。
• 关闭连接的过程:
• 1)客户端发出段7,FIN位表示关闭连接的请求
• 2)服务器发出段8,应答客户端的关闭连接请求。
• 3)服务器发出段9,其中也包含FIN 位,向客户端发送关闭连接请求。
• 4)客户端发出段10,应答服务器的关闭请求
•
• 注意:服务器的应答和关闭连接请求通常不合 并在⼀一个段中,因为有连接半关闭的情况,这种情况下客户端关闭连接之后就不能再发送 数据给服 务器了,但是服务器还可以发送数据给客户端,直到服务器也关闭连接为⽌。
• http协议为什么不用UDP而用TCP呢?
• TCP发送连接请求不成功就重传,这样的话如果不超时总能保证连接请求被服务器接收,并且不会丢包保证传输无错误。UDP发后不管,可能存在服务器接收到的信息存在缺失或者错误。但也并不是不能用UDP,UDP有速度的优势
http为什么使用TCP?
1, 如果用UDP,网页源文件传输后不是会错误百出嘛,浏览器解析的时候不是疯掉了!!!
2
,udp链接不安全,不可靠,主要应用在不安全性要求不高,效率要求比较高的应用程序,比如聊天程序
http
要处理电子商务的应用。
3
,http协议只定义了应用层的东西,下层的可靠性要传输层来保证,但是没有说一定要用tcp,只要是可以保证可靠性传输层协议都可以承载http,比如有基于sctp的http实现。http也不是不能通过udp承载,在手机上就有人自己开发基于reliable udp的http协议,不过都是非标准的
TCP
和UDP的区别:现在Internet上流行的协议是TCP/IP协议,该协议中对低于1024的端口都有确切的定义,他们对应着Internet上一些常见的服务。这些常见的服务可以分为使用TCP端口(面向连接)和使用UDP端口(面向无连接)两种。说到TCP和UDP,首先要明白“连接”和“无连接”的含义,他们的关系可以用一个形象地比喻来说明,就是打电话和写信。两个人如果要通话,首先 要建立连接——即打电话时的拨号,等待响应后——即接听电话后,才能相互传递信息,最后还要断开连接——即挂电话。写信就比较简单了,填写好收信人的地址 后将信投入邮筒,收信人就可以收到了。从这个分析可以看出,建立连接可以在需要痛心地双方建立一个传递信息的通道,在发送方发送请求连接信息接收方响应 后,由于是在接受方响应后才开始传递信息,而且是在一个通道中传送,因此接受方能比较完整地收到发送方发出的信息,即信息传递的可靠性比较高。但也正因为 需要建立连接,使资源开销加大(在建立连接前必须等待接受方响应,传输信息过程中必须确认信息是否传到及断开连接时发出相应的信号等),独占一个通道,在 断开连接钱不能建立另一个连接,即两人在通话过程中第三方不能打入电话。而无连接是一开始就发送信息(严格说来,这是没有开始、结束的),只是一次性的传 递,是先不需要接受方的响应,因而在一定程度上也无法保证信息传递的可靠性了,就像写信一样,我们只是将信寄出去,却不能保证收信人一定可以收到。
TCP
是面向连接的,有比较高的可靠性,一些要求比较高的服务一般使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3等,而UDP是面向无连接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003以前是只使用UDP协议的,其服务器 使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口 常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的QQ版本中也支持使用TCP协议了。
https://www.cnblogs.com/hxc555/p/6506154.html
· Accept:浏览器能够处理的内容类型
· Accept-Charset:浏览器能够显示的字符集
· Accept-Encoding:浏览器能够处理的压缩编码
· Accept-Language:浏览器当前设置的语言
· Connection:浏览器与服务器之间连接的类型
· Cookie:当前页面设置的任何Cookie
· Host:发出请求的页面所在的域
· Referer:发出请求的页面的URL
· User-Agent:浏览器的用户代理字符串
HTTP响应头部信息:
· Date:表示消息发送的时间,时间的描述格式由rfc822定义
· server:服务器名字。
· Connection:浏览器与服务器之间连接的类型
· content-type:表示后面的文档属于什么MIME类型
· Cache-Control:控制HTTP缓存
拥塞控制
拥塞现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。这种现象跟公路网中经常所见的交通拥挤一样,当节假日公路网中车辆大量增加时,各种走向的车流相互干扰,使每辆车到达目的地的时间都相对增加(即延迟增加),甚至有时在某段公路上车辆因堵塞而无法开动(即发生局部死锁)。
造成拥塞的原因
(1)多条流入线路有分组到达,并需要同一输出线路,此时,如果路由器没有足够的内存来存放所有这些分组,那么有的分组就会丢失。
(2)路由器的慢带处理器的缘故,以至于难以完成必要的处理工作,如缓冲区排队、更新路由表等。
(1)在传输层可采用:重传策略、乱序缓存策略、确认策略、流控制策略和确定超时策略。
(2)在网络层可采用:子网内部的虚电路与数据报策略、分组排队和服务策略、分组丢弃策略、路由算法和分组生存管理。
(3)在数据链路层可采用:重传策略、乱序缓存策略、确认策略和流控制策略。
控制方法
缓冲区预分配法
分组丢弃法
定额控制法
OC对象的内存布局
一言以蔽之,属性(包括父类)都保存在对象本身的存储空间内;本类的实例方法保存在类对象中,本类的类方法保存在元类对象中;父类的实例方法保存在各级super class 中,父类的类方法保存在各级super meta class 中。
下图是对象的内存布局,isa 指向其类对象,其余空间保存各级的ivar:
need-to-insert-img
下图是类对象的内存布局(详细可看runtime.h 中对objc_class 的定义),isa 指向其元类,super_class指向其父类,此外还包含实例变量列表、方法列表、协议列表:
need-to-insert-img
ps: 实例变量的定义如下,它包含了变量的名称、类型、偏移等,但却不包括变量的值——值在对象而非类中:
structobjc_ivar { char*ivar_name OBJC2_UNAVAILABLE; char*ivar_type OBJC2_UNAVAILABLE; intivar_offset OBJC2_UNAVAILABLE; #ifdef __LP64__ intspace OBJC2_UNAVAILABLE; #endif}
多态
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
C++中,实现多态有以下方法:虚函数,抽象类,覆盖,模板(重载和多态无关)。
OC中的多态:不同对象对同一消息的不同响应.子类可以重写父类的方法
多态就是允许方法重名参数或返回值可以是父类型传入或返回。
作用
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
赋值之后,父类型的引用就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。
Ping 是什么协议
使用的是ICMP协议,是“Internet Control Message Protocol”(Internet控制消息协议)的缩写,是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。
MTU
MTU值的意思是网络上传送的最大数据包,单位是字节。不同的接入方式,MTU值是不一样的,如果值太大就会产生很多数据包碎片,增加丢包率,降低网络速度。平常使用的宽带PPPoE连接方式,其MTU值最大为1492,解决的办法就是在注册表中对MaxMTU值逐步调低,直到网络最正常为止。
最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等
TCP/IP报文头部结构整理
https://blog.csdn.net/ythunder/article/details/65664309
https://blog.csdn.net/mrwangwang/article/details/8537775
IP协议是TCP/IP协议族的动力,它为上层协议提供无状态、无连接、不可靠的服务。 优点:简单,高效。
IP头部信息: 头部长度:通常20字节,有选项时更长,总共不超过60字节。
IP
数据报长度:65535字节。
/*TCP头定义,共20个字节*/
typedef struct _TCP_HEADER
{
short m_sSourPort;
// 源端口号16bit
short m_sDestPort;
// 目的端口号16bit
unsigned int m_uiSequNum;
// 序列号32bit
unsigned int m_uiAcknowledgeNum; //
确认号32bit
short m_sHeaderLenAndFlag;
// 前4位:TCP头长度;中6位:保留;后6位:标志位
short m_sWindowSize;
// 窗口大小16bit
short m_sCheckSum;
// 检验和16bit
short m_surgentPointer;
// 紧急数据偏移量16bit
}__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
/*UDP头定义,共8个字节*/
typedef struct _UDP_HEADER
{
unsigned short m_usSourPort;
// 源端口号16bit
unsigned short m_usDestPort;
// 目的端口号16bit
unsigned short m_usLength;
// 数据包长度16bit
unsigned short m_usCheckSum;
// 校验和16bit
}__attribute__((packed))UDP_HEADER, *PUDP_HEADER;
寻找两个单链表中第一个相同的节点
https://blog.csdn.net/adcxf/article/details/2759555
1. 寻找连个链表中第一个相同的节点
2. * 思路:先求出两个链表的长度差diff,再将指向较长的链表的指针移动diff
3. * 个节点,然后两个链表同时移动,并判断,若指针相同,则找到,否则,任何
4. * 一个链表移动到链尾,则没有共同的节点。
5. 链表中有一个节点重合,之后的节点都重合
https://www.cnblogs.com/JohnTsai/p/5606719.html
1.使用数组
* 将字符串转换为char数组
* 遍历循环给char数组赋值
2.使用栈
我们都知道,栈有"后进先出(LIFO)"的特点。这一特点刚好用于反转字符串。
具体思路是:
· 将字符串转换为char数组
· 将char数组中的字符依次压入栈中
· 将栈中的字符依次弹出赋值给char数组
谈谈iOS 中图片的解压缩
http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/
图片加载的工作流
概括来说,从磁盘中加载一张图片,并将它显示到屏幕上,中间的主要工作流如下:
1. 假设我们使用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;
2. 然后将生成的 UIImage 赋值给 UIImageView ;
3. 接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;
4. 在主线程的下一个run loop 到来时,Core Animation 提交了这个隐式的transaction ,这个过程可能会对图片进行copy 操作,而受图片是否字节对齐等因素的影响,这个copy 操作可能会涉及以下部分或全部步骤:
a. 分配内存缓冲区用于管理文件IO 和解压缩操作;
b. 将文件数据从磁盘读到内存中;
c. 将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的CPU 操作;
d. 最后Core Animation 使用未压缩的位图数据渲染 UIImageView 的图层。
在上面的步骤中,我们提到了图片的解压缩是一个非常耗时的CPU 操作,并且它默认是在主线程中执行的。那么当需要加载的图片比较多时,就会对我们应用的响应性造成严重的影响,尤其是在快速滑动的列表上,这个问题会表现得更加突出。
解压缩后的图片大小与原始文件大小之间没有任何关系,而只与图片的像素有关
解压缩后的图片大小=图片的像素宽30*图片的像素高30*每个像素所占的字节数4
不管是JPEG 还是PNG 图片,都是一种压缩的位图图形格式。只不过PNG 图片是无损压缩,并且支持alpha 通道,而JPEG 图片则是有损压缩,可以指定0-100% 的压缩比。值得一提的是,在苹果的SDK 中专门提供了两个函数用来生成PNG 和JPEG 图片