第一节
1.基本概念
01 进程
进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
02 线程
2-1 基本概念
1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
2-2 线程的串行
1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。
03 多线程
3-1 基本概念
即1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
3-2 线程的并行
并行即同时执行。比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C。
3-3 多线程并发执行的原理
在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
3-4 多线程优缺点
优点
1)能适当提高程序的执行效率。
2)能适当提高资源利用率(CPU、内存利用率)
缺点
1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
2)线程越多,CPU在调度线程上的开销就越大。
3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享
--------------------------
04 多线程在iOS开发中的应用
4-1 主线程
1)一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。
2)作用。刷新显示UI,处理UI事件。
4-2 使用注意
1)不要将耗时操作放到主线程中去处理,会卡住线程。
2)和UI相关的刷新操作必须放到主线程中进行处理。
--------------------------
05 iOS中多线程的实现方案
5-1 pthread
a.特点:
1)一套通用的多线程API
2)适用于Unix\Linux\Windows等系统
3)跨平台\可移植
4)使用难度大
b.使用语言:c语言
c.使用频率:几乎不用
d.线程生命周期:由程序员进行管理
5-2 NSThread
a.特点:
1)使用更加面向对象
2)简单易用,可直接操作线程对象
b.使用语言:OC语言
c.使用频率:偶尔使用
d.线程生命周期:由程序员进行管理
5-3 GCD
a.特点:
1)旨在替代NSThread等线程技术
2)充分利用设备的多核(自动)
b.使用语言:C语言
c.使用频率:经常使用
d.线程生命周期:自动管理
5-4 NSOperation
a.特点:
1)基于GCD(底层是GCD)
2)比GCD多了一些更简单实用的功能
3)使用更加面向对象
b.使用语言:OC语言
c.使用频率:经常使用
d.线程生命周期:自动管理
2.线程安全
01 前提:多个线程访问同一块资源会发生数据安全问题
02 解决方案:加互斥锁
03 相关代码:@synchronized(self){}
04 专业术语-线程同步
05 原子和非原子属性(是否对setter方法加锁)
3.线程间通信
-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
// [self download2];
//开启一条子线程来下载图片
[NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
-(void)downloadImage
{
//1.确定要下载网络图片的url地址,一个url唯一对应着网络上的一个资源
NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];
//2.根据url地址下载图片数据到本地(二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
//3.把下载到本地的二进制数据转换成图片
UIImage *image = [UIImage imageWithData:data];
//4.回到主线程刷新UI
//4.1 第一种方式
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//4.2 第二种方式
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//4.3 第三种方式
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
4.GCD
- GCD基本使用【重点】
01 异步函数+并发队列:开启多条线程,并发执行任务
02 异步函数+串行队列:开启一条线程,串行执行任务
03 同步函数+并发队列:不开线程,串行执行任务
04 同步函数+串行队列:不开线程,串行执行任务
05 异步函数+主队列:不开线程,在主线程中串行执行任务
06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
07 注意同步函数和异步函数在执行顺序上面的差异
- GCD的栅栏函数
在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)
第二节
1.单例模式
//提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static XMGTools *_instance;
//类方法,返回一个单例对象
+(instancetype)shareTools
{
//注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
return [[self alloc]init];
}
//保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代码
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加锁的方式,保证只分配一次存储空间
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
/*
1. mutableCopy 创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
2. copy 返回一个不可变对象。分两种情况:(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加 1 ;(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
*/
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
2.Opertion
(1)NSOperation中的两种队列
01 主队列 通过mainQueue获得,凡是放到主队列中的任务都将在主线程执行
02 非主队列 直接alloc init出来的队列。非主队列同时具备了并发和串行的功能,通过设置最大并发数属性来控制任务是并发执行还是串行执行
(2)设置最大并发数【控制任务并发和串行】
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.设置最大并发数
//注意点:该属性需要在任务添加到队列中之前进行设置
//该属性控制队列是串行执行还是并发执行
//如果最大并发数等于1,那么该队列是串行的,如果大于1那么是并行的
//系统的最大并发数有个默认的值,为-1,如果该属性设置为0,那么不会执行任何任务
queue.maxConcurrentOperationCount = 2;
(3)暂停和恢复以及取消
//设置暂停和恢复
//suspended设置为YES表示暂停,suspended设置为NO表示恢复
//暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的
if (self.queue.isSuspended) {
self.queue.suspended = NO;
}else
{
self.queue.suspended = YES;
}
//取消队列里面的所有操作
//取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样
//取消操作是不可以恢复的
[self.queue cancelAllOperations];
(4)自定义NSOperation取消操作
-(void)main
{
//耗时操作1
for (int i = 0; i<1000; i++) {
NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
//苹果官方建议,每当执行完一次耗时操作之后,就查看一下当前队列是否为取消状态,如果是,那么就直接退出
//好处是可以提高程序的性能
if (self.isCancelled) {
return;
}
//耗时操作2
for (int i = 0; i<1000; i++) {
NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
}
第三节
第三方框架SDWebImage
(1)SDWebImage基本使用
03 系统级内存警告如何处理(面试)
//取消当前正在进行的所有下载操作
[[SDWebImageManager sharedManager] cancelAll];
//清除缓存数据(面试)
//cleanDisk:删除过期的文件数据,计算当前未过期的已经下载的文件数据的大小,如果发现该数据大小大于我们设置的最大缓存数据大小,那么程序内部会按照按文件数据缓存的时间从远到近删除,知道小于最大缓存数据为止。
//clearMemory:直接删除文件,重新创建新的文件夹
//[[SDWebImageManager sharedManager].imageCache cleanDisk];
[[SDWebImageManager sharedManager].imageCache clearMemory];
04 SDWebImage默认的缓存时间是1周
(2)SDWebImage内部结构
[图片上传失败...(image-a9bcc0-1520218368724)]
Runloop基础知识
- Runloop对象
(1)在iOS开发中有两套api来访问Runloop
a.foundation框架【NSRunloop】
b.core foundation框架【CFRunloopRef】
(2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换
(3)NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
- Runloop参考资料
(1)苹果官方文档
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
(2)CFRunLoopRef开源代码下载地址:
http://opensource.apple.com/source/CF/CF-1151.16/
- Runloop与线程
1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程
问题:如何让子线程不死
回答:给这条子线程开启一个Runloop
2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁
注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
4.在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
5.Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。
- Runloop相关类
[图片上传失败...(image-bed3f1-1520218368724)]
- Runloop和相关类之间的关系图
[图片上传失败...(image-756f0d-1520218368724)]
CFRunloopModeRef
1.CFRunloopModeRef代表着Runloop的运行模式
2.一个Runloop中可以有多个mode,一个mode里面又可以有多个source\observer\timer等等
3.每次runloop启动的时候,只能指定一个mode,这个mode被称为该Runloop的当前mode
4.如果需要切换mode,只能先退出当前Runloop,再重新指定一个mode进入
5.这样做主要是为了分割不同组的定时器等,让他们相互之间不受影响
6.系统默认注册了5个mode
a.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
b.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
c.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
d.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
e.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
- CFRunloopSourceRef
1.是事件源也就是输入源,有两种分类模式;
一种是按照苹果官方文档进行划分的
另一种是基于函数的调用栈来进行划分的(source0和source1)。
2.具体的分类情况
(1)以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
(2)现在的分法
Source0:非基于Port的
Source1:基于Port的
3.可以通过打断点的方式查看一个方法的函数调用栈
- CFRunLoopObserverRef
/创建一个runloop监听者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"监听runloop状态改变---%zd",activity);
});
//为runloop添加一个监听者
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
- Runloop运行逻辑
[图片上传失败...(image-b83f47-1520218368724)]
- Runloop应用
常驻线程:在子线程中开启一个runloop
自动释放池
第一次创建:进入runloop的时候
最后一次释放:runloop退出的时候
其它创建和释放:当runloop即将休眠的时候会把之前的自动释放池释放,然后重新创建一个新的释放池
第四节
static
static关键字会在声明变量的时候分配内存,在程序运行期间只分配一次内存。之后再访问时,实际都是在访问原先分配的内存
如果使用static来修饰局部变量,那么局部变量在代码块结束后将不会回收,下次使用保持上次使用后的值。
如果使用static来修饰全局变量,那么表示该全局变量只在本文件中有效,外界无法使用extern来引用。static变量的作用域被限制在定义变量的当前文件中,其它文件是不能访问的
NSURLConnection使用
- 步骤
01 设置请求路径
02 创建请求对象(默认是GET请求,且已经默认包含了请求头)
03 使用NSURLSession sendsync方法发送网络请求
04 接收到服务器的响应后,解析响应体
JSON解析
(1)JSON的格式很像OC中的字典和数组
(2)标准JSON格式key必须是双引号
- JSON解析方案
a.第三方框架 JSONKit\SBJSON\TouchJSON
b.苹果原生(NSJSONSerialization)
- OC对象和JSON数据格式之间的一一对应关系
//OC对象和JSON数据之间的一一对应关系
-(void)oCWithJSON
{
//JSON的各种数据格式
//NSString *test = @"\"wendingding\"";
//NSString *test = @"true";
NSString *test = @"{\"name\":\"wendingding\"}";
//把JSON数据->OC对象,以便查看他们之间的一一对应关系
//注意点:如何被解析的JSON数据如果既不是字典也不是数组(比如是NSString), 那么就必须使用这NSJSONReadingAllowFragments
id obj = [NSJSONSerialization JSONObjectWithData:[test dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", [obj class]);
/* JSON数据格式和OC对象的一一对应关系
{} -> 字典
[] -> 数组
"" -> 字符串
10/10.1 -> NSNumber
true/false -> NSNumber
null -> NSNull
*/
}
}
XML解析
- XML解析
a.XML解析的两种方式
001 SAX:从根元素开始,按顺序一个元素一个元素的往下解析,可用于解析大、小文件
002 DOM:一次性将整个XML文档加载到内存中,适合较小的文件
b.解析XML的工具
001 苹果原生NSXMLParser:使用SAX方式解析,使用简单
002 第三方框架
libxml2:纯C语言的,默认包含在iOS SDK中,同时支持DOM和SAX的方式解析
GDataXML:采用DOM方式解析,该框架由Goole开发,是基于xml2的
- 使用GDataParser解析XML的步骤和方法
//4.0 配置环境
// 001 先导入框架,然后按照框架使用注释配置环境
// 002 GDataXML框架是MRC的,所以还需要告诉编译器以MRC的方式处理GDataXML的代码
//4.1 加载XML文档(使用的是DOM的方式一口气把整个XML文档都吞下)
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];
//4.2 获取XML文档的根元素,根据根元素取出XML中的每个子元素
NSArray * elements = [doc.rootElement elementsForName:@"video"];
//4.3 取出每个子元素的属性并转换为模型
for (GDataXMLElement *ele in elements) {
XMGVideo *video = [[XMGVideo alloc]init];
video.name = [ele attributeForName:@"name"].stringValue;
video.length = [ele attributeForName:@"length"].stringValue.integerValue;
video.url = [ele attributeForName:@"url"].stringValue;
video.image = [ele attributeForName:@"image"].stringValue;
video.ID = [ele attributeForName:@"id"].stringValue;
//4.4 把转换好的模型添加到tableView的数据源self.videos数组中
[self.videos addObject:video];
}
如何解决字典和数组中输出乱码的问题
给字典和数组添加一个分类,重写descriptionWithLocale方法,在该方法中拼接元素格式化输出。
-(nonnull NSString *)descriptionWithLocale:(nullable id)locale
第五节
大文件的下载
实现思路:边接收数据边写文件以解决内存越来越大的问题
有两种方法:一种是实用文件句柄下载NSFileHandle
另外一种是实用NSOutStream输出流下载
第一种
//当接收到服务器响应的时候调用,该方法只会调用一次
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//0.获得当前要下载文件的总大小(通过响应头得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
self.totalLength = res.expectedContentLength;
NSLog(@"%zd",self.totalLength);
//创建一个新的文件,用来当接收到服务器返回数据的时候往该文件中写入数据
//1.获取文件管理者
NSFileManager *manager = [NSFileManager defaultManager];
//2.拼接文件的全路径
//caches文件夹路径
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [caches stringByAppendingPathComponent:res.suggestedFilename];
self.fullPath = fullPath;
//3.创建一个空的文件
[manager createFileAtPath:fullPath contents:nil attributes:nil];
}
//当接收到服务器返回的数据时会调用
//该方法可能会被调用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//1.创建一个用来向文件中写数据的文件句柄
//注意当下载完成之后,该文件句柄需要关闭,调用closeFile方法
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
//2.设置写数据的位置(追加)
[handle seekToEndOfFile];
//3.写数据
[handle writeData:data];
//4.计算当前文件的下载进度
self.currentLength += data.length;
NSLog(@"%f",1.0* self.currentLength/self.totalLength);
self.progressView.progress = 1.0* self.currentLength/self.totalLength;
}
第二种
/*
第一个参数:二进制的流数据要写入到哪里
第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
*/
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];
//只要调用了该方法就会往文件中写数据
//如果文件不存在,那么会自动的创建一个
[stream open];
self.stream = stream;
//2.当接收到数据的时候写数据
//使用输出流写数据
/*
第一个参数:要写入的二进制数据
第二个参数:要写入的数据的大小
*/
[self.stream write:data.bytes maxLength:data.length];
//3.当文件下载完毕的时候关闭输出流
//关闭输出流
[self.stream close];
self.stream = nil;
大文件断点下载
实现思路:在下载文件的时候不再是整块的从头开始下载,而是看当前文件已经下载到哪个地方,然后从该地方接着往后面下载。可以通过在请求对象中设置请求头实现。
解决方法
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1 设置下载文件的某一部分
// 只要设置HTTP请求头的Range属性, 就可以实现从指定位置开始下载
/*
表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
*/
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
注意点
下载进度并判断是否需要重新创建文件
//获得当前要下载文件的总大小(通过响应头得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//注意点:res.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)
//因此:文件的总大小 == 本次要下载的文件大小+已经下载的文件的大小
self.totalLength = res.expectedContentLength + self.currentLength;
NSLog(@"----------------------------%zd",self.totalLength);
//0 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回
if (self.currentLength >0) {
return;
}
输出流
//1.创建一个数据输出流
/*
第一个参数:二进制的流数据要写入到哪里
第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
*/
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];
//只要调用了该方法就会往文件中写数据
//如果文件不存在,那么会自动的创建一个
[stream open];
self.stream = stream;
//2.当接收到数据的时候写数据
//使用输出流写数据
/*
第一个参数:要写入的二进制数据
第二个参数:要写入的数据的大小
*/
[self.stream write:data.bytes maxLength:data.length];
//3.当文件下载完毕的时候关闭输出流
//关闭输出流
[self.stream close];
self.stream = nil;
使用多线程下载文件思路
01 开启多条线程,每条线程都只下载文件的一部分(通过设置请求头中的Range来实现)
02 创建一个和需要下载文件大小一致的文件,判断当前是那个线程,根据当前的线程来判断下载的数据应该写入到文件中的哪个位置。(假设开5条线程来下载10M的文件,那么线程1下载0-2M,线程2下载2-4M一次类推,当接收到服务器返回的数据之后应该先判断当前线程是哪个线程,假如当前线程是线程2,那么在写数据的时候就从文件的2M位置开始写入)
03 代码相关:使用NSFileHandle这个类的seekToFileOfSet方法,来向文件中特定的位置写入数据。
04 技术相关
a.每个线程通过设置请求头下载文件中的某一个部分
b.通过NSFileHandle向文件中的指定位置写数据
文件的压缩和解压缩
- 使用ZipArchive来压缩和解压缩文件需要添加依赖库(libz),使用需要包含SSZipArchive文件
文件的上传
如何获得文件的MIMEType类型
- 直接对该对象发送一个异步网络请求,在响应头中通过response.MIMEType拿到文件的MIMEType类型
- (NSString *)getMIMEType
{
NSString *filePath = @"/Users/文顶顶/Desktop/备课/其它/swift.md";
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]] returningResponse:&response error:nil];
return response.MIMEType;
}
//对该文件发送一个异步请求,拿到文件的MIMEType
- (void)MIMEType
{
// NSString *file = @"file:///Users/文顶顶/Desktop/test.png";
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:@"/Users/文顶顶/Desktop/test.png"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
// response.MIMEType
NSLog(@"%@",response.MIMEType);
}];
}
NSURLConnection和Runloop
- 开子线程发送网络请求的注意点,适用于自动发送网络请求模式
//在子线程中发送网络请求-调用startf方法发送
-(void)createNewThreadSendConnect1
{
//1.创建一个非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封装操作,并把任务添加到队列中执行
[queue addOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
//2-1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];
//2-2.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2-3.使用NSURLConnection设置代理,发送网络请求
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
//2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行
[connection setDelegateQueue:[[NSOperationQueue alloc]init]];
//2-5.发送网络请求
//注意:start方法内部会把当前的connect对象作为一个source添加到当前线程对应的runloop中
//区别在于,如果调用start方法开发送网络请求,那么再添加source的过程中,如果当前runloop不存在
//那么该方法内部会自动创建一个当前线程对应的runloop,并启动。
[connection start];
}];
}
//在子线程中发送网络请求-自动发送网络请求
-(void)createNewThreadSendConnect2
{
NSLog(@"-----");
//1.创建一个非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封装操作,并把任务添加到队列中执行
[queue addOperationWithBlock:^{
//2-1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];
//2-2.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2-3.使用NSURLConnection设置代理,发送网络请求
//注意:该方法内部虽然会把connection添加到runloop,但是如果当前的runloop不存在,那么不会主动创建。
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
//2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行
[connection setDelegateQueue:[[NSOperationQueue alloc]init]];
//2-5 创建当前线程对应的runloop,并开启
[[NSRunLoop currentRunLoop]run];
}];
}
第六节
NSURLSession下载文件-代理
- 遵守代理协议,实现代理方法(3个相关的代理方法)
/*
1.当接收到服务器响应的时候调用
session:发送请求的session对象
dataTask:根据NSURLSession创建的task任务
response:服务器响应信息(响应头)
completionHandler:通过该block回调,告诉服务器端是否接收返回的数据
*/
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler
/*
2.当接收到服务器返回的数据时调用
该方法可能会被调用多次
*/
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data
/*
3.当请求完成之后调用该方法
不论是请求成功还是请求失败都调用该方法,如果请求失败,那么error对象有值,否则那么error对象为空
*/
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
- 当接收到服务器响应的时候,告诉服务器接收数据(调用block)
//默认情况下,当接收到服务器响应之后,服务器认为客户端不需要接收数据,所以后面的代理方法不会调用
//如果需要继续接收服务器返回的数据,那么需要调用block,并传入对应的策略
/*
NSURLSessionResponseCancel = 0, 取消任务
NSURLSessionResponseAllow = 1, 接收任务
NSURLSessionResponseBecomeDownload = 2, 转变成下载
NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 转变成流
*/
completionHandler(NSURLSessionResponseAllow);
NSURLSessionDownloadTask实现大文件下载
/*
1.当接收到下载数据的时候调用,可以在该方法中监听文件下载的进度
该方法会被调用多次
totalBytesWritten:已经写入到文件中的数据大小
totalBytesExpectedToWrite:目前文件的总大小
bytesWritten:本次下载的文件数据大小
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
/*
2.恢复下载的时候调用该方法
fileOffset:恢复之后,要从文件的什么地方开发下载
expectedTotalBytes:该文件数据的总大小
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/*
3.下载完成之后调用该方法
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
/*
4.请求完成之后调用
如果请求失败,那么error有值
*/
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
使用NSURLSessionDataTask实现大文件离线断点下载(完整)
- 关于网络请求请求头的设置(可以设置请求下载文件的某一部分)
//1. 设置请求对象
//1.1 创建请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//1.2 创建可变请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//1.3 拿到当前文件的残留数据大小
self.currentContentLength = [self FileSize];
//1.4 告诉服务器从哪个地方开始下载文件数据
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
NSLog(@"%@",range);
//1.5 设置请求头
[request setValue:range forHTTPHeaderField:@"Range"];
- NSURLSession对象的释放
-(void)dealloc
{
//在最后的时候应该把session释放,以免造成内存泄露
// NSURLSession设置过代理后,需要在最后(比如控制器销毁的时候)调用session的invalidateAndCancel或者resetWithCompletionHandler,才不会有内存泄露
// [self.session invalidateAndCancel];
[self.session resetWithCompletionHandler:^{
NSLog(@"释放---");
}];
}
关于NSURLSessionConfiguration相关
01 作用:可以统一配置NSURLSession,如请求超时等
02 创建的方式和使用
//创建配置的三种方式
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
//统一配置NSURLSession
-(NSURLSession *)session
{
if (_session == nil) {
//创建NSURLSessionConfiguration
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
//设置请求超时为10秒钟
config.timeoutIntervalForRequest = 10;
//在蜂窝网络情况下是否继续请求(上传或下载)
config.allowsCellularAccess = NO;
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
第七节
AFN框架基本使用
- NSURLSession
+ AFURLSessionManager
+ AFHTTPSessionManager(封装了常用的 HTTP 方法)
* GET
* POST
* UIKit + AFNetworking 分类
* NSProgress :利用KVO
- 半自动的序列化&反序列化的功能
+ AFURLRequestSerialization :请求的数据格式/默认是二进制的
+ AFURLResponseSerialization :响应的数据格式/默认是JSON格式
- 附加功能
+ 安全策略
* HTTPS
* AFSecurityPolicy
+ 网络检测
* 对苹果的网络连接检测做了一个封装
* AFNetworkReachabilityManager
使用AFN下载文件
-(void)download
{
//1.创建会话管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_13.png"]];
//3.创建下载Task
/*
第一个参数:请求对象
第二个参数:进度回调
downloadProgress.completedUnitCount :已经下载的数据
downloadProgress.totalUnitCount:数据的总大小
第三个参数:destination回调,该block需要返回值(NSURL类型),告诉系统应该把文件剪切到什么地方
targetPath:文件的临时保存路径
response:响应头信息
第四个参数:completionHandler请求完成后回调
response:响应头信息
filePath:文件的保存路径,即destination回调的返回值
error:错误信息
*/
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"%f",1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"%@\n%@",targetPath,fullPath);
return [NSURL fileURLWithPath:fullPath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(@"%@",filePath);
}];
//4.执行Task
[downloadTask resume];
}
AFN使用技巧
1.在开发的时候可以创建一个工具类,继承自我们的AFN中的请求管理者,再控制器中真正发请求的代码使用自己封装的工具类。
2.这样做的优点是以后如果修改了底层依赖的框架,那么我们修改这个工具类就可以了,而不用再一个一个的去修改。
3.该工具类一般提供一个单例方法,在该方法中会设置一个基本的请求路径。
4.该方法通常还会提供对GET或POST请求的封装。
5.在外面的时候通过该工具类来发送请求
6.单例方法:
+ (instancetype)shareNetworkTools
{
static XMGNetworkTools *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 注意: BaseURL中一定要以/结尾
instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://120.25.226.186:32812/"]];
});
return instance;
}
AFN文件上传
1.文件上传拼接数据的第一种方式
[formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];
2.文件上传拼接数据的第二种方式
[formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];
3.文件上传拼接数据的第三种方式
[formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
4.【注】在资料中已经提供了一个用于文件上传的分类。
/*文件上传相关的代码如下*/
-(void)upload1
{
//1.创建会话管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.处理参数(非文件参数)
NSDictionary *dict = @{
@"username":@"123"
};
//3.发送请求上传文件
/*
第一个参数:请求路径(NSString类型)
第二个参数:非文件参数,以字典的方式传递
第三个参数:constructingBodyWithBlock 在该回调中拼接文件参数
第四个参数:progress 进度回调
uploadProgress.completedUnitCount:已经上传的数据大小
uploadProgress.totalUnitCount:数据的总大小
第五个参数:success 请求成功的回调
task:上传Task
responseObject:服务器返回的响应体信息(已经以JSON的方式转换为OC对象)
第六个参数:failure 请求失败的回调
task:上传Task
error:错误信息
*/
[manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
UIImage *image = [UIImage imageNamed:@"Snip20160117_1"];
NSData *imageData = UIImagePNGRepresentation(image);
//在该block中拼接要上传的文件参数
/*
第一个参数:要上传的文件二进制数据
第二个参数:文件参数对应的参数名称,此处为file是该台服务器规定的(通常会在接口文档中提供)
第三个参数:该文件上传到服务后以什么名称保存
第四个参数:该文件的MIMeType类型
*/
[formData appendPartWithFileData:imageData name:@"file" fileName:@"123.png" mimeType:@"image/png"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(@"%f",1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"请求成功----%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败----%@",error);
}];
}
-(void)upload2
{
//1.创建会话管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.处理参数(非文件参数)
NSDictionary *dict = @{
@"username":@"123"
};
//3.发送请求上传文件
/*
第一个参数:请求路径(NSString类型)
第二个参数:非文件参数,以字典的方式传递
第三个参数:constructingBodyWithBlock 在该回调中拼接文件参数
第四个参数:progress 进度回调
uploadProgress.completedUnitCount:已经上传的数据大小
uploadProgress.totalUnitCount:数据的总大小
第五个参数:success 请求成功的回调
task:上传Task
responseObject:服务器返回的响应体信息(已经以JSON的方式转换为OC对象)
第六个参数:failure 请求失败的回调
task:上传Task
error:错误信息
*/
[manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSURL *fileUrl = [NSURL fileURLWithPath:@"/Users/文顶顶/Desktop/Snip20160117_1.png"];
//在该block中拼接要上传的文件参数
//第一种拼接方法
/*
第一个参数:要上传的文件的URL路径
第二个参数:文件参数对应的参数名称,此处为file是该台服务器规定的(通常会在接口文档中提供)
第三个参数:该文件上传到服务后以什么名称保存
第四个参数:该文件的MIMeType类型
第五个参数:错误信息,传地址
*/
//[formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"1234.png" mimeType:@"image/png" error:nil];
//第二种拼接方法:简写方法
/*
第一个参数:要上传的文件的URL路径
第二个参数:文件参数对应的参数名称,此处为file
第三个参数:错误信息
说明:AFN内部自动获得路径URL地址的最后一个节点作为文件的名称,内部调用C语言的API获得文件的类型
*/
[formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(@"%f",1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"请求成功----%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败----%@",error);
}];
}
使用AFN进行序列化处理
1.AFN它内部默认把服务器响应的数据当做json来进行解析,所以如果服务器返回给我的不是JSON数据那么请求报错,这个时候需要设置AFN对响应信息的解析方式。AFN提供了三种解析响应信息的方式,分别是:
1)AFXMLParserResponseSerializer----XML
2) AFHTTPResponseSerializer---------默认二进制响应数据
3)AFJSONResponseSerializer---------JSON
2.还有一种情况就是服务器返回给我们的数据格式不太一致(开发者工具Content-Type:text/xml),那么这种情况也有可能请求不成功。解决方法:
1) 直接在源代码中修改,添加相应的Content-Type
2) 拿到这个属性,添加到它的集合中
使用AFN来检测网络状态
说明:可以使用AFN框架中的AFNetworkReachabilityManager来监听网络状态的改变,也可以利用苹果提供的Reachability来监听。建议在开发中直接使用AFN框架处理。
*/
//使用AFN框架来检测网络状态的改变
-(void)AFNReachability
{
//1.创建网络监听管理者
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
//2.监听网络状态的改变
/*
AFNetworkReachabilityStatusUnknown = 未知
AFNetworkReachabilityStatusNotReachable = 没有网络
AFNetworkReachabilityStatusReachableViaWWAN = 3G
AFNetworkReachabilityStatusReachableViaWiFi = WIFI
*/
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"没有网络");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"3G");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WIFI");
break;
default:
break;
}
}];
//3.开始监听
[manager startMonitoring];
}
数据安全
01 攻城利器:Charles(公司中一般都使用该工具来抓包,并做网络测试)
注意:Charles在使用中的乱码问题,可以显示包内容,然后打开info.plist文件,找到java目录下面的VMOptions,在后面添加一项:-Dfile.encoding=UTF-8
02 MD5消息摘要算法是不可逆的。
03 数据加密的方式和规范一般公司会有具体的规定,不必多花时间。
HTTPS的基本使用
- 如果是自己使用NSURLSession来封装网络请求,涉及代码如下。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[task resume];
}
只要请求的地址是HTTPS的, 就会调用这个代理方法
我们需要在该方法中告诉系统, 是否信任服务器返回的证书
Challenge: 挑战 质问 (包含了受保护的区域)
protectionSpace : 受保护区域
NSURLAuthenticationMethodServerTrust : 证书的类型是 服务器信任
*/
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
// NSLog(@"didReceiveChallenge %@", challenge.protectionSpace);
NSLog(@"调用了最外层");
// 1.判断服务器返回的证书类型, 是否是服务器信任
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"调用了里面这一层是服务器信任的证书");
/*
NSURLSessionAuthChallengeUseCredential = 0, 使用证书
NSURLSessionAuthChallengePerformDefaultHandling = 1, 忽略证书(默认的处理方式)
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, 忽略书证, 并取消这次请求
NSURLSessionAuthChallengeRejectProtectionSpace = 3, 拒绝当前这一次, 下一次再询问
*/
// NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential , card);
}
}
- 如果是使用AFN框架,那么我们不需要做任何额外的操作,AFN内部已经做了处理。
Cocoapods的安装
1.先升级Gem
sudo gem update --system
2.切换cocoapods的数据源
【先删除,再添加,查看】
gem sources --remove https://rubygems.org/
gem sources -a https://ruby.taobao.org/
gem sources -l
3.安装cocoapods
sudo gem install cocoapods
或者(如10.11系统)sudo gem install -n /usr/local/bin cocoapods
4.将Podspec文件托管地址从github切换到国内的oschina
【先删除,再添加,再更新】
pod repo remove master
pod repo add master http://git.oschina.net/akuandev/Specs.git
pod repo add master https://gitcafe.com/akuandev/Specs.git
pod repo update
5.设置pod仓库
pod setup
6.测试
【如果有版本号,则说明已经安装成功】
pod --version
7.利用cocoapods来安装第三方框架
01 进入要安装框架的项目的.xcodeproj同级文件夹
02 在该文件夹中新建一个文件podfile
03 在文件中告诉cocoapods需要安装的框架信息
a.该框架支持的平台
b.适用的iOS版本
c.框架的名称
d.框架的版本
8.安装
pod install --no-repo-update
pod update --no-repo-update
9.说明
platform :ios, '8.0' 用来设置所有第三方库所支持的iOS最低版本
pod 'SDWebImage','~>2.6' 设置框架的名称和版本号
版本号的规则:
'>1.0' 可以安装任何高于1.0的版本
'>=1.0' 可以安装任何高于或等于1.0的版本
'<1.0' 任何低于1.0的版本
'<=1.0' 任何低于或等于1.0的版本
'~>0.1' 任何高于或等于0.1的版本,但是不包含高于1.0的版本
'~>0' 任何版本,相当于不指定版本,默认采用最新版本号
10.使用pod install命令安装框架后的大致过程:
01 分析依赖:该步骤会分析Podfile,查看不同类库之间的依赖情况。如果有多个类库依赖于同一个类库,但是依赖于不同的版本,那么cocoaPods会自动设置一个兼容的版本。
02 下载依赖:根据分析依赖的结果,下载指定版本的类库到本地项目中。
03 生成Pods项目:创建一个Pods项目专门用来编译和管理第三方框架,CocoaPods会将所需的框架,库等内容添加到项目中,并且进行相应的配置。
04 整合Pods项目:将Pods和项目整合到一个工作空间中,并且设置文件链接。
注意:遇到问题可以参考https://www.jianshu.com/p/2f61a90f49f3
Base64补充
1.Base64简单说明
描述:Base64可以成为密码学的基石,非常重要。
特点:可以将任意的二进制数据进行Base64编码
结果:所有的数据都能被编码为并只用65个字符就能表示的文本文件。
65字符:A~Z a~z 0~9 + / =
对文件进行base64编码后文件数据的变化:编码后的数据~=编码前数据的4/3,会大1/3左右。
2.命令行进行Base64编码和解码
编码:base64 123.png -o 123.txt
解码:base64 123.txt -o test.png -D
3.终端测试命令
$ echo -n A | base64
$ echo -n QQ== |base64 -D
加密相关
网络应用程序数据的原则:
1. 在网络上"不允许"传输用户隐私数据的"明文"
2. 在本地"不允许"保存用户隐私数据的"明文"
加密相关
1. base64 编码格式
2. 密码学演化 "秘密本"-->RSA
RSA简单说明:加密算法算法是公开的,加密方式如下:
- "公钥"加密,"私钥"解密
- "私钥"加密,"公钥"解密
目前流行的加密方式:
---------------
- 哈希(散列)函数
- MD5
- SHA1
- SHA256
- 对称加密算法
- DES
- 3DES
- AES(高级密码标准,美国国家安全局使用的)
- 非对称加密算法(RSA)
散列函数:
---------------
特点:
- 算法是公开的
- "对相同的数据加密,得到的结果是一样的"
- 对不同的数据加密,得到的结果是定长的,MD5对不同的数据进行加密,得到的结果都是 32 个字符长度的字符串
- 信息摘要,信息"指纹",是用来做数据识别的!
- 不能反算的
用途:
- 密码,服务器并不需要知道用户真实的密码!
- 搜索
张老师 杨老师 苍老师
苍老师 张老师 杨老师
张老师 1bdf605991920db11cbdf8508204c4eb
杨老师 2d97fbce49977313c2aae15ea77fec0f
苍老师 692e92669c0ca340eff4fdcef32896ee
如何判断:对搜索的每个关键字进行三列,得到三个相对应的结果,按位相加结果如果是一样的,那搜索的内容就是一样的!
- 版权
版权保护,文件的识别。
破解:
- http://www.cmd5.com 记录超过24万亿条,共占用160T硬盘 的密码数据,通过对海量数据的搜索得到的结果!
提升MD5加密安全性,有两个解决办法
1. 加"盐"(佐料)
2. HMAC:给定一个"秘钥",对明文进行加密,并且做"两次散列"!-> 得到的结果,还是 32 个字符
网友评论