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的更多相关文章

  1. IOS高级开发之多线程(五)NSOperation 2

    接着看NSOperation.NSOperationQueue线程间的通信: 应用场景:比如我们经常把一些耗时的操作比如下载图片放在子线程,那么当这个完成之后,我们就需要回到主线程,这个时候就需要用到 ...

  2. ios高级开发之多线程(三)GCD技术

    GCD是基于C的API,它是libdispatch的的市场名称.而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持. 那么我们为什么要用 ...

  3. ios高级开发之多线程(二)NSThread技术

    多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...

  4. ios高级开发之多线程(一)

    1.概念: 多线程(multithreading)到底是什么呢,它是指在软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件的支持,而能够在同一时间执行多个线程,进而提升整体处理性 ...

  5. (转发)IOS高级开发~Runtime(四)

    用C代替OC: #import <objc/runtime.h> #import <objc/message.h> #import <stdio.h> extern ...

  6. (转发)IOS高级开发~Runtime(三)

    11.系统类的方法实现部分替换 - (void) methodExchange { Method m1 = class_getInstanceMethod([NSStringclass],@selec ...

  7. (转发)IOS高级开发~Runtime(二)

    一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...

  8. (转发)IOS高级开发~Runtime(一)

    IOS高级开发-Runtime(一) IOS高级开发-Runtime(二) IOS高级开发-Runtime(三) IOS高级开发-Runtime(四) 一些公用类: @interface Custom ...

  9. 移动开发在路上-- IOS移动开发系列 多线程二

    最近太忙没太多的时间,忙碌的码农生活空下来一点时间,都会挤出来看一些技术或者咨询的文章,废话不多说,直奔主题. 接着上一次的继续说. 定时器在多线程的使用 NSRunLoop 是线程相关的基础框架的一 ...

随机推荐

  1. Scala中 zip或者zipWithIndex的用法

    问题:你要遍历一个有序集合,同时你又想访问一个循环计数器,但最重要的是你真的不需要手动创建这个计数器.解决方案:    使用zipWithIndex或者zip方法来自动地创建一个计数器,假设你有一个有 ...

  2. 21.命名空间别名限定符::和global全局名称空间限定符

    命名空间别名限定符(::)用于查找标识符,它在指定的别名的命名空间中查找运算符,如下代码是在全局名称空间中查找System.Console.WriteLine("Hello World&qu ...

  3. linux ping: unknown host www.baidu.com

    在虚拟机中使用CentOS6.5时,ping www.baidu.com出现报错信息:“ping: unknown hostwww.baidu.com”,虚拟机和物理机网络连接是NAT方式,物理机访问 ...

  4. linux 笔记 第一天

    打开终端:ctrl+alt+t 清屏:ctrl+l 在终端在退出锁定:ctrl+c 目录:又称为文件夹,是包含所有的文件 目录创建规则: 1.大小是256 2.不能包含特殊字符 3.见名知义 路径:是 ...

  5. phpstorm----------phpstorm如何安装和使用laravel plugin

    1.安装 2.安装成功以后,删除项目里面的.idea文件.然后关闭phpstrom,重新打开该项目,就会提示你 然后.idea里面就会生成 laravel-plugin.xml 文件.就可以使用直接C ...

  6. JavaScript与java差异

  7. 【电子书分享】Learning PySpark下载,包含pdf、epub格式

    <Learning PySpark>电子书下载链接: 链接:http://pan.baidu.com/s/1skAC6LZ 密码:kbse,包括pdf.epub格式: (--学习愉快--)

  8. 【论文速读】Fangfang Wang_CVPR2018_Geometry-Aware Scene Text Detection With Instance Transformation Network

    Han Hu--[ICCV2017]WordSup_Exploiting Word Annotations for Character based Text Detection 作者和代码 caffe ...

  9. ERROR 1698 (28000): Access denied for user 'root'@'localhost'

    Some systems like Ubuntu, mysql is using by default the UNIX auth_socket plugin. Basically means tha ...

  10. get_class __class__ get_called_class 分析记录

    首先看代码: class A { use T { T::say as aTsay; } public function say() { echo 'a__class__:' . __CLASS__ . ...