主要的几个大模块
1、block
一、
Block变量的声明格式为: 返回值类型(^Block名字)(参数列表);
void(^aBlock)(NSString *x, NSString *y);声明
Block变量的赋值格式为: Block变量 = ^(参数列表){函数体};
aBlock = ^(NSString *x, NSString *y){ 赋值
NSLog(@"%@ love %@", x, y);
};
aBlock(@"Li Lei",@"Han Meimei"); 调用
二、
在Block中可以访问局部变量;在声明Block之后、调用Block之前对局部变量进行修改,在调用Block时局部变量值是修改之前的旧值;在Block中不可以直接修改局部变量。
__block修饰的局部变量 就可以
全局变量 就可以
static静态变量 就可以
三、
如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,解决循环引用的办法是使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样避免了Block对对象进行强引用
➤谈谈block使用时的注意点?
1、在block内部使用外部指针且会造成循环引用情况下,需要用__weak修饰外部指针__weak typeof(self) weakSelf = self;
2、在block内部如果调用了延时函数还使用弱指针会取不到该指针,因为已经被销毁了,需要在block内部再将弱指针重新强引用一下__strong typeof(self) strongSelf = weakSelf;
3、如果需要在block内部改变外部变量的话,需要在用__block修饰外部变量
➤在Block中如何修改Block外部变量的值
我们最常见的做法就是在需要修改的变量前加上 __block 修饰
*解释:很多时候,我们都直接这样去操作,但是为什么加上__block之后,就可以这样操作?
在内存中分区大致可分为:
栈:用于存放临时变量,处理速度快,但是因为栈内存极小,容易产生内存泄漏
堆:空间大,处理速度一般
只读数据段:用于存放常量(字面常量,符号常量),不能被修改
数据段:全部变量
代码段:代码,函数名等
通过上面的分区我们可以知道
变量声明出来存放在栈上面
而block,默认存放在NSGlobalBlock 全局的block;我们常常把block和C中的函数做对比,此时也类似,NSGlobalBlock类似于函数,存放在代码段
当block内部使用了外部的变量时,block的存放位置变成了NSMallockBlock(堆)
__block 修饰以后,会类似于桥接,将被修饰的变量被block所持有,此时该变量也转存到堆空间,所以此时Block内部就可以对外部的变量进行修改
2.运行时机制-Runtime
runtime库里面包含了跟类、成员变量、方法相关的API,比如获取类里面的所有成员变量,为类动态添加成员变量,动态改变类的方法实现,为类动态添加新的方法等 需要导入
1.是什么
1> runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
2> 平时编写的OC代码, 在程序运行过程中, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
3> 举例: OC : [[MJPerson alloc] init]
runtime : objc_msgSend(objc_msgSend("MJPerson" , "alloc"), "init")
2.用过么? 怎么用?
1> runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现) * 在程序运行过程中, 动态创建一个类(比如KVO的底层实现) * 在程序运行过程中, 动态地为某个类添加属性方法, 修改属性值方法 * 遍历一个类的所有成员变量(属性)所有方法
3.相关的头文件
1> 头文件
2> 相关应用
* NSCoding(归档和解档, 利用runtime遍历模型对象的所有属性)
* 字典 --> 模型 (利用runtime遍历模型对象的所有属性, 根据属性名从字典中取出对应的值, 设置到模型的属性上)
* KVO(利用runtime动态产生一个类)
* 用于封装框架(想怎么改就怎么改)
3> 相关函数
* objc_msgSend : 给对象发送消息
* class_copyMethodList : 遍历某个类所有的方法
* class_copyIvarList : 遍历某个类所有的成员变量
* class_.....
3.Runloop
RunLoop,是多线程的法宝,即一个线程一次只能执行一个任务,执行完任务后就会退出线程。主线程执行完即时任务时会继续等待接收事件而不退出。非主线程通常来说就是为了执行某一任务的,执行完毕就需要归还资源,因此默认是不运行RunLoop的;
每一个线程都有其对应的RunLoop,只是默认只有主线程的RunLoop是启动的,其它子线程的RunLoop默认是不启动的,若要启动则需要手动启动;
在一个单独的线程中,如果需要在处理完某个任务后不退出,继续等待接收事件,则需要启用RunLoop;
NSRunLoop提供了一个添加NSTimer的方法,可以指定Mode,如果要让任何情况下都回调,则需要设置Mode为Common模式;
实质上,对于子线程的runloop默认是不存在的,因为苹果采用了懒加载的方式。如果我们没有手动调用[NSRunLoop currentRunLoop]的话,就不会去查询是否存在当前线程的RunLoop,也就不会去加载,更不会创建。
4.多线程
NSThread:当需要进行一些耗时操作时会把耗时的操作放到线程中。线程同步:多个线程同时访问一个数据会出问题,NSlock、线程同步块、@synchronized(self){}。
NSOperationQueue操作队列(不需考虑线程同步问题)。编程的重点都放在main里面,NSInvocationOperation、BSBlockOperation、自定义Operation。创建一个操作绑定相应的方法,当把操作添加到操作队列中时,操作绑定的方法就会自动执行了,当把操作添加到操作队列中时,默认会调用main方法。
GCD(``Grand Central Dispatch`)宏大的中央调度,串行队列、并发队列、主线程队列;
同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。
串行和并行:串行是多个任务按一定顺序执行,并行是多个任务同时执行;
代码是在分线程执行,在主线程嘟列中刷新UI。
** 多线程编程是防止主线程堵塞、增加运行效率的最佳方法。**
Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法;
一个NSOperationQueue操作队列,相当于一个线程管理器,而非一个线程,因为你可以设置这个线程管理器内可以并行运行的线程数量等。
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。
iPhoneOS下的主线程的堆栈大小是1M。第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力。
➤线程与进程的区别和联系?
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比=多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
5.内存的使用和优化的注意事项
重用问题:如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:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存;
6.网络
➤TCP和UDP的区别于联系
TCP为传输控制层协议,为面向连接、可靠的、点到点的通信;
UDP为用户数据报协议,非连接的不可靠的点到多点的通信;
TCP侧重可靠传输,UDP侧重快速传输。
➤TCP连接的三次握手
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN+RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次状态。
➤Scoket连接和HTTP连接的区别:
HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。
Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。
➤HTTP协议的特点,关于HTTP请求GET和POST的区别
➤GET和POST的区别:
HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。
HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。
HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。
GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。
GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。 以?分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送, 如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.
GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。
传输数据的大小:
GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。
安全性:
POST的安全性要比GET的安全性高;
通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。
7.SDWebImage原理
调用类别的方法:
从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;
从沙盒中找,找到直接使用,缓存到内存。
从网络上获取,使用,缓存到内存,缓存到沙盒。