1.NSThread基本运用
app启动时默认都是,包括ViewController里执行的都是在主线程执行的
当计算了过大的时候会造成线程阻塞,比如点击按钮执行下面的方法,只有计算完,结束后,程序才能继续执行,如果是死循环的话,会一直占用主线程,导致主线程无法继续进行
-(void)pressBtn:(UIButton*)btn
{
int i=0;
while (true)
{
i++;
NSLog(@"i=%d",i);
if (i==10000)
{
//主线程,只有执行了10000次循环才会退出这个方法,继续向下执行
break;
}
}
}
这样就需要开启另外一个线程,开启的方式有两种
第一种:alloc init 需要手动的start 和cancel
-(void)pressBtnMuti:(UIButton*)btnMuti
{
//创建一个多线程对象
NSThread*thread=[[NSThread alloc]initWithTarget:self selector:@selector(thread01) object:nil];
//启动线程
[thread start];
}
-(void)thread01
{
int i=0;
while (true)
{
i++;
NSLog(@"in thread i=%d",i);
[NSThread sleepForTimeInterval:1];
if (i>=10000)
{
//直接结束当前的线程
//+方法
//会判断是哪一个线程来执行的这个函数
//线程是由操作系统来管理
//向操作系统发送通知,要求结束当前的进程
[NSThread exit];
//获取当前线程对象
NSThread*curThread=[NSThread currentThread];
//通知操作从系统来取消当前线程的执行
[curThread cancel];
}
}
}
第二种创建方式,直接开启并执行的一个线程,执行完方法程自动关闭
-(void)pressBtn{
[NSThread detachNewThreadSelector:@selector(thread02) toTarget:self withObject:nil];
}
-(void)thread02{
NSLog(@"线程二执行");
}
2.线程执行顺序
当一个方法同上开启另个线程,是执行顺序是不确定的,又系统资源决定的
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//开始执行线程1
[NSThread detachNewThreadSelector:@selector(threadCount01) toTarget:self withObject:nil];
//线程二:
[NSThread detachNewThreadSelector:@selector(threadCount02) toTarget:self withObject:nil];
}
以下执行方法
conut1 final=200?
conut1 final=200?
不确定的,因为都是异步线程执行的方法,谁先执行完不确定,所以conut1 ,count2只有一个等于200,另一个不确定
而且每次run 结果极有可能不一样,考虑线程安全,需要上锁
-(void)threadCount01
{
for (int i=0; i<100; i++)
{
//[_lock lock];
_count++;
//[_lock unlock];
NSLog(@"Count=%d",_count);
}
NSLog(@"conut1 final=%d",_count);
}
-(void)threadCount02
{
for (int i=0; i<100; i++)
{
[_lock lock];
_count++;
[_lock unlock];
NSLog(@"Count=%d",_count);
}
NSLog(@"conut2 final=%d",_count);
}
同样的例子更加可以说明,线程是有操作系统资源决定
-(void)pressBtnMuti:(UIButton*)btn
{
NSThread*threadAdd=[[NSThread alloc]initWithTarget:self selector:@selector(threadAdd) object:nil];
[threadAdd start];
[NSThread detachNewThreadSelector:@selector(threadMinus) toTarget:self withObject:nil];
}
-(void)threadAdd
{
while (true)
{
[_lock lock];
_value++;
[_lock unlock];
NSLog(@"value++=%d",_value);
[NSThread sleepForTimeInterval:arc4random()%5];
//[NSThread sleepForTimeInterval:1];
}
}
-(void)threadMinus
{
while (true)
{
[_lock lock];
_value--;
[_lock unlock];
NSLog(@"value--=%d",_value);
[NSThread sleepForTimeInterval:arc4random()%5];
//[NSThread sleepForTimeInterval:1];
}
}
执行结果等一段时间 value的值是不确定的
3.关于线程锁,多个线程对同一个数组进行访问,读写操作都要考虑加锁
//不考虑线程安全
-(void)addElment:(NSObject*)elem
{
if (elem==nil)
return ;
// if ([_arrayQueue count]==0)
// {
// _arrayQueue=[[NSMutableArray alloc]init];
// }
if ([_arrayQueue count]>KMaxArrayQueue) {
return ;
}
[_arrayQueue addObject:elem];
}
//不考虑线程安全
-(id)getElement
{
if ([_arrayQueue count]==0)
return nil;
id obj=[_arrayQueue objectAtIndex:0];
[_arrayQueue removeObjectAtIndex:_arrayQueue.count-1];
return obj;
}
-(void)addElmentThreadSafty:(id)obj
{
[_lock lock];
[self addElment:obj];
[_lock unlock];
}
-(id)getElementThreadSafty
{
[_lock lock];
id obj= [self getElement];
[_lock unlock];
return obj;
}
4.线程可以手动的取消
- (void)run
{
for(int i = 0 ; i < 100 ; i++)
{
if([NSThread currentThread].isCancelled)
{
// 终止当前正在执行的线程
[NSThread exit];
}
NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
// 每执行一次,线程暂停0.5秒
[NSThread sleepForTimeInterval:0.5];
}
}
- (IBAction)cancelThread:(id)sender
{
// 取消thread线程,调用该方法后,thread的isCancelled方法将会返回NO
[thread cancel]; // ①
}
5.线程的优先级, 改变线程的优先级,可以改变输出顺序
优先级相同,输出不确定,当不一样的时候优先级高的for循环先执行完,然后执行另外优先级低的for循环
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"UI线程的优先级为:%g" , [NSThread threadPriority]);//0.5
// 创建第一个线程对象
NSThread* thread1 = [[NSThread alloc]
initWithTarget:self selector:@selector(run) object:nil];
// 设置第一个线程对象的名字
thread1.name = @"线程A";
NSLog(@"线程A的优先级为:%g" , thread1.threadPriority);
// 设置使用最低优先级
thread1.threadPriority = 1.0;
// 创建第二个线程对象
NSThread* thread2 = [[NSThread alloc]
initWithTarget:self selector:@selector(run) object:nil];
// 设置第二个线程对象的名字
thread2.name = @"线程B";
NSLog(@"线程B的优先级为:%g" , thread2.threadPriority);
// 设置使用最高优先级
thread2.threadPriority = 0;
// 启动2个线程
//改变线程的优先级,可以改变输出顺序
[thread1 start];
[thread2 start];
}
- (void)run
{
for(int i = 0 ; i < 100 ; i++)
{
NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
}
}
6.应用举例1
在一个循环(for i in 20)里开启另外一个线程,另外一个线程执行一个(for i in 5),请问打印结果?
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"===%@" , [NSThread currentThread]);//其实就是主线程
// ===<NSThread: 0x7fea61d130b0>{number = 1, name = main}
for(int i = 0 ; i < 20 ; i++) {
NSLog(@"===%d" , i);
if(i == 5) {
// 创建线程对象
NSThread *thread = [[NSThread alloc]initWithTarget:self
selector:@selector(run) object:nil];
// 启动新线程
NSLog(@"thread==%@",thread);
//thread==<NSThread: 0x7facaa478610>{number = 2, name = main}
[thread start];
// // 创建并启动新线程
// [NSThread detachNewThreadSelector:@selector(run) toTarget:self
// withObject:nil];
}
}
}
- (void)run
{
for(int i = 0 ; i < 5 ; i++) {
NSLog(@"-----%@----%d" , [NSThread currentThread].name, i);
}
}
打印结果:
如果再次run一下,打印结果为:
为何不一样呢,因为开启的线程,不是在主线程,而且是异步执行,什么时候开始执行,取决于系统资源,所以会不一致
7.应用举例2
利用NSThread如何实现适时更改UIProgressView的进度条呢?,
方法:开启一个异步线程,执行方法改变progress,执行主线程方法,改变UI,属于异步执行,同步UI
//使用主线程来调用函数
-(void)pressMian{
//参数二:函数中的参数
//参数三:是否等待主线程空余时执行
//如果为NO 不需要等待
[self performSelectorOnMainThread:@selector(updateProgressMain:) withObject:_progressView waitUntilDone:NO];
}
//主线程主使用
//方法一:
-(void)updateProgressMain:(UIProgressView*)pView
{
while (_progressView.progress<1)
{
_progressView.progress+=0.001;
NSLog(@"_progress=%f",_progressView.progress);
}
}
#pragma mark 开启一个异步线程,执行方法改变progress,执行主线程方法,改变UI,属于异步执行,同步UI
//方法二:
-(void)pressMian2
{
[NSThread detachNewThreadSelector:@selector(updateProgress02) toTarget:self withObject:nil];
}
-(void)updateProgress02
{
while (_curProgress<=1){
_curProgress+=0.0001;
//获得主线程
NSThread*mainT=[NSThread mainThread];
//执行一个函数,指定某个线程来执行某个函数
[self performSelectorOnMainThread:@selector(changeProgress) withObject:mainT waitUntilDone:YES];
}
}
//主线程中执行函数
-(void)changeProgress
{
_progressView.progress=_curProgress;
NSLog(@"_progress=%f",_progressView.progress);
if (_progressView.progress==1)
{
_progressView.progress=0;
return;
}
}
#pragma mark 开启普通线程,进度条会在progress=1是突然出现
//开启普通线程
-(void)pressThread{
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(updateProgress) object:nil];
[thread start];
}
//普通线程函数
-(void)updateProgress {
while (_progressView.progress<1.0f){
_progressView.progress+=0.0001;
NSLog(@"_progress=%f",_progressView.progress);
if (_progressView.progress==1){
return;
}
}
}
8应用举例3
异步下载一张图片,同步更新Image
- (IBAction)showImage:(id)sender
{
NSString* url = @"http://www.crazyit.org/logo.jpg";
// 创建新线程对象
NSThread *thread = [[NSThread alloc]initWithTarget:self
selector:@selector(downloadImageFromURL:) object:url];
// 启动线程
[thread start];
}
// 定义一个方法作为线程执行体。
-(void)downloadImageFromURL:(NSString *) url
{
// 从网络获取数据
NSData *data = [[NSData alloc]
initWithContentsOfURL:[NSURL URLWithString:url]];
// 将网络数据初始化为UIImage对象
UIImage *image = [[UIImage alloc]initWithData:data];
if(image != nil)
{
// 在主线程中执行updateUI:方法
[self performSelectorOnMainThread:@selector(updateUI:)
withObject:image waitUntilDone:YES]; // ①
}
else
{
NSLog(@"---下载图片出现错误---");
}
}
-(void)updateUI:(UIImage*) image
{
self.iv.image = image;
}
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>