IOS高级开发之多线程(四)NSOperation
1.什么是NSOperation,NSOperationQueue?
NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作。系统已经给我们封装了NSBlckOperation和NSInvocationOperation两个实体类。使用起来也非常简单,不过我们更多的使用是自己继承并定制自己的操作。
2.我们为什么使用NSOperation?
在IOS的开发中,为了照顾用户体验,我们通常将那些耗时的操作的放到主线程以为的线程去处理。对于一些简单的操作,我们可以使用代码更为简洁的GCD来进行处理。NSOperation也是基于GCD来实现,不过相对于GCD来说,可操控性更强,而且可以加入操作依赖。
1.可添加完成的代码块,在操作完成后执行。
2.添加操作之间的依赖关系,方便的控制执行顺序。
3.设定操作执行的优先级。
4.可以很方便的取消一个操作的执行。
5.使用KVO观察对操作状态进行的更改,isExcuting,isFinished,isCancelld.
NSOperation:执行操作的意思,就是需要在线程中执行的那段代码。
在GCD中,我们是放到block中去执行的,而在NSOperaion中,我们是使用NSOpration的子类NSInvocationOperation,NSBlokOperation,或者自定义子类来进行封装。
OperationQueue:操作队列,不同于GCD的先进先出,对于进入队列中的操作,首先进入准备就绪状态,不过这个取决于各个操作之间的依赖关系,然后进入就绪状态的操作的执行顺序,是由操作之间的相对优先级决定的。
操作队列通过设置最大并发数来控制并发,串行。
NSOperationQueue为我们提供了两种队列:主队列(运行在主线程),自定义队列(运行在后台)。
有关NSOperation、NSOperationQueue的使用:
NSOperation需要配合NSOperationQueue来实现多线程操作。默认情况下,单独使用NSOPeration,系统同步执行操作。配合NSOperationQueue来实现异步操作。
NSOperation的使用一般分为三个步骤:
1.创建操作:将需要执行的操作,封装到一个NSOperation中去。
2.创建队列:创建NSOperationQueue对象。
3.将操作加入到队列中去:将NSOperation对象加入到NSOperationQueue队列中去。
在之后,系统会自动将NSOpeationQueue中的NSOperation对象取出来,在新线程中执行。
我们来具体看下这三步是如何操作的:
先来看第一步,创建操作:
NSOperation是一个抽象类,所以我们要使用他的子类来进行封装操作。
有这么几个方式:
1.使用NSInvocationOperation
2.使用NSBlockOperation
3.自定义继承NSOpeation的子类,通过实现内部相应的方法来封装操作。
如果不配合NSOperationQueue单独使用NSOperation,系统同步执行操作。
我们来分别看下代码:
/*
使用子类NSInvocationOperation
*/
-(void)useInvocationOperation{
//1.创建NSInvocationOperation对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
//2.调用start方法执行操作
[op start];
} -(void)task1{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"------%@",[NSThread currentThread]);
}
打印:
2019-04-08 17:18:36.020622+0800 MyNSOperation[1118:14940] ------<NSThread: 0x600000fcf900>{number = 1, name = main}
可以看出,操作在当前线程中进行,并没有开启新线程。
但是,如果在其他线程中执行操作,打印则为其他线程:
[NSThread detachNewThreadSelector:@selector(useInvocationOperation) toTarget:self withObject:nil];
打印:
2019-04-08 17:25:38.360812+0800 MyNSOperation[1379:19754] ------<NSThread: 0x6000003cd9c0>{number = 3, name = (null)}
接下来,再来看NSBlockOperation:
/**
使用子类NSBlockOperation
*/
-(void)useNSBlockOperation{
//1.创建NSBlockOperation对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"----%@",[NSThread currentThread]);
}
}]; //2.调用start方法执行操作
[op start];
}
打印结果:
2019-04-08 17:32:50.691864+0800 MyNSOperation[1669:25050] ----<NSThread: 0x600002f9f0c0>{number = 1, name = main}
2019-04-08 17:32:52.692550+0800 MyNSOperation[1669:25050] ----<NSThread: 0x600002f9f0c0>{number = 1, name = main}
可以看到NSBlock执行一个操作,是在当前线程中执行的,并没有开启一个新的线程。
不过也和NSInovationOperation一样,如果在其他线程中执行,则打印结果为其他线程。
不过NSBlockOperation还提供了一个方法:NSExecutionBlock:,通过这个方法就可以为NSExecutionBlock添加额外的操作。如果添加的操作多的话,blockOperationWithBlock:也可能会在其他线程中执行,这个是由系统决定的。来看下代码:
/**
使用NSBlockOperation
调用AddExecutionBlock;
*/
-(void)useBlockOperationAddExecutionBlock{
//1.创建NSBlockOperation对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"1-----%@",[NSThread currentThread]);
}
}];
//2.添加额外的操作
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"2-----%@",[NSThread currentThread]);
}
}]; [op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"3-----%@",[NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"4-----%@",[NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"5-----%@",[NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"6-----%@",[NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"7-----%@",[NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"8-----%@",[NSThread currentThread]);
}
}];
[op start];
}
打印结果:
2019-04-08 18:43:48.694212+0800 MyNSOperation[3981:56480] 1-----<NSThread: 0x6000003e6840>{number = 1, name = main}
2019-04-08 18:43:48.694212+0800 MyNSOperation[3981:56536] 3-----<NSThread: 0x600000382e00>{number = 5, name = (null)}
2019-04-08 18:43:48.694216+0800 MyNSOperation[3981:56538] 2-----<NSThread: 0x600000382d40>{number = 4, name = (null)}
2019-04-08 18:43:48.694235+0800 MyNSOperation[3981:56537] 4-----<NSThread: 0x6000003bd2c0>{number = 3, name = (null)}
2019-04-08 18:43:50.695591+0800 MyNSOperation[3981:56480] 1-----<NSThread: 0x6000003e6840>{number = 1, name = main}
2019-04-08 18:43:50.695591+0800 MyNSOperation[3981:56537] 4-----<NSThread: 0x6000003bd2c0>{number = 3, name = (null)}
2019-04-08 18:43:50.695592+0800 MyNSOperation[3981:56536] 3-----<NSThread: 0x600000382e00>{number = 5, name = (null)}
2019-04-08 18:43:50.695639+0800 MyNSOperation[3981:56538] 2-----<NSThread: 0x600000382d40>{number = 4, name = (null)}
2019-04-08 18:43:52.697095+0800 MyNSOperation[3981:56480] 7-----<NSThread: 0x6000003e6840>{number = 1, name = main}
2019-04-08 18:43:52.697095+0800 MyNSOperation[3981:56536] 6-----<NSThread: 0x600000382e00>{number = 5, name = (null)}
2019-04-08 18:43:52.697095+0800 MyNSOperation[3981:56537] 5-----<NSThread: 0x6000003bd2c0>{number = 3, name = (null)}
2019-04-08 18:43:52.697178+0800 MyNSOperation[3981:56538] 8-----<NSThread: 0x600000382d40>{number = 4, name = (null)}
2019-04-08 18:43:54.698766+0800 MyNSOperation[3981:56537] 5-----<NSThread: 0x6000003bd2c0>{number = 3, name = (null)}
2019-04-08 18:43:54.698777+0800 MyNSOperation[3981:56480] 7-----<NSThread: 0x6000003e6840>{number = 1, name = main}
2019-04-08 18:43:54.698777+0800 MyNSOperation[3981:56536] 6-----<NSThread: 0x600000382e00>{number = 5, name = (null)}
2019-04-08 18:43:54.698847+0800 MyNSOperation[3981:56538] 8-----<NSThread: 0x600000382d40>{number = 4, name = (null)}
可以看出,在添加了多个AddExcutionBlock操作之后,操作在不同的线程中异步执行了。这个是由系统决定的。
一般情况下,如果一个NSBlockOperation封装了多个操作,NSBlockOperation是否开启新线程,取决于操作的个数,如果添加的操作个数多,就会开启新线程。当然开启的新的线程数量也是由系统决定的。
不过我们经常遇到使用NSInvocationOperation以及NSBlockOperation无法满足的情况,这个时候我们就需要自定义NSOpration的子类啦!
可以通过重写main方法或者star方法来定义自己的NSOperaion对象。
我们先来看重写main方法:
新建一个类继承自NSOperation:
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface TestOperation : NSOperation @end NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface TestOperation : NSOperation @end NS_ASSUME_NONNULL_END
在使用的地方:
//使用自定义的NSOperation
-(void)useCustomOperaion{
//1.创建TestOperation对象
TestOperation *op = [[TestOperation alloc] init];
//2.调用start方法执行操作
[op start];
}
来看打印:
2019-04-08 19:04:03.931693+0800 MyNSOperation[4724:68006] 1-----<NSThread: 0x600001d113c0>{number = 1, name = main}
2019-04-08 19:04:05.933372+0800 MyNSOperation[4724:68006] 1-----<NSThread: 0x600001d113c0>{number = 1, name = main}
可以看出,在单独使用NSOperaion的时候,是在当前线程里的,接着我们看下使用NSOperaionQueue:
NSOperation主要包括两种队列:主队列和自定义队列。
凡是添加到主队列中的操作都会放到当前线程中执行。(不包括前面提到的addExcutionBlock添加的额外操作);
主队列的获取方法:
NSOperationQueue *[mainQueue = [NSOperationQueue mainQueue];
自定义队列:
添加到自定义队列中的操作就会放到子线程中去执行。同时包含了串行,并发的功能。
主队列是获取,而自定义队列则需要自己创建了:
//自定义队列的创建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
有了队列,把操作加到队列中就OK了,我们来看下代码:
//把操作添加到队列中去:
-(void)addOperationToQueue{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
//使用NSBlockOperation创建操作3
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"3--%@",[NSThread currentThread]);
} }];
[op3 addExecutionBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"4----%@",[NSThread currentThread]);
}
}];
//3.将操作都添加到队列中去
[queue addOperation:op1];//[op1 start]
[queue addOperation:op2];
[queue addOperation:op3];
}
来看打印:
2019-04-08 19:26:06.403243+0800 MyNSOperation[5573:82196] ------<NSThread: 0x6000028a0b80>{number = 3, name = (null)}
2019-04-08 19:26:06.403369+0800 MyNSOperation[5573:82195] 4----<NSThread: 0x6000028a7bc0>{number = 5, name = (null)}
2019-04-08 19:26:06.403372+0800 MyNSOperation[5573:82198] 3--<NSThread: 0x6000028a0bc0>{number = 4, name = (null)}
2019-04-08 19:26:08.406961+0800 MyNSOperation[5573:82198] 3--<NSThread: 0x6000028a0bc0>{number = 4, name = (null)}
2019-04-08 19:26:08.406952+0800 MyNSOperation[5573:82195] 4----<NSThread: 0x6000028a7bc0>{number = 5, name = (null)}
可以看到,加入到了不同的子线程中进行了异步执行。
再来看一种无需创建操作,直接写在block中的方法,使用NSOPerationWithBlock:
-(void)addOperationToQueueWithBlock{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.使用addOperaionWithBlock添加操作到队列中
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:1.0];
NSLog(@"1----%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2----%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:1.0];
NSLog(@"3----%@",[NSThread currentThread]);
}
打印结果:
2019-04-08 19:41:56.635903+0800 MyNSOperation[6125:93195] 4----<NSThread: 0x6000035d1fc0>{number = 5, name = (null)}
2019-04-08 19:41:56.635918+0800 MyNSOperation[6125:93186] ------<NSThread: 0x6000035eae00>{number = 3, name = (null)}
2019-04-08 19:41:56.635904+0800 MyNSOperation[6125:93188] 3--<NSThread: 0x6000035eae40>{number = 4, name = (null)}
2019-04-08 19:41:58.637956+0800 MyNSOperation[6125:93188] 3--<NSThread: 0x6000035eae40>{number = 4, name = (null)}
2019-04-08 19:41:58.637958+0800 MyNSOperation[6125:93195] 4----<NSThread: 0x6000035d1fc0>{number = 5, name = (null)}
再来看使用NSOperationQueue来控制串行,并行
这里注意一个重要属性,maxConcurrentOperationCount,它是一个队列中能同时并发执行的最大操作数量。
这个数默认情况为-1,表示不进行限制。
当为1的时候,很显然就是串行。
大于1的时候,就是并行。当然,你如果设置了很大,超过了系统 限制,系统就自动调整为他所允许的最大值。
//设置最大并发数量
-(void)setMaxConcurrentOperationCount{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.设置最大的并发数量
queue.maxConcurrentOperationCount = 1;//串行队列
// queue.maxConcurrentOperationCount = 2;//并发队列
// queue.maxConcurrentOperationCount = 999;
//3.添加操作
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"1---%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"2---%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"3---%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i=0; i<2; ++i) {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"4---%@",[NSThread currentThread]);
}
}];
}
当设为1的时候的打印:
2019-04-08 19:52:04.936432+0800 MyNSOperation[6517:99856] 1---<NSThread: 0x6000003b5040>{number = 3, name = (null)}
2019-04-08 19:52:06.938095+0800 MyNSOperation[6517:99856] 1---<NSThread: 0x6000003b5040>{number = 3, name = (null)}
2019-04-08 19:52:08.939725+0800 MyNSOperation[6517:99855] 2---<NSThread: 0x6000003b4fc0>{number = 4, name = (null)}
2019-04-08 19:52:10.940218+0800 MyNSOperation[6517:99855] 2---<NSThread: 0x6000003b4fc0>{number = 4, name = (null)}
2019-04-08 19:52:12.941507+0800 MyNSOperation[6517:99856] 3---<NSThread: 0x6000003b5040>{number = 3, name = (null)}
2019-04-08 19:52:14.946991+0800 MyNSOperation[6517:99856] 3---<NSThread: 0x6000003b5040>{number = 3, name = (null)}
2019-04-08 19:52:16.951288+0800 MyNSOperation[6517:99855] 4---<NSThread: 0x6000003b4fc0>{number = 4, name = (null)}
2019-04-08 19:52:18.956847+0800 MyNSOperation[6517:99855] 4---<NSThread: 0x6000003b4fc0>{number = 4, name = (null)}
可以看出,为串行执行。
当设为2的时候的打印:
2019-04-08 19:53:48.113132+0800 MyNSOperation[6584:101034] 1---<NSThread: 0x60000357bc80>{number = 3, name = (null)}
2019-04-08 19:53:48.113146+0800 MyNSOperation[6584:101036] 2---<NSThread: 0x60000357bcc0>{number = 4, name = (null)}
2019-04-08 19:53:50.114523+0800 MyNSOperation[6584:101034] 1---<NSThread: 0x60000357bc80>{number = 3, name = (null)}
2019-04-08 19:53:50.114535+0800 MyNSOperation[6584:101036] 2---<NSThread: 0x60000357bcc0>{number = 4, name = (null)}
2019-04-08 19:53:52.116719+0800 MyNSOperation[6584:101035] 3---<NSThread: 0x600003542000>{number = 6, name = (null)}
2019-04-08 19:53:52.116730+0800 MyNSOperation[6584:101037] 4---<NSThread: 0x600003542880>{number = 5, name = (null)}
2019-04-08 19:53:54.117248+0800 MyNSOperation[6584:101037] 4---<NSThread: 0x600003542880>{number = 5, name = (null)}
2019-04-08 19:53:54.117292+0800 MyNSOperation[6584:101035] 3---<NSThread: 0x600003542000>{number = 6, name = (null)}
可以看出,是并发执行。并发的数量为2,但是具体开几个线程,这个就是由系统决定的。我们无需操心。
接下来,我们就要看NSOperation最令人着迷的地方:操作依赖,通过给操作添加依赖关系,我们就很容易控制操作之间的执行先后顺序。
NSOperation提供了3个接口供我们来管理和查看依赖关系:
1.addDependency添加依赖,使当前的操作依赖于参数的那个操作
2.removeDependency移除依赖
3.@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
我们来看一个添加依赖操作的代码把:
//添加依赖:
-(void)addDependency{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
//3.添加依赖
[op2 addDependency:op1];
//4.加到队列中去
[queue addOperation:op1];
[queue addOperation:op2];
}
来看打印:
2019-04-08 20:07:31.661222+0800 MyNSOperation[7082:109038] 1---<NSThread: 0x60000016bd40>{number = 3, name = (null)}
2019-04-08 20:07:33.662707+0800 MyNSOperation[7082:109038] 1---<NSThread: 0x60000016bd40>{number = 3, name = (null)}
2019-04-08 20:07:35.668239+0800 MyNSOperation[7082:109039] 2---<NSThread: 0x600000157900>{number = 4, name = (null)}
2019-04-08 20:07:37.673734+0800 MyNSOperation[7082:109039] 2---<NSThread: 0x600000157900>{number = 4, name = (null)}
可以看到,无论执行几次,2都会等1执行完毕,他才执行。
关于优先级与依赖关系:
优先级不能取代依赖关系。
优先级作用在同一队列的准备就绪状态下的依赖关系。
今天就先玩到这里把。
IOS高级开发之多线程(四)NSOperation的更多相关文章
- IOS高级开发之多线程(五)NSOperation 2
接着看NSOperation.NSOperationQueue线程间的通信: 应用场景:比如我们经常把一些耗时的操作比如下载图片放在子线程,那么当这个完成之后,我们就需要回到主线程,这个时候就需要用到 ...
- ios高级开发之多线程(三)GCD技术
GCD是基于C的API,它是libdispatch的的市场名称.而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持. 那么我们为什么要用 ...
- ios高级开发之多线程(二)NSThread技术
多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...
- ios高级开发之多线程(一)
1.概念: 多线程(multithreading)到底是什么呢,它是指在软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件的支持,而能够在同一时间执行多个线程,进而提升整体处理性 ...
- (转发)IOS高级开发~Runtime(四)
用C代替OC: #import <objc/runtime.h> #import <objc/message.h> #import <stdio.h> extern ...
- (转发)IOS高级开发~Runtime(三)
11.系统类的方法实现部分替换 - (void) methodExchange { Method m1 = class_getInstanceMethod([NSStringclass],@selec ...
- (转发)IOS高级开发~Runtime(二)
一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...
- (转发)IOS高级开发~Runtime(一)
IOS高级开发-Runtime(一) IOS高级开发-Runtime(二) IOS高级开发-Runtime(三) IOS高级开发-Runtime(四) 一些公用类: @interface Custom ...
- 移动开发在路上-- IOS移动开发系列 多线程二
最近太忙没太多的时间,忙碌的码农生活空下来一点时间,都会挤出来看一些技术或者咨询的文章,废话不多说,直奔主题. 接着上一次的继续说. 定时器在多线程的使用 NSRunLoop 是线程相关的基础框架的一 ...
随机推荐
- macOS 版微信小助手,支持微信多开、防撤回、远程控制mac、自动回复等等
微信小助手 GitHub大牛提供的微信小助手是一款插件,该插件具备多开.防撤回.免手机认证登录.自动回复.远程控制自己的 macOS.群发等众多功能 GitHub网址:https://github.c ...
- Python3学习之路~9.1 paramiko模块:实现ssh执行命令以及传输文件
我们一般使用linux的时候,都是在Windows上安装一个ssh客户端连接上去.那么从一台linux如何连接到另一条linux呢?使用ssh命令即可,因为每台linux机器自己都有一个ssh客户端. ...
- Yoink Mac版(临时文件存储助手)中文版
Yoink Mac版是Mac上一款临时文件存储助手,当你拖动文件时Yoink for Mac就会出现,拖放文件到Yoink窗口中即可,需要文件时随时都能从Yoink窗口中拖出文件,使用非常便捷,小编准 ...
- 爬取豆瓣电影排行top250
功能描述V1.0: 爬取豆瓣电影排行top250 功能分析: 使用的库 1.time 2.json 3.requests 4.BuautifulSoup 5.RequestException 上机实验 ...
- 2018-2019-2 网络对抗技术 20165321 Exp6 信息搜集与漏洞扫描
1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 2.实践内容 (1)各种搜索技巧的应用 百度查找IP地址: 查了一下kali的IP地址 https://fofa.so/的使用: 查询了一 ...
- CDIE2019中国数字化创新展暨首席信息官峰会上海站来袭~
China Digital Innovation Expo & CIO Summit 2019是由Dot Connector(上海华昂商务咨询有限公司)主办的第五届聚焦中国技术领袖,探索创新, ...
- Intellij IDEA 配置Tomcat远程调试
一.前言 在服务器端开发过程中,由于服务器环境差异导致运行结果不符合预期. 所以就需要到IDEA Debug 服务器代码.看起来貌似很高大上的事情. 今天就说说使用Intellij IDEA 配置的方 ...
- linux配制DNS服务器基本能功能
1.环境 Centos 6.5 bind 关闭防火墙和SELINUX 2.安装bind服务软件 yum -y install bind 3.配制主配制文件/etc/name.conf options ...
- Python3标准库
文本 1. string:通用字符串操作 2. re:正则表达式操作 3. difflib:差异计算工具 4. textwrap:文本填充 5. unicodedata:Unicode字符数据库 6. ...
- JavaFX-Application
JavaFX—Application 1.Application是JavaFX程序的入口,任何javafx应用程序程序都要继承该类并重写start()方法 public class TsetStage ...