iOS多线程知识梳理

线程进程基础概念

进程

进程是指在系统中正在运行的一个应用程序

每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

线程

1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程,称为主线程)

一个进程(程序)的所有任务都在线程中执行

比较

1.线程是CPU调用(执行任务)的最小单位。

2.进程是CPU分配资源的最小单位。

3.一个进程中至少要有一个线程。

4.同一个进程内的线程共享进程的资源。

多线程概述

1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务

多线程技术可以提高程序的执行效率

原理

同一时间,CPU只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

那么如果线程非常非常多,会发生什么情况?

CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源,同时每条线程被调度执行的频次也会会降低(线程的执行效率降低)。

因此我们一般只开3-5条线程

优缺点

多线程的优点

能适当提高程序的执行效率

能适当提高资源利用率(CPU、内存利用率)

多线程的缺点

创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间

如果开启大量的线程,会降低程序的性能,线程越多,CPU在调度线程上的开销就越大。

程序设计更加复杂:比如线程之间的通信、多线程的数据共享等问题

多线程应用

主线程的主要作用

显示\刷新UI界面

处理UI事件(比如点击事件、滚动事件、拖拽事件等)

主线程的使用注意

别将比较耗时的操作放到主线程中

耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验

将耗时操作放在子线程中执行,提高程序的执行效率

iOS 开发中的几种线程知识点

pthread(c)

POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Windows操作系统也有其移植版pthreads-win32

具体使用

//创建线程
pthread_t thread;
/*
第一个参数pthread_t *restrict:线程对象
第二个参数const pthread_attr_t *restrict:线程属性
第三个参数void *(*)(void *) :指向函数的指针
第四个参数void *restrict:函数的参数
*/
pthread_create(&thread, NULL,run ,NULL);

NSThread

相对 pthread 提供了面向对象的方法使用更加简单

// 获取当前线程
+ (NSThread *)currentThread;
// 创建启动线程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 判断是否是多线程
+ (BOOL)isMultiThreaded;
// 线程休眠 NSDate 休眠到什么时候
+ (void)sleepUntilDate:(NSDate *)date;
// 线程休眠时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 结束/退出当前线程
+ (void)exit;
// 获取当前线程优先级
+ (double)threadPriority;
// 设置线程优先级 默认为0.5 取值范围为0.0 - 1.0
// 1.0优先级最高
// 设置优先级
+ (BOOL)setThreadPriority:(double)p;
// 获取指定线程的优先级
- (double)threadPriority NS_AVAILABLE(10_6, 4_0);
- (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0); // 设置线程的名字
- (void)setName:(NSString *)n NS_AVAILABLE(10_5, 2_0);
- (NSString *)name NS_AVAILABLE(10_5, 2_0); // 判断指定的线程是否是 主线程
- (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0);
// 判断当前线程是否是主线程
+ (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main
// 获取主线程
+ (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0); - (id)init NS_AVAILABLE(10_5, 2_0); // designated initializer
// 创建线程
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);
// 指定线程是否在执行
- (BOOL)isExecuting NS_AVAILABLE(10_5, 2_0);
// 线程是否完成
- (BOOL)isFinished NS_AVAILABLE(10_5, 2_0);
// 线程是否被取消 (是否给当前线程发过取消信号)
- (BOOL)isCancelled NS_AVAILABLE(10_5, 2_0);
// 发送线程取消信号的 最终线程是否结束 由 线程本身决定
- (void)cancel NS_AVAILABLE(10_5, 2_0);
// 启动线程
- (void)start NS_AVAILABLE(10_5, 2_0); // 线程主函数 在线程中执行的函数 都要在-main函数中调用,自定义线程中重写-main方法
- (void)main NS_AVAILABLE(10_5, 2_0); // thread body meth

GCD

GCD的优势

GCD是苹果公司为多核的并行运算提出的解决方案

GCD会自动利用更多的CPU内核(比如双核、四核)

GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

核心:任务和队列

任务

任务:执行什么操作,任务有两种执行方式: 同步函数 和 异步函数,他们之间的区别是

同步:只能在当前线程中执行任务,不具备开启新线程的能力,任务立刻马上执行,会阻塞当前线程并等待 Block中的任务执行完毕,然后当前线程才会继续往下运行

异步:可以在新的线程中执行任务,具备开启新线程的能力,但不一定会开新线程,当前线程会直接往下执行,不会阻塞当前线程

  • 异步操作:dispatch_async

  • 同步操作:dispatch_sync

//async: asynchronous 将任务异步的追加到队列中

dispatch_async(dispatch_get_global_queue(0,0), ^{

NSLog(@"async");

});

//sync: synchronous将任务同步的追加到队列中(等队列中的任务执行完,再将任务追加到队列)

//是同步追加,不是任务同步执行,在串行队列中,任务才同步执行

dispatch_sync(dispatch_get_global_queue(0,0), ^{

NSLog(@"sync");

});

队列

队列:用来存放任务,分为串行队列 和 并行队列

串行队列(Serial Dispatch Queue)

让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

并发队列(Concurrent Dispatch Queue)

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效

队列代码

dispatch_queue_t queue = dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
  • Serial Dispatch Queue --- 等待现在正在执行的任务处理结束(串行)

  • Concurrent Dispatch Queue --- 不等待现在正在执行的任务处理结束(并行、并发)

并发队列

dispatch_queue_t queue = dispatch_queue_create("com.xxcc", DISPATCH_QUEUE_CONCURRENT);

串行队列

dispatch_queue_t queue = dispatch_queue_create("com.xxcc", DISPATCH_QUEUE_SERIAL);

优先级

/**
第一个参数:优先级 也可直接填后面的数字
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
第二个参数: 预留参数 0
*/
dispatch_queue_t quque1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

任务和队列的组合

  1. 任务:同步函数 异步函数

  2. 队列:串行 并行

  • 异步函数+并发队列:会开启新的线程,并发执行
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});
  • 异步函数+串行队列:会开启一条线程,任务串行执行
dispatch_queue_t queue = dispatch_queue_create("com.xxcc", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});
  • 同步函数+并发队列:不会开线程,任务串行执行
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});
  • 同步函数+串行队列:不会开线程,任务串行执行
dispatch_queue_t queue = dispatch_queue_create("com.xxcc", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});
  • 异步函数+主队列:不会开线程,任务串行执行
dispatch_queue_t queue = dispatch_queue_create("com.xxcc", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});

*同步函数+主队列:死锁

//1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2.同步函数
dispatch_sync(queue, ^{
NSLog(@"---download1---%@",[NSThread currentThread]);
});

同步异步函数区别

  • 同步函数:立刻马上执行,会阻塞当前线程

  • 异步函数:当前线程会直接往下执行,不会阻塞当前线程

GCD 常用函数用法

1. dispatch_after

延迟执行

/*
第一个参数:延迟时间
第二个参数:要执行的代码
如果想让延迟的代码在子线程中执行,也可以更改在哪个队列中执行 dispatch_get_main_queue() -> dispatch_get_global_queue(0, 0)
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"---%@",[NSThread currentThread]);
});

2. dispatch_once

一次性代码

-(void)once
{
//整个程序运行过程中只会执行一次
//onceToken用来记录该部分的代码是否被执行过
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
} //创建一个单例方法
+ (ShareOnce *)shanreInstence{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[ShareOncealloc] init]; NSLog(@"只执行1次"); }); returninstance; }

3. dispatch_group_async & dispatch_group_notify

组队列

// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并行队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 执行队列组任务
dispatch_group_async(group, queue, ^{
});
//队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(group, queue, ^{
});
例子
//并行队列执行任务,在并行队列中加入多个串行队列

//disptch_group

dispatch_group_t group =dispatch_group_create();

dispatch_queue_t queue =dispatch_get_global_queue(0,0);

dispatch_group_async(group, queue, ^{

NSLog(@"task 01");

});

dispatch_group_async(group, queue, ^{

NSLog(@"task 02");

});

dispatch_group_async(group, queue, ^{

sleep(6);//等待6秒执行任务3 目的:测试等待时间的dispatch_group_wait这个方法

NSLog(@"task 03");

});

dispatch_group_async(group, queue, ^{

sleep(2);

NSLog(@"task 04");

});

//监视函数

//监视队列中得任务结束,执行block中得任务

dispatch_group_notify(group, queue, ^{

NSLog(@"done");

});

//等待时间

dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW,5ull*NSEC_PER_SEC);

//dispatch_group_wait 指定时间后,看你一眼queue是否执行完毕

//如果执行完返回0

//没有执行完,返回非0值

long result = dispatch_group_wait(group, time);

if (result == 0) {

NSLog(@"finish");

}else{

NSLog(@"not finish");
}
}

4. dispatch_barrier_async

栅栏函数

栅栏函数可以控制任务执行的顺序,栅栏函数之前的执行完毕之后,执行栅栏函数,然后在执行栅栏函数之后的

5. dispatch_apply

快速迭代

/*
第一个参数:迭代的次数
第二个参数:在哪个队列中执行
第三个参数:block要执行的任务
*/
dispatch_apply(10, queue, ^(size_t index) {
});

6. dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait

信号量机制

//使用

dispatch_queue_t queue =dispatch_get_global_queue(0,0);

dispatch_semaphore_t dsema =dispatch_semaphore_create(1);

NSMutableArray *array = [NSMutableArrayarray];

for (int i =0; i < 1000; i++) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(dsema,DISPATCH_TIME_FOREVER);
[arrayaddObject:[NSNumbernumberWithInt:i]];
dispatch_semaphore_signal(dsema); }); } NSLog(@"%@", array);

NSOperation

NSOperation 是苹果公司对 GCD 的封装,完全面向对象,并比GCD多了一些更简单实用的功能,所以使用起来更加方便易于理解。NSOperation 和NSOperationQueue 分别对应 GCD 的 任务 和 队列。

NSOperation和NSOperationQueue实现多线程的具体步骤

1.将需要执行的操作封装到一个NSOperation对象中

2.将NSOperation对象添加到NSOperationQueue中

系统会自动将NSOperationQueue中的NSOperation取出来,并将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

使用NSOperation子类的方式有3种

1.NSInvocationOperation
NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];
//启动操作
[op start];
2.NSBlockOperation 常用的一种
//1.封装操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//要执行的操作,在主线程中执行
NSLog(@"1------%@",[NSThread currentThread]);
}];
//2.追加操作,追加的操作在子线程中执行,可以追加多条操作
[op addExecutionBlock:^{
NSLog(@"---download2--%@",[NSThread currentThread]);
}];
[op start];
3.自定义类继承NSOperation,实现内部方法,复用性强
// 重写自定义类的main方法实现封装操作
-(void)main
{
// 要执行的操作
} // 实例化一个自定义对象,并执行操作
CustomOperation *op = [[CustomOperation alloc]init];
[op start];

NSOperationQueue队列的使用

两种队列

主队列:通过mainQueue获得,凡是放到主队列中的任务都将在主线程执行

非主队列:直接alloc init出来的队列。非主队列同时具备了并发和串行的功能,通过设置最大并发数属性来控制任务是并发执行还是串行执行

作用

NSOperation可以调用start方法来执行任务,但默认是同步执行的

如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

两者结合的方法使用 NSOperation和NSOperationQueue结合使用创建多线程

注:这里使用NSBlockOperation示例,其他两种方法一样
// 1. 创建非主队列 同时具备并发和串行的功能,默认是并发队列
NSOperationQueue *queue =[[NSOperationQueue alloc]init];
//NSBlockOperation 不论封装操作还是追加操作都是异步并发执行
// 2. 封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1 -- %@",[NSThread currentThread]);
}];
// 3. 将封装操作加入主队列
// 也可以不获取封装操作对象 直接添加操作到队列中
//[queue addOperationWithBlock:^{
// 操作
//}];
[queue addOperation:op1];

NSOperation和NSOperationQueue的重要属性和方法

  • NSOperation的依赖

  • NSOperation监听操作

  • NSOperationQueue

  • maxConcurrentOperationCount 并发数

  • suspended 是否暂停

  • cancelAllOperations 取消所有任务,不再执行,不可逆

关键点:暂停和取消只能暂停或取消处于等待状态的任务,不能暂停或取消正在执行中的任务,必须等正在执行的任务执行完毕之后才会暂停,如果想要暂停或者取消正在执行的任务,可以在每个任务之间即每当执行完一段耗时操作之后,判断是否任务是否被取消或者暂停。如果想要精确的控制,则需要将判断代码放在任务之中,但是不建议这么做,频繁的判断会消耗太多时间

一个例子

#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIImage *image1;
@property(nonatomic,strong)UIImage *image2;
@end
@implementation ViewController -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 创建非住队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 下载第一张图片
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://img2.3lian.com/2014/c7/12/d/77.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image1 = [UIImage imageWithData:data];
}];
// 下载第二张图片
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://img2.3lian.com/2014/c7/12/d/77.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image2 = [UIImage imageWithData:data];
}];
// 合成操作
NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
// 开启图形上下文
UIGraphicsBeginImageContext(CGSizeMake(375, 667));
// 绘制图片1
[self.image1 drawInRect:CGRectMake(0, 0, 375, 333)];
// 绘制图片2
[self.image2 drawInRect:CGRectMake(0, 334, 375, 333)];
// 获取合成图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 回到主线程刷新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
// 添加依赖,合成图片需要等图片1,图片2都下载完毕之后合成
[combie addDependency:download1];
[combie addDependency:download2];
// 添加操作到队列
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combie];
}
@end

多线程共享读写资源隐患问题

容易死锁

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1========%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
NSLog(@"3========%@",[NSThread currentThread]);
} ThreadDemo[5615:874679] 1========{number = 1, name = main}

原因:任务A等待任务B完成才能继续执行,但作为串行队列的主队列又不能让任务B在任务A未完成之前开始执行,所以任务A等着任务B完成,任务B等着任务A完成

解决方案:

1.普通执行

2.同步队列

3.异步队列

多线程安全隐患的原因:1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件。

那么当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

可以使用互斥锁解决

几种加锁方法

  • synchronized

@synchronized(锁对象) {

// 需要锁定的代码

}

  • NSLock

iOS多线程知识梳理的更多相关文章

  1. iOS 多线程知识梳理

    #iOS多线程知识梳理 ##线程进程基础概念 ###进程 进程是指在系统中正在运行的一个应用程序每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 ###线程 1个进程要想执行任务,必须 ...

  2. iOS开发知识梳理博文集

    前言 做iOS开发有3年了,从当初的小白到现在,断断续续看过很多资料,之前也写过一些博文来记录,但是感觉知识点都比较凌乱.所以最近准备抽时间把iOS开发的相关知识进行一个梳理,主要分为OC基础.UI控 ...

  3. iOS多线程知识总结--GCD

    iOS多线程知识总结--GCD 1. iOS中苹果提供4钟方案来帮助我们实现多线程: (1) 纯C语言的pthread,偏底层,需要程序员手动管理线程的生命周期,基本不用. (2) OC语言的NSTr ...

  4. IOS多线程知识总结/队列概念/GCD/主队列/并行队列/全局队列/主队列/串行队列/同步任务/异步任务区别(附代码)

    进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间 线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程 队列 dispa ...

  5. IOS多线程知识总结/队列概念/GCD/串行/并行/同步/异步

    进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间: 线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程. 队列:dis ...

  6. 复习IOS多线程知识

    线程的注意点 1.不要同时开太多的线程(1~3条线程即可,不要超过5条) 2.线程概念 * 主线程 : UI线程,显示.刷新UI界面,处理UI控件的事件 * 子线程 : 后台线程,异步线程 3.不要把 ...

  7. iOS多线程编程的知识梳理

    多线程编程也称之为并发编程,由于其作用大,有比较多的理论知识,因此在面试中也是受到面试官的青睐.在日常项目开发中,至少网络请求上是需要使用到多线程知识的,虽然使用第三方的框架比如AFNetworkin ...

  8. iOS中多线程知识总结(一)

    这一段开发中一直在处理iOS多线程的问题,但是感觉知识太散了,所以就把iOS中多线程的知识点总结了一下. 1.基本概念 1)什么是进程?进程的特性是什么? 进程是指在系统中正在运行的一个应用程序.   ...

  9. iOS多线程学习

    在 iOS 中其实目前有 4 套多线程方案,他们分别是: Pthreads NSThread GCD NSOperation & NSOperationQueue 所以接下来,我会一一讲解这些 ...

随机推荐

  1. Python元组tuple(不可变)

    Python元组Tuple(不可变): 元组的特点: 1.元组的初始化: tuple = (1, )  #元组只有一个元素的话,初始化时要加,否则当做元素的普通变量类型处理 tuple = (1, 2 ...

  2. WY 2019 人工智能数学基础视频教程

    ├─01.第一阶段:AI数学基石 │ ├─01.第一章:线性代数 │ │ │ ]AI Math_[第1章]线性代数v2 .pdf │ │ │ │ │ └─视频 │ │ 01_本章概述.mp4 │ │ ...

  3. ARTS-S docker安装miniconda

    FROM centos:centos7.3.1611 MAINTAINER zhouyang3 <aaa@qq.com> WORKDIR /usr/local ADD ./ /usr/lo ...

  4. 经典常用SQL语句大全

    创建表 --删除表 --DROP TABLE [dbo].[Test] --创建表 CREATE TABLE [dbo].[Test] ( ,) PRIMARY KEY, ----自增主键 ) NUL ...

  5. 浅谈Java三大特性

    Java三大特性想必大家都不陌生:封装.继承以及多态.很多刚接触Java的小伙伴都会想,它们到底有什么了不得之处,又赋予Java这门编程语言什么魔力呢?今天我们来探讨一下吧~~ 首先,名词解释: 封装 ...

  6. JS基础-正则

    正则表达式 创建正则表达式 使用一个正则表达式字面量 const regex = /^[a-zA-Z]+[0-9]*\W?_$/gi; 调用RegExp对象的构造函数 const regex = ne ...

  7. 3步轻松搞定Spring Boot缓存

    作者:谭朝红 前言 本次内容主要介绍基于Ehcache 3.0来快速实现Spring Boot应用程序的数据缓存功能.在Spring Boot应用程序中,我们可以通过Spring Caching来快速 ...

  8. 通过url返回的状态来抢注好的用户名

    之前在注册很多网站时都想取一个好的用户名,但是不知道那些被注册了没有,通常时一个一个测试,但是很慢当时就想过这个思路,由于懒并没有去搞 主要思路就是:很多网站的用户主页的Url都存在用户名,替换为自己 ...

  9. eclipse设置护眼模式,就是设置为黑色背景,

    效果如上图 首先下载jar包,然后放到下面的目录,然后打开eclipse然后选择哪个dark的那个主题就可以了 然而这里只是设置软件部分的, 代码的背景和高亮显示,是在另外一个地方设置, 一般是下载e ...

  10. Vue.js 一问一答

    Vue.js 一问一答 记录一下在学习 Vue 过程中给自己问的一些问题,持续更新中... Vue.js 的核心是什么? 官网:Vue.js 的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进 ...