iOS编程基础-OC(十一)-Foundation框架中的系统服务:并发机制和线程
来源:互联网 发布:传奇天下轮回每层数据 编辑:程序博客网 时间:2024/06/02 06:30
该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
第11章 Foundation框架中的系统服务
11.5 并发机制和线程
使用支持并发机制和线程的类可以实现管理线程的功能,以及通过线程并行执行多个代码块的功能;
接下来介绍这些类,第17章还会详细介绍OC平台的并行编程方法;
11.5.1 线程管理
NSTask类和NSThread类:
用于管理线程和进程;
使用NSTask类可以在OC运行时系统中创建和管理进程;
NSTask实例作为独立进程进行操作,它不与其他进程共享内存;包括创建它的进程;
一个NSTask对象只能运行一次,而且其环境需要在它运行之前配置好;
比如:
我们使用下述方法,创建和启动一个任务,执行当前目录中名为greeting的程序;
NSTask * task = [NSTask launchTaskWithLaunchPath:@"./greeting" argments:nil];//OS X
也可以:
NSTask * hello = [[NSTask alloc] init];
[hello setLaunchPath:@"./greeting"];
[hello launch];
可以使用isRunning方法查询任务状态;
线程是一种操作系统机制,用于以并行的方式执行多个指令序列;
线程通常被实现为轻量级进程,因为同一个进程中可以同时存在多个线程;
同一个进程中的线程可以共享计算机中的内存和其他资源;
使用NSThread类可以创建和控制线程;
这个类中有很多方法,这些方法可以用来创建和初始化NSThread对象、启动和停止线程、配置线程以及查询线程及其执行环境;
使用NSThread的detachThreadSelector:toTarget:withObject:方法,可以创建和启动新线程;
使用setThreadPriority可以设置线程的优先权;(1.0为最高等级优先权)
使用cancel方法可以向线程发送取消信号;
-(void)testCurrentTargetSEL{ NSLog(@"Thead operation doing");}
[NSThread detachNewThreadSelector:@selector(testCurrentTargetSEL) toTarget:self withObject:nil];//创建并执行(无法获取) NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(testCurrentTargetSEL) object:nil];//创建获取 手动启动 [thread setThreadPriority:1.0]; [thread start]; [thread cancel];
log:
2017-12-25 16:24:59.920413+0800 精通Objective-C[23587:8140370] Thead operation doing
11.5.2 并行操作
NSOperation、NSBlockOperation和NSInvocationOperation类:
用于管理一个或多个操作、代码以及单独任务关联数据的并行执行过程;
操作队列:
是指提供并行执行任务功能的OC对象;
每个任务(即操作)都定义了需要执行的程序和与之相关的数据;而且会被封装在块对象或NSOperation类的具体子类中;
NSOperation是一个抽象类,用于封装单个任务的代码和相关数据;
在处理非并行任务时,具体子类通常只需要重写主要方法;
在处理并行任务时,至少必须重写start、isConcurrent、isExecuting和isFinished方法;
使用NSOperationQueue类可以通过队列系统控制NSOperation对象的执行;
操作队列会使用线程执行操作;(这类实现细节是隐式的)
如[NSOperationQueue mainQueue];
可以通过addOperation:来添加操作;
11.5.3 锁定
使用NSLock、NSDistributedLock、NSConditionLock和NSRecursiveLock类可以为同步执行的代码创建锁;
NSLock类为并行编程方式实现了一个基本的互斥锁;(互斥锁)
该类遵守NSLocking协议,因此实现了用于获取和释放锁的lock方法和unlock方法;
NSDistributedLock类:(分布锁)
定义了可由多台主机上的多个应用程序使用的锁,该锁可以控制对共享资源的访问;
NSConditionLock类:(条件锁)
定义了只能在特定条件下获取和释放的锁;
NSRecursiveLock类:(递归所)
定义了在不导致死锁的前提下,可由同一线程使用多次的锁;
该锁会记录自身被使用的次数,在释放该锁前必须先调用对应的方法解锁对象,以实现所定和解锁操作的平衡;
11.5.4 计时器和运行循环
运行循环:
是一种基于线程的机制;用于调度任务和协调收到的输入事件;
如果程序的线程 需要回应 入站事件,就必须将之附到运行循环中,以在新事件出现时唤醒该线程;
ios的UIApplication类的run方法,可以将应用程序的主循环添加到标准启动序列中,因此,使用xcode模板创建的程序,通常无需编写运行循环代码;
其他情况下,如创建命令行程序,如果你编写的程序需要回应来自输入资源的事件,就需要编写运行循环代码;
Foundation框架提供很多异步输入功能的类(如网络、数据流I/O),应该在程序中将这些类与运行循环结合起来使用;
NSRunLoop类:
提供了管理运行循环的API;
这个类含有很多方法,使用它们可以访问运行循环和模式、管理计时器和端口、运行某个循环和管理消息;
使用currentRunLoop方法可以获取当前的运行循环(代表当前线程的NSRunLoop对象);
如果线程的运行循环还不存在,该方法可以创建运行循环,并返回它;
运行模式:为运行循环定义了输入源;
Foundation框架定义了一系列用于设置可用运行循环模式的常数;
常数NSDefaultRunLoopMode是最常用的运行循环模式;
多种NSRunLoop方法运行循环;
使用run方法可以将线程添加到NSDefaultRunLoopMode实例的永久循环中,然后线程就可以在NSDefaultRunLoopMode实例的生命周期中处理所有来自输入源的事件;
如果要使用能够停止的循环,就不应该使用该方法,可以使用根据收到的输入信息或指定的时间运行相应循环的方法;
使用runModel:beforeData:方法可以在NSDefaultRunLoopMode实例中运行一次循环,在从输入源收到时间和beforeDate:参数设置的时间点之前,不接受输入信息;
示例:线程会一直等待所需输入的信息;
NSRunLoop * loop = [NSRunLoop currentRunLoop]; [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
如果[NSDate distantFuture]中的日期参数值为无穷大,线程就会一直等待所需输入的信息;
NSTimer类:
用于在经过了指定的时间后,向目标对象发送消息;
将它的实例与NSRunLoop实例一起使用,可以通过同步的方式向线程发送事件;
使用NSTimer实例可以设置运行循环对象等到输入的最长时间;
使用NSTimer类的方法可以创建和初始化计时器、启动计时器、停止计时器和获取计时器信息;
使用[NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval) invocation:(nonnull NSInvocation *) repeats:(BOOL)];方法可以创建经过指定时间后取消的计时器;
该计时器可用于调用消息设置时间间隔,并且可以在有需要时重复计时;
使用invalidate方法可以使计时器不再启动;
isValid方法能够返回表明当前计时器是否合法的布尔值;
说了这么多,接下来实践一下;
11.5.5 创建Bonjour网络服务客户端
接下来将编写一个使用Bonjour网络服务API和运行循环的程序;
该程序会创建一个服务端浏览器,后者会以异步方式查找指定类型的已注册服务;
我们新建一个命令行应用程序命名为C11BonjourClient;
接下来在工程目录中创建一个使用加载URL的API下载URL资源的类:C11BonjourClient类;
更新相关代码如下:
(Code C11BonjourClient.h C11BonjourClient.m)
注意以下代码是在单独的一个工程中的;
#import <Foundation/Foundation.h>@interface C11BonjourClient : NSObject<NSNetServiceBrowserDelegate>@property (retain) NSNetServiceBrowser * serviceBrowser;@property BOOL finishedLoading;@end
#import "C11BonjourClient.h"@implementation C11BonjourClient-(instancetype)init{ if (self = [super init]) { _finishedLoading = NO; _serviceBrowser = [[NSNetServiceBrowser alloc] init];//浏览器实例 [_serviceBrowser setDelegate:self];//浏览器实例的代理设置为当前对象 } return self;}#pragma mark -#pragma mark NSNetServiceBrowserDelegate methods-(void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser{ NSLog(@"Begin searching");}-(void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindDomain:(NSString *)domainString moreComing:(BOOL)moreComing{ }-(void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing{ NSLog(@"Find server:%@",service); if (!moreComing) {//条件判断:服务浏览器是否正在等待其他服务 //没有心的服务 停止搜索 [self.serviceBrowser stop]; }}-(void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser{ //停止搜索,将标记设置为退出循环运行 NSLog(@"Stop searching"); self.finishedLoading = YES;}@end
分析:
C11BonjourClient类的接口采用NSNetServiceBrowserDelegate协议,因此会通过异步的方式从NSNetServiceBrowser实例加载数据;
属性serverBrowser属性就是NSNetServiceBrowserDelegate实例;
finishedLoading属性用于指明NSNetServiceBrowser实例完成数据加载的时间;
当服务浏览器实例开始搜索服务时,会调用相应的回调方法;
编写好了C11BonjourClient类,接下来我们通过Bonjour协议使用该类搜索已注册的服务;
更新该工程的main函数中代码如下:
(Code main.m)
#import <Foundation/Foundation.h>#import "C11BonjourClient.h"int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); //获取当前的运行循环查看信息 NSRunLoop * loop = [NSRunLoop currentRunLoop]; //创建一个客户端,并将其服务浏览器实例添加到当前的运行循环中 C11BonjourClient * client = [[C11BonjourClient alloc] init]; //查找指定的服务类型 [client.serviceBrowser searchForServicesOfType:@"_ipp._tcp." inDomain:@"local."]; //一直循环,直到服务浏览器停止 while (!client.finishedLoading && [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { } } return 0;}
表达式[loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]会执行一次运行循环,阻止从输入源接收其他信息;
接收信息后,该消息会在当前线程(即被调用的相应委托方法)中被处理,而且循环会再次开始运行;
因此在设置finishedLoading之前,不会退出while循环;
这个练习展示了如何编写一个使用Foundation框架API以异步的方式寻找已注册服务的程序;
这个程序并不完整,已注册服务可以自行实现类进行测试,书中内容至此结束,待后续可查找资料补充实现,进行测试;
接下来研究处理url的方式。