面试题总结
1.swift struct class
相同点:
1.定义属性用于存储值 (property)
2.定义方法用于提供功能 (function)
3.定义下标操作使得可以通过下标语法来访问实例所包含的值 (subscript)
4.定义构造器用于生成初始化值 (initializers)
5.通过扩展以增加默认实现的功能 (extension)
6.实现协议以提供某种标准功能 (protocol)
不同点:
1.class是引用类型;struct是值类型
2.class支持继承;struct不支持继承
3.class声明的方法修改属性时不需要mutating关键字;struct需要
4.class中每一个成员变量都必须被初始化,否则编译器会报错,而struct不需要,编译器会自动帮我们生成init函数,给变量赋一个默认值
5.class支持引用计数(reference counting)(允许对一个类的多次引用),struct不支持
6.class支持type casting(类型转换)(允许在运行时检查和解释一个类实例的类型),struct不支持
7.class支持deinitializers(析构器)(允许一个类实例释放任何其所被分配的资源),struct不支持
8.变量赋值方式不同(深浅copy),class浅拷贝,struct深拷贝,class的赋值是传递引用,struct则是copy传值,不是使用引用计数。
9.内存管理:struct存储在stack中,class存储在heap中、
10.方法派发方式:struct的方法调用是静态绑定,而class的方法调用是动态实现
Tip: class的对象是引用类型,而struct是值类型。所以class的赋值是传递引用,则是copy传值,不是使用引用计数。
class为了支持的额外功能则会增加其复杂性。一般,更倾向使用选择struct和enum,因为他们更容易理解,而class,则在合适和必要的时候使用。实际上,这意味着大多数的自定义数据类型定义为struct和enum就可以
了。
class和struct的选择
struct实例总是通过值传递,class实例总是通过引用传递。这意味两者适用不同的任务。当你在考虑一个工程项目的数据结构和功能的时候,你需要决定每个数据结构是定义成struct还是class。
按照通用的准则,当符合一条或多条以下条件时,请考虑构建struct
:
该数据结构的主要目的是用来封装少量相关简单数据值。
有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用。
该数据结构中储存的值类型属性,也应该被拷贝,而不是被引用。
该数据结构不需要去继承另一个既有类型的属性或者行为。
总结起来就是一句话:能使用struct就不要使用class
为什么优选struct:
使用struct不需要考虑内存泄漏和多线程读写的问题,因为在传递值的时候它会进行值的copy
struct存储在stack中,class存储在heap中,struct更快
在运行时,struct在性能方面更优于class,原因是struct的方法调用是静态绑定,而class的方法调用是动态实现的。这就是尽可能得使用struct代替class的又一个好的原因。
2.为什么用struct比用class好
同上1
3.protocol的理解
一,作用
1,公开方法一般都放在.h文件中,如果想隐藏实现细节,可以把这些方法放到协议中,再让该类遵守此协议(一个协议对应一个类)
2,将一些公共方法抽取出来封装成协议,任何类想拥有这些方法,只需遵守此协议即可(一个协议对应多个类)
二,基本使用
1,修饰符
@required:遵守此协议的类必须实现它修饰的方法(默认修饰符)
@optional:遵守此协议的类可以不实现它修饰的方法
2,能声明属性,但必须在遵守此协议的类中调用@syntheszie才能正常使用
3,不能声明成员变量
三,特点
1,协议只有方法的声明,没有方法的实现
2,遵守协议只能在类的声明@interface上,不能在类的实现@implementation上
3,一个协议可以遵守多个其他协议
4,一个协议若遵守了其他协议,就拥有其他协议所有方法的声明
5,一个协议可以被多个类遵守,一个类可以遵守多个协议
6,一个类若遵守了某个协议,就必须实现协议中@required修饰的方法
7,若父类遵守了某个协议,子类也就遵守了此协议
四,应用场景
1,不同的类使用统一入口传递数据(一个协议对应多个类)
2,面向接口编程:将接口(声明)和实现分离,对外只暴露接口(一个协议对应一个类)
4.闭包 逃逸闭包 非逃逸闭包 作用域
5.oc把结构体放数组如何实现 为什么不能直接放
比较常见的结构体:CGPoint ,CGSize,CGRect。。。。。。我们如何存放到数组中呢?因为是结构体不是对象,不能添加到数组中,解决方法:把这些常见的结构装换成对象,让后放进去,取出来在装换成结构体使用。我们想到了NSValue使用方法如下
CGPoint point = CGPointMake(0, 0);
NSMutableArray *array = [[NSMutableArray alloc]initWithCapacity:0];
NSValue *value = [NSValue valueWithCGPoint:point];
[array addObject:value];
取出数组之后的对象的使用:
NSValue *tmpValue = array[0];
CGPoint tmpPoint = [tmpValue CGPointValue];
结构体可以存不同类型的元素,而数组只能存同一类型
结构体类型需要我们自已定义.数组是用别的类型加[元素个数]
结构体内存分配方式很特别,使用对齐原则,不一定是所有元素的字节数和,而数组一定是所有元素的字节数和.
结构体指针可以指针名->结构体元素名(取元素);数组不行.
6.http的理解
7.为什么三次握手四次挥手
TCP的三次握手
①首先 Client 端发送连接请求报文
②Server 段接受连接后回复 ACK 报文,并为这次连接分配资源。
③Client 端接收到 ACK 报文后也向 Server 段发生 ACK 报文,并分配资源,这样 TCP 连接就建立了。
用川航举例子
①四川8633请求建立连接(SYN),并且发送出序号。
②服务端接受到信号,即有确认号(ACK),此时并同样返回请求序号Seq
③客户端接受到信号,即有确认号(ACK),连接已经建立。
小结:三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack = Seq + 1是否成立,如果成立说明对方正确收到了自己的数据包。
如果只有两次握手 这个时候客户端没有回应,这样会浪费服务端的资源
那你是否思考过为什么需要第三次通信 ?
1、在第一次通信过程中,A向B发送信息之后,B收到信息后可以确认自己的收信能力和A的发信能力没有问题。
2、在第二次通信中,B向A发送信息之后,A可以确认自己的发信能力和B的收信能力没有问题,但是B不知道自己的发信能力到底如何,所以就需要第三次通信。
3、在第三次通信中,A向B发送信息之后,B就可以确认自己的发信能力没有问题。
4、 小结:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
四.TCP的四次挥手
第一次挥手:Clien发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。
第三次挥手: Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次握手。
川航图举例
①客户端申请断开连接即FIN (我这边准备断开连接了)
②服务端接收信息返回,表示我已经接收到 (收到,请稍等,我这边准备一下)
③服务端发送信息表示可以断开连接 (我准备好了,你可以断开连接了)
④客户端接受信息,同时返回信息通知服务端自己收到信息,开始断开 连接(好的,拜拜!)
为什么连接的时候是三次握手,关闭的时候却是四次握手?
①因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
②但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。
③只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
• TCP的三次握手一定能保证传输可靠吗?
• 不能
• 三次握手比两次更可靠,但也不是完全可靠,而追加更多次握手也不能使连接更可靠了。因此选择了三次握手。
• 世界上不存在完全可靠的通信协议。从通信时间成本空间成本以及可靠度来讲,选择了“三次握手”作为点对点通信的一般规则。
8.button大小不变,如何改变点击的区域
重写: -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;方法 例如: -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ //当前btn大小 CGRect btnBounds = self.bounds; //扩大点击区域,想缩小就将-10设为正值 btnBounds = CGRectInset(btnBounds, -10, -10); //若点击的点在新的bounds里,就返回YES return CGRectContainsPoint(btnBounds, point); }
9.runtime的机制 如何给类添加属性
static char * OtherNameKey = "OtherNameKey"; //运行时实现getter方法 - (NSString *)otherName { //如果属性值是非id类型,可以通过属性值先构造OC的id对象,再通过对象获取非id类型属性 return objc_getAssociatedObject(self, OtherNameKey); } //运行时实现setter方法 - (void)setOtherName:(NSString*)otherName{ objc_setAssociatedObject(self, &OtherNameKey, otherName, OBJC_ASSOCIATION_RETAIN);
10.runtime进行方法交换 黑魔法
//原来的方法 Method orginMethod = class_getInstanceMethod(self, @selector(reloadData)); //现在的方法 Method currentMethod = class_getInstanceMethod(self, @selector(currentReloadData)); //方法交换 method_exchangeImplementations(orginMethod, currentMethod);
11.算法题 n阶楼梯 一次一级或一次两级的上 多少种方法
12.snapkit的block为什么不用weak修饰
13.inout的使用
通过指针传递,在外部修改内部的值,达到某些功能或流程控制: class SkyInoutTool: NSObject { lg: class func foreachToArray(array: Array<Int>,operate: (_ idx: Int, _ item: Int, _ stop : inout Bool ) -> Void) { for index in 0...(array.count-1) { var isStop: Bool = false; operate(index,array[index],&isStop); if isStop { break; } } } } 使用: override func viewDidLoad() { super.viewDidLoad() SkyInoutTool.foreachToArray(array: [100,200,300,400,500]) { index, item, stop in print("index\(index) - - - item\(item)"); if index == 2 { stop = true; } } }
extension Array { func enumerateArray(operate: (_ idx: Int, _ item: Any, _ stop : inout Bool ) -> Void) { for index in 0...(self.count-1) { var isStop: Bool = false; operate(index,self[index],&isStop); if isStop { break; } } } }
14.响应事件链
15.xib storyboard连线的控件为什么用weak修饰
16.block 解决循环引用