一、多线程概述

  • 程序、进程、线程

    • 程序:由源代码生成的可执行应用。(例如:QQ.app)
    • 进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的QQ就是一个进程),进程拥有独立运行所需的全部资源。
    • 线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)

  一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。

  • 单线程

    • 每个正在运行的程序(即进程),至少包含一个线程,这个线程叫主线程。
    • 主线程在程序启动时被创建,用于执行main函数。
    • 只有一个主线程的程序,称作单线程程序。
    • 在单线程程序中,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。
  • 多线程
    • 拥有多个线程的程序,称作多线程程序。
    • iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程。
    • 可以根据需要开辟若干子线程
    • 子线程和主线程 都是 独立的运行单元,各自的执行互不影响,因此能够并发执行。
  • 单线程、多线程区别
    • 单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)。
    • 多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。
    • 注意:iOS中关于UI的添加和刷新必须在主线程中操作。

二、iOS平台下的多线程

  • iOS多线程实现种类

    • NSThread
    • NSOperationQueue
    • NSObject
    • GCD

三、NSThread

  • NSThread是一个轻量级的多线程,它有以下两种创建方法

    方法 功能
    - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument 初始化一个子线程,但需要手动开启
    + (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument 初始化一个子线程并自动开启
    -start 开启子线程
    -cancel 取消当前子线程,不是真正的取消,而是给子线程发送了一个“取消”消息,标记为canceled
    +exit

    直接将线程退出

    • 第一种:手动开辟一个子线程

       - (void)viewDidLoad {
      #pragma mark - NSThread手动开辟子线程
      // 参数1: target
      // 参数2: 方法
      // 参数3: 要传的参数
      NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHello:) object:@"说你好!"];
      #pragma mark - 启动子线程
      [thread start]; // 判断一个线程是否正在执行
      NSLog(@"是否正在执行%d",[thread isExecuting]);
      // 判断一个线程是否完成了任务(是否执行完毕)
      NSLog(@"是否执行完毕%d", [thread isFinished]); #pragma mark - 释放子线程的两种方式
      // 第一种:取消线程,不是真正的取消,而是给子线程发送了一个“取消”消息,标记为canceled
      // 判断线城是否被标记为canceled
      // NSLog(@"%d", [thread isCancelled]);
      // [thread cancel];
      // // 第二种:直接将线程退出
      // [NSThread exit]; } - (void)sayHello:(NSString *)hello {
      NSLog(@"%@", hello);
      // 打印当前线程
      NSLog(@"当前线程:%@", [NSThread currentThread]);
      NSLog(@"主线程:%@", [NSThread mainThread]);
      }

      Thread手动开辟一个子线程

    开辟子线程成功

    • 第二种:

       - (void)viewDidLoad {
      #pragma mark - NSThread自动开辟子线程
      // 线程自动开启,不需要创建NSThread的对象
      // 与手动开启线程的方法相比,target 和 selector 两个参数顺序颠倒
      [NSThread detachNewThreadSelector:@selector(sayHello:) toTarget:self withObject:@"说你好!"];
      } - (void)sayHello:(NSString *)hello {
      NSLog(@"%@", hello);
      // 打印当前线程
      NSLog(@"当前线程:%@", [NSThread currentThread]);
      NSLog(@"主线程:%@", [NSThread mainThread]);
      }

      Thread自动开辟子线程

  • 开辟子线程成功
  • 其他方法
  • 注意:
    • 每个线程都维护着与自己对应的NSAutoreleasePool对象,将其放在线程栈的栈顶。当线程结束时,会清空自动释放池。
    • 为保证对象的及时释放,在多线程方法中需要添加自动释放池。
    • 在应用程序打开的时候,系统会自动为主线程创建一个自动释放池。
    • 我们手动创建的子线程需要我们手动添加自动释放池。

四、NSObject实现异步后台执行

 - (void)viewDidLoad {
#pragma mark - NSObject开辟子线程
/**
* 开启线程的方式之二:NSObject
*/
// 使用performSelectorInBackground开辟子线程
// 参数1:方法
// 参数2:要传的参数
[self performSelectorInBackground:@selector(sayHello:) withObject:@"说你好!"];
self.view.backgroundColor = [UIColor yellowColor]; } - (void)sayHello:(NSString *)hello {
NSLog(@"%@", hello);
// 打印当前线程
NSLog(@"当前线程:%@", [NSThread currentThread]);
NSLog(@"主线程:%@", [NSThread mainThread]); // 回到主线程修改view的背景颜色
// 参数1:方法
// 参数2:要传的参数
// 参数3:是否等待子线程完成后再进入主线程
[self performSelectorOnMainThread:@selector(changeColor) withObject:nil waitUntilDone:NO];
} - (void)changeColor {
self.view.backgroundColor = [UIColor cyanColor];
// 打印当前线程
NSLog(@"==当前线程:%@", [NSThread currentThread]);
NSLog(@"==主线程:%@", [NSThread mainThread]);
}

NSObject异步后台执行,开辟子线程

开辟子线程成功

NSThread和NSObject结合使用:同步请求延迟加载图片

 @interface ViewController ()
/// 声明一个ImageView用来显示图片
@property (nonatomic, strong) UIImageView *showImageView;
/// 声明一个NSData类型的数据用于接收图片的数据
@property (nonatomic, strong) NSData *imageData;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// 创建imageView
_showImageView = [[UIImageView alloc] initWithFrame:CGRectMake(, , , )];
_showImageView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_showImageView]; } #pragma mark - 触发子线程去加载数据
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelectorInBackground:@selector(loadImageData) withObject:nil];
} // 加载图片数据
- (void)loadImageData {
NSLog(@"当前线程:%@", [NSThread currentThread]);
NSLog(@"主线程:%@", [NSThread mainThread]);
NSURL *url = [NSURL URLWithString:@"http://img4q.duitang.com/uploads/item/201506/13/20150613185209_nHy5E.jpeg"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// 创建主线程用于刷新UI
[self performSelectorOnMainThread:@selector(renovateUI) withObject:nil waitUntilDone:YES]; } // 主线程刷新UI
- (void)renovateUI {
// 安全判断
if ([NSThread mainThread]) {
_showImageView.image = [UIImage imageWithData:self.imageData];
}
}
@end

同步请求实现延迟加载图片

点击触发事件,开辟子线程成功,在主线程中刷新UI,图片显示成功

五、NSOperation和NSOperationQueue

  • NSOperation

    • NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类
    • 因为它是抽象的,不能够直接使用这个类,而是使用子类( NSInvocationOperation或NSBlockOperation )来执行实际任务。
    • NSOperation(含子类),只是一个操作,本身无主线程、子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用。
  • NSInvocationOperation
    • NSInvocationOperation是NSOperation的子类
    • 封装了执行操作的target和要执行的action。
 - (void)viewDidLoad {
/**
* NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue。
NSOperation的子类本身和多线程没有任何关系,它只是封装了一定的代码段和数据去实现一个功能。
在单独使用NSOperation的子类去创建线程的时候,实际上线程没有真正被创建。
*/
// 使用NSOperation的第一个子类去创建子线程 -- NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sayHello:) object:@"

UI进阶 多线程的更多相关文章

  1. UI进阶 科大讯飞(2) 语音合成(文字转换成语音)

    科大讯飞开放平台.SDK下载.添加静态库.初始化见UI进阶 科大讯飞(1) 语音听写(语音转换成文字) 实现语音合成 功能实现步骤: 导入头文件 创建文字识别对象 指定文字识别后的回调代理对象 开启文 ...

  2. android UI进阶之用【转】

    android UI进阶之用ViewPager实现欢迎引导页面 摘要: ViewPager需要android-support-v4.jar这个包的支持,来自google提供的一个附加包.大家搜下即可. ...

  3. UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天

    这篇博客的代码是直接在上篇博客的基础上增加的,先给出部分代码,最后会给出能实现简单功能的完整代码. UI进阶 即时通讯之XMPP登录.注册 1.好友列表 初始化好友花名册 #pragma mark - ...

  4. Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask

    多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread.Runnable.Callable.Future.FutureTask这几个类往往是初学者容易搞混的. ...

  5. UI:多线程 、用GCD创建线程

    什么是应用(程序):就是我们编写的代码编译后生成的app文件 进程:凡是一个运行的程序都可以看作为一个进程,如打开的多个 word,就可以认为是一个进程的多个线程. 线程:至少有一个线程就是主线程,网 ...

  6. Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析

    AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并主线程中更新UI,通过AsyncTask可以更加方便执行后台任务以及在主线程中访问UI ...

  7. UI进阶

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  8. iOS开发——UI进阶篇(十五)Quartz2D介绍

    一.Quartz2D简介 1.什么是Quartz2DQuartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完成的工作绘制图形 : 线条\三角形\矩形\圆\弧等绘制文字绘 ...

  9. iOS开发——UI进阶篇(七)程序启动原理、打电话、发短信

    一.Info.plist常见的设置 1.建立一个工程后,会在Supporting files文件夹下看到一个“工程名-Info.plist”的文件,该文件对工程做一些运行期的配置,非常重要,不能删除 ...

随机推荐

  1. 垃圾收集器GC的种类

    堆内存的结构:

  2. SRM 586 DIV1 L1

    可以化简为求n条线段的最大覆盖问题,需要注意的是对于实数而言. #include <iostream> #include <vector> #include <strin ...

  3. 捉虫记2:windows程序句柄泄露的上下文环境

    作为程序员,开发程序是基本功,而调试程序也是必不可少的技能之一.软件在主体功能开发完成后会经历各个阶段的测试,才会被发布.在测试过程中,出现较多的可能就是内存泄漏,句柄泄漏,异常崩溃等属于非功能型的软 ...

  4. 【转】Ubuntu乱码解决方案(全)

    转自:http://www.cnblogs.com/end/archive/2011/04/19/2021507.html ubuntu下中文乱码解决方案(全) 1.ibus输入法 Ubuntu 系统 ...

  5. android SharedPreferences 使用

    除了SQLite数据库外,SharedPreferences也是一种轻型的数据存储方式,它的本质是基于XML文件存储key-value键值 对数据,通常用来存储一些简单的配置信息.其存储位置在/dat ...

  6. 【HDOJ】1648 Keywords

    PE的注意,如果没有满足条件的不输出空格.简单模拟,暴力解. /* */ #include <iostream> #include <sstream> #include < ...

  7. SQL Server中Delete语句表名不能用别名

    delete from TABLEA A where A.FIELD1=10        (ORACLE适用)delete TABLEA from TABLEA A where A.FIELD1=1 ...

  8. 以CTE表达式实现MSSQL的字符串分割函数

    ALTER     FUNCTION [dbo].[Split] (@sep varchar(2), @s varchar(512))RETURNS tableASRETURN (    WITH P ...

  9. PopupWindow-弹窗的界面

      1 效果图 2 知识点 PopupWindow(View contentView, int width, int height) //创建一个没有获取焦点.长为width.宽为height,内容为 ...

  10. PostgreSql字符串函数和操作符

    本节描述了用于检查和操作字符串数值的函数和操作符.在这个环境中的字符串包括所有 character, character varying, text 类型的值.除非另外说明,所有下面列出的函数都可以处 ...