pthread 实现多线程操作

代码实现:

void * run(void *param)
{
   for (NSInteger i = 0; i < 1000; i++) {
       NSLog(@"---buttonclick---%zd---%@", i, [NSThread currentThread]);
   }    return NULL;
} @implementation ViewController - (IBAction)clickButton:(id)sender {
   // 定义一个线程
   pthread_t thread;    // 创建一个线程  (参1)pthread_t *restrict:创建线程的指针,(参2)const pthread_attr_t *restrict:线程属性  (参3)void *(*)(void *):线程执行的函数的指针,(参4)void *restrict:null
   pthread_create(&thread, NULL, run, NULL);    // 何时回收线程不需要你考虑
   pthread_t thread2;    pthread_create(&thread2, NULL, run, NULL); }

NSThread实现多线程

一个 NSThread 对象就代表一条线程

创建线程的多种方式

  • 第一种方式:先创建再启动线程

      // 创建线程
     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"];  // 线程启动了,事情做完了才会死, 一个NSThread对象就代表一条线程
     [thread start];
  • 第二种:直接创建并启动线程

      // 直接创建并启动线程
     [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"];
  • 第三种:

      // 直接创建并启动线程
     [self performSelectorInBackground:@selector(run:) withObject:@"jack"];  // 使线程进入阻塞状态
     [NSThread sleepForTimeInterval:2.0];  #pragma mark - 执行run方法
     - (void)run:(NSString *)param
     {
         // 当前线程是否是主线程
         for (NSInteger i = 0; i < 100; i++) {
             NSLog(@"---%@---%zd---%d", [NSThread currentThread], i,  [NSThread isMainThread]);
         }
     }
  • 方法2和方法3的优点:快捷 方法1的优点:可以轻松拿到线程

线程间通信

  • 线程间通信的体现

1个线程传递数据给另1个线程

在1个线程中执行完特定任务后,转到另1个线程继续执行任务

线程间通信的常用方法:小程序图片下载

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   // 获取图片的url
   NSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"];
// 另开1条线程 object用于数据的传递
   NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downLoadWithURL:) object:url];
   // 由于下面下载图片的耗时太长,应领开启线程来完成
   [thread start];
} // 下载图片
- (void)downLoadWithURL:(NSURL *)url
{
   NSLog(@"%@", [NSThread currentThread]);
   // 下载图片
   NSData *data = [NSData dataWithContentsOfURL:url];
   // 生成图片
   UIImage *image = [UIImage imageWithData:data];    // 返回主线程显示图片
   [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}

以上两种方式使用线程已经过时了,开发中我们操作线程大多都使用 GCD 和 NSOperation 来实现多线程操作。

下面我就给大家系统的介绍一下 GCD 是如何实现多线程的

GCD 实现多线程

GCD 简介

GCD 全称是Grand Central Dispatch,可译为“超级厉害的中枢调度器”,GCD 是苹果公司为多核的并行运算提出的解决方案, GCD会自动利用更多的 CPU 内核(比如双核、四核)来开启线程执行任务,GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程),不需要我们程序员手动管理内存。

任务和队列

任务:在同步函数和异步函数中执行

队列:用来存放任务(并发 串行)

GCD会自动将队列中的任务取出,放到对应的线程,任务的取出遵循FIFO,即先入先出队列,First Input First Output 的缩写。先进入的任务先完成并结束,再执行后面的任务。

同步函数和异步函数,并发队列和串行队列

  • 用同步的方式执行任务:在当前线程中可立即执行任务,不具备开启线程的能力

  • 用异步的方式执行任务:在当前线程结束时执行任务,具备开启新的线程的能力

  • 并发队列:允许多个任务同时执行

  • 串行队列:一个任务执行完毕后,再执行下一个任务

创建并发/串行队列代码:

// 创建并发队列
// 参1:const char *label 队列名称
// 参2:dispatch_queue_attr_t attr 队列类型
dispatch_queue_t queueConcurrent = dispatch_queue_create("520it.com", DISPATCH_QUEUE_CONCURRENT); // 创建串行队列  serial 串行  concurrent并发
dispatch_queue_t queueSerial = dispatch_queue_create("520it.com", DISPATCH_QUEUE_SERIAL); // 获取全局队列 全局队列是并发队列
// 参1:队列的优先级
// 参2:0(以后可能用到的参数)
dispatch_queue_t queueGlobal = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 全局并发队列的优先级
#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 // 后台 // 获取主队列  在主队列中的任务都会在主线程中执行。
dispatch_queue_t queueMain = dispatch_get_main_queue();

同步/异步函数代码表示:

// GCD同步函数串行队列(立即执行,当前线程)
// 参1: dispatch_queue_t queue 队列
// 参2: 任务
dispatch_sync(queueSerial, ^{
   for (NSInteger i = 0; i < 10; i++) {
       NSLog(@"~~~%@", [NSThread currentThread]);
  }
}); // 同步函数并行队列(立即执行,当前线程)
dispatch_sync(queueConcurrent, ^{
   for (NSInteger i = 0; i < 10; i++) {
       NSLog(@"~~~%@", [NSThread currentThread]);
   }
}); // 异步函数串行队列 (另开线程,多个任务按顺序执行)
dispatch_async(queueSerial, ^{
   dispatch_async(queueSerial, ^{
       for (NSInteger i = 0; i < 10; i++) {
           NSLog(@"~~~%@", [NSThread currentThread]);
       }
   });
   dispatch_async(queueSerial, ^{
       for (NSInteger i = 0; i < 10; i++) {
           NSLog(@"~~~%@", [NSThread currentThread]);
       }
   });
   dispatch_async(queueSerial, ^{
       for (NSInteger i = 0; i < 10; i++) {
           NSLog(@"~~~%@", [NSThread currentThread]);
       }
   });
}); // 异步函数并行队列 (另开线程,多个任务一起执行)
dispatch_async(queueConcurrent, ^{
   dispatch_async(queueSerial, ^{
           for (NSInteger i = 0; i < 10; i++) {
               NSLog(@"~~~%@", [NSThread currentThread]);
           }
   });
   dispatch_async(queueSerial, ^{
       for (NSInteger i = 0; i < 10; i++) {
           NSLog(@"~~~%@", [NSThread currentThread]);
       }
   });
   dispatch_async(queueSerial, ^{
       for (NSInteger i = 0; i < 10; i++) {
           NSLog(@"~~~%@", [NSThread currentThread]);
       }
   });
}); // 主队列:(任何一个任务只要在主队列中,都会加入到主线程的队列中执行)

注意:使用sync函数(同步函数)往当前串行队列中添加任务,会卡住当前的串行队列

解释:使用同步函数添加任务 A 到串行队列,说明要在当前串行队列立即执行任务 A ,任务 A 执行完后,才会执行任务 A 后面的代码。但当前队列是串行队列,也就是说任务 A 必须等到当前串行队列中正在执行的任务 B 完成之后才能执行,因此又必须先执行任务 A 中立即执行任务,又要必须等到任务 B 执行完以后才能执行下一个任务,所以就会卡死。你等我,我等你,谁也无法执行。

GCD实现线程通信

小项目:下载图片

代码如下:

// 获取图片的url
NSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]; // 开启线程下载图片
dispatch_queue_t queue = dispatch_queue_create("111", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{
   NSData *data = [NSData dataWithContentsOfURL:url];
   UIImage *image = [UIImage imageWithData:data];    // 下载完成后返回主线程显示图片
   dispatch_async(dispatch_get_main_queue(), ^{
       self.imageView.image = image;
   });
});

GCD其他常用函数

dispatch_barrier 栅栏

// 1.barrier : 在barrier前面的先执行,然后再执行barrier,然后再执行barrier后面的 barrier的queue不能是全局的并发队列
dispatch_queue_t queue = dispatch_queue_create("11", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{
   for (int i = 0;  i < 100; i++) {
       NSLog(@"%@--1", [NSThread currentThread]);
   }
}); dispatch_async(queue, ^{
   for (int i = 0;  i < 100; i++) {
       NSLog(@"%@--2", [NSThread currentThread]);
   }
}); dispatch_barrier_async(queue, ^{
   for (int i = 0;  i < 100; i++) {
       NSLog(@"%@--3", [NSThread currentThread]);
   }
}); dispatch_async(queue, ^{
   for (int i = 0;  i < 100; i++) {
       NSLog(@"%@--4", [NSThread currentThread]);
   }
});

dispatch_after 延迟执行

// 延迟执行
// 方法1
[self performSelector:@selector(run:) withObject:@"参数" afterDelay:2.0]; // 方法2
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
   for (NSInteger i = 0; i < 100; i++) {
       NSLog(@"%@", [NSThread currentThread]);
   }
}); // 方法3
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

dispatch_once 整个程序运行中执行一次

// 整个程序中只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
   // 一次性代码
});

作用:实现某个类的单粒对象

单例模式:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

static id _person;
+ (instancetype)sharePerson
{
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _person = [[super alloc] init];
   });
   return _person;
} + (instancetype)allocWithZone:(struct _NSZone *)zone
{
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _person = [super allocWithZone:zone];
   });
   return _person;
} - (id)copy
{
   return _person;
}

开发中一般自定义成宏,比较方便,一行代码搞定。

dispatch_apply 快速迭代

示例小程序:将一个文件夹中的图片剪切到另一个文件夹

// 将图片剪切到另一个文件夹里
NSString *from = @"/Users/Ammar/Pictures/壁纸";
NSString *to = @"/Users/Ammar/Pictures/to";
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *subPaths = [manager subpathsAtPath:from]; // 快速迭代
dispatch_apply(subPaths.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
  NSLog(@"%@ - %zd", [NSThread currentThread], index);
   NSString *subPath = subPaths[index];
   NSString *fromPath = [from stringByAppendingPathComponent:subPath];
   NSString *toPath = [to stringByAppendingPathComponent:subPath];    // 剪切
   [manager moveItemAtPath:fromPath toPath:toPath error:nil];
   NSLog(@"%@---%zd", [NSThread currentThread], index);
});

dispatch_group 队列组

示例小程序:需求下载图片1 下载图片2 将图片1和图片2合成新的图片

// 创建队列
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 创建组
dispatch_group_t group = dispatch_group_create(); // 用组队列下载图片1
dispatch_group_async(group, queue, ^{
   NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]];
   self.image1 = [UIImage imageWithData:data];
   NSLog(@"1%@", [NSThread currentThread]);
}); // 用组队列下载图片2
dispatch_group_async(group, queue, ^{
   NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]];
   self.image2 = [UIImage imageWithData:data];
   NSLog(@"2%@", [NSThread currentThread]);
}); // 将图片1和图片2合成一张图片
dispatch_group_notify(group, queue, ^{
   CGFloat imageW = self.imageView.bounds.size.width;
   CGFloat imageH = self.imageView.bounds.size.height;    // 开启位图上下文
   UIGraphicsBeginImageContext(self.imageView.bounds.size);    // 画图
   [self.image1 drawInRect:CGRectMake(0, 0, imageW * 0.5, imageH)];
   [self.image2 drawInRect:CGRectMake(imageW * 0.5, 0, imageW * 0.5, imageH)];    // 将图片取出
   UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    // 关闭图形上下文
   UIGraphicsEndImageContext();    // 在主线程上显示图片
   dispatch_async(dispatch_get_main_queue(), ^{
       self.imageView.image = image;
   });
   NSLog(@"3%@", [NSThread currentThread]);
});

GCD定时器

GCD定时器不受Mode影响因此比NSTimer要准确

static int count = 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
   // 这句话的意思现在很好懂了
}); // GCD定时器
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 1.创建一个定时器源 // 参1:类型定时器
// 参2:句柄
// 参3:mask传0
// 参4:队列  (注意:dispatch_source_t本质是OC对象,表示源)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 严谨起见,时间间隔需要用单位int64_t,做乘法以后单位就变了
// 下面这句代码表示回调函数时间间隔是多少
int64_t interval = (int64_t)(2.0 * NSEC_PER_SEC); // 如何设置开始时间 CGD给我们了一个设置时间的方法  
// 参1:dispatch_time_t when 传一个时间, delta是增量 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)); // 从现在起3秒后开始 // 2.设置定时器的各种属性 // 参1:timer
// 参2:开始时间
// 参3:时间间隔
// 参4:传0 不需要   DISPATCH_TIME_NOW 表示现在 GCD 时间用 NS 表示
dispatch_source_set_timer(self.timer, start, interval, 0); // 3.设置回调(即每次间隔要做什么事情)
dispatch_source_set_event_handler(self.timer, ^{
   NSLog(@"----------------%@", [NSThread currentThread]);    // 如果希望做5次就停掉
   count++;
   if (count == 5) {
       dispatch_cancel(self.timer);
       self.timer = nil;
   }
}); // 4.启动定时器  (恢复)
dispatch_resume(self.timer);

讲完 GCD 就该讲讲 NSOperation,它是 GCD 的面向对象的封装,使用起来也更方便,

NSOperation实现多线程

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

NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法

使用 NSOperation 实现多线程的步骤:

创建任务 NSOperation 对象
创建 NSOperationQueue 队列
将任务 NSOperation 对象 add 到 NSOperationQueue 队列中去

NSInvocationOperation

代码如下:

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

[op start];

注意:默认情况下,调用了start方法后并不会开一条新的线程去执行,而是在当前线程同步执行操作,只有将 NSOperation 放到一个 NSOperationQueue 中,才会异步执行操作

NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
   // 在主线程
     NSLog(@"下载1------%@", [NSThread currentThread]);
}]; // 添加额外的任务(在子线程执行),封装数大于1才会异步执行
[op addExecutionBlock:^{
   NSLog(@"下载2------%@", [NSThread currentThread]);
}];

自定义Operation:需要实现- (void)main方法,需要做的事情放在mian方法中

NSOperationQueue

使用NSOperationQueue创建队列:主队列和全局队列

// 创建一个其他队列(包括串行队列和并发队列) 放到这个队列中的NSOperation对象会自动放到子线程中执行

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 创建一个主队列,放到这个队列中的NSOperation对象会自动放到子线程中执行
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; // 表示并发数量:即同时执行任务的最大数。
queue.maxConcurrentOperationCount = 1;

队列的取消、暂停、恢复:

// NSOpertion的 - cancel 方法也可以停止单个操作
- (void)cancelAllOperations;
// YES代表暂停队列,NO代表恢复队列
- (void)setSuspended:(BOOL)b;

添加依赖

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
   NSLog(@"download1 -------------- %@", [NSThread currentThread]);
}]; NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
   NSLog(@"download2 -------------- %@", [NSThread currentThread]);
}]; NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
   NSLog(@"download3 -------------- %@", [NSThread currentThread]);
}]; // 添加依赖: block1 和 block2执行完后 再执行 block3  block3依赖于block1和block2 // 给block3添加依赖 让block3在block1和block2之后执行
[block3 addDependency:block1];
[block3 addDependency:block2]; [queue addOperation:block1];
[queue addOperation:block2];
[queue addOperation:block3];

注意:不能循环依赖,但可以跨队列依赖,不管NSOperation对象在哪个队列。只要是两个NSOperation对象就可以依赖

线程间通信

示例:下载图片

// 下载图片 operation实现线程间通信
[[[NSOperationQueue alloc] init] addOperation:[NSBlockOperation blockOperationWithBlock:^{
   UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];    // 返回主线程
   [[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{
       self.imageView.image = image;
   }]]; }]];

示例:下载图片1和图片2 并合成图片

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 下载图片1
__block UIImage *image1 = nil;
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
   image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];
}]; // 下载图片2
__block UIImage *image2 = nil;
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
   image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]]; }]; CGFloat imageW = self.imageView.bounds.size.width;
CGFloat imageH = self.imageView.bounds.size.height; // 合成图片
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
   UIGraphicsBeginImageContext(CGSizeMake(imageW, imageH));
   [image1 drawInRect:CGRectMake(0, 0, imageW * 0.5, imageH)];
   [image2 drawInRect:CGRectMake(0.5 * imageW, 0, 0.5 * imageW, imageH)];    UIImage *image3 = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();
   // 切换回主线程显示图片
   [[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{
       self.imageView.image = image3;
   }]]; }]; // 设置依赖
[block3 addDependency:block1];
[block3 addDependency:block2]; // 添加任务到队列中
[queue addOperation:block1];
[queue addOperation:block2];
[queue addOperation:block3];

应用

应用:SDWebImage 框架的底层主要功能实现就是基于多线程,使用多线程,我们可以实现小图片的多图片下载。这里的逻辑其实是比较复杂的

实现小图片的多图片下载思路:

代码实现见本文代码。

本文代码见:Multithreading

https://github.com/lizhaoLoveIT/Multithreading

iOS中实现多线程的技术方案的更多相关文章

  1. OS X 和iOS 中的多线程技术(上)

    OS X 和iOS 中的多线程技术(上) 本文梳理了OS X 和iOS 系统中提供的多线程技术.并且对这些技术的使用给出了一些实用的建议. 多线程的目的:通过并发执行提高 CPU 的使用效率,进而提供 ...

  2. OS X 和iOS 中的多线程技术(下)

    OS X 和iOS 中的多线程技术(下) 上篇文章中介绍了 pthread 和 NSThread 两种多线程的方式,本文将继续介绍 GCD 和 NSOperation 这两种方式.. 1.GCD 1. ...

  3. iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)

    简介 在软件开发中,多线程编程技术被广泛应用,相信多线程任务对我们来说已经不再陌生了.有了多线程技术,我们可以同做多个事情,而不是一个一个任务地进行.比如:前端和后台作交互.大任务(需要耗费一定的时间 ...

  4. iOS中发送HTTP请求的方案

    在iOS中,常见的发送HTTP请求的方案有 苹果原生(自带) NSURLConnection:用法简单,最古老最经典的一种方案 NSURLSession:功能比NSURLConnection更加强大, ...

  5. iOS中几种数据持久化方案

    概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) ...

  6. iOS中几种数据持久化方案:我要永远地记住你!

    http://www.cocoachina.com/ios/20150720/12610.html 作者:@翁呀伟呀 授权本站转载 概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启 ...

  7. iOS中的多线程 NSOperation

    在ios中,使用多线程有三种方式,分别是:NSThread.NSOperation和NSOperationQueue.GCD,在本节,主要讲解一下NSOperation的使用. NSOperation ...

  8. IOS中的多线程和NSRunLoop概述(转载)

    线程概述 有些程序是一条直线,从起点到终点,如Hello World,运行打印完,它的生命周期便结束了:有些程序是一个圆,不断循环,直到将它切断,如操作系统,一直运行直到你关机.  一个运行着的程序就 ...

  9. IOS中DES与MD5加密方案

      0 2 项目中用的的加密算法,因为要和安卓版的适配,中间遇到许多麻烦. MD5算法和DES算法是常见的两种加密算法. MD5:MD5是一种不可逆的加密算法,按我的理解,所谓不可逆,就是不能解密,那 ...

随机推荐

  1. 在map中根据value获取key

    原文:http://blog.csdn.net/mexican_jacky/article/details/51789548 //根据map的value获取map的key private static ...

  2. 转:为什么C++中空类和空结构体大小为1?

    参考:http://www.spongeliu.com/260.html 为什么C++中空类和空结构体大小为1? On November 17, 2010, in C语言, 语言学习, by spon ...

  3. Bootstrap文本对齐风格

    在排版中离不开文本的对齐方式.在CSS中常常使用text-align来实现文本的对齐风格的设置.其中主要有四种风格: ☑  左对齐,取值left ☑  居中对齐,取值center ☑  右对齐,取值r ...

  4. mongodb数据库js查询

    #健康风险-disease db.disease.find({versions:'2'}).forEach(function(item){ item.diseaseDetail && ...

  5. centos7 修改selinux 开机导致 faild to load SELinux policy freezing 错误

    centos7 修改selinux 开机导致 faild to load SELinux policy  freezing 错误 之前把selinux关闭了,这次想打开selinux,于是修改了 /e ...

  6. Android GC 那点事

    版权声明:本文由陈昱全原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/170 来源:腾云阁 https://www.qclo ...

  7. DOI EXCEL显示报表

    我这个是比较不规则的数据填充 1.程序开头,定义一个工作区,存对应单元格的值: BEGIN OF TY_EXCEL, C031() TYPE C, C032() TYPE C, C033() TYPE ...

  8. 创建生产订单函数BAPI_PRODORD_CREATE

    创建生产订单,创建订单长文本,订单下达 DATA:gs_bapi_pp_order_create TYPE bapi_pp_order_create. DATA:gt_bapi_order_key T ...

  9. Objective-C:Foundation框架-常用类-NSMutableArray

    NSMutableArray是NSArray对的子类,它是可变的,可以随意添加或者删除元素.与Array,也有静态和动态两种创建方法.也可已使用NSArray的方法来创建NSMutableArray. ...

  10. spring项目中使用定时任务

    当我们希望在某个时间点来执行一些业务方法的时候就用到定时任务,在spring的项目中使用定时任务很简单.如下 第一步.加入jar包 <dependency> <groupId> ...