深入淺出iOS多線程(二)——pthraed和NSThread的使用

深入淺出iOS多線程(一)——線程的概念
深入淺出iOS多線程(二)——pthraed和NSThread的使用
深入淺出iOS多線程(三)——GCD多線程
深入淺出iOS多線程(四)——NSOperation多線程
深入淺出iOS多線程(五)——多線程鎖

pthread

pthread簡(jiǎn)介

pthread 是屬于 POSIX 多線程開(kāi)發(fā)框架,POSIX表示可移植操作系統(tǒng)接口(Portable Operating System Interface of UNIX,縮寫(xiě)為 POSIX ),如果想學(xué)習(xí)這套API,在網(wǎng)上是可以找到相關(guān)的資料等,由于在iOS中有NSThrad,如果不考慮移植性,那么在iOS開(kāi)發(fā)中基本上不回去使用,所以只是了解,pthread是多線程的一種技術(shù)實(shí)現(xiàn)。

iOS中的pthread

在iOS中需要導(dǎo)入頭文件pthread.h才能夠使用pthrad的Api

#import <pthread.h>

pthraed的特點(diǎn)

  • 一套通用的多線程API
  • 跨平臺(tái)可移植
  • 使用難度比較大
  • 基于C語(yǔ)言的開(kāi)發(fā)

pthread的簡(jiǎn)單使用

/**
 參數(shù):
 1.指向線程標(biāo)示的指針
 2.線程的屬性
 3.指向函數(shù)的指針
 4.傳遞給該函數(shù)的參數(shù)
 
 返回值
 - 如果是0,標(biāo)示正確
 - 如果非0,標(biāo)示錯(cuò)誤代碼
 
 void *   (*)      (void *)
 返回值   (函數(shù)指針)  (參數(shù))
 void *  和OC中的  id 是等價(jià)的!
 
 */
    
pthread_t pthreadId ;
    
NSString *str = @"敲代碼";
    
int result = pthread_create(&pthreadId, 
                                  NULL, 
                                &doing, 
                 (__bridge void *)(str)
                           );
    
if(result == 0){
    NSLog(@"開(kāi)啟成功");
}else{
    NSLog(@"開(kāi)啟失敗");
}


void * doing(void * param){
    
    NSLog(@"%@,%@",[NSThread currentThread],param);
    return NULL;
}

NSThread

iOS的多線程N(yùn)SThread簡(jiǎn)介

NSThread是蘋(píng)果官方提供面向?qū)ο蟛僮骶€程的技術(shù),簡(jiǎn)單方便,可以直接操作對(duì)象,需要手動(dòng)控制線程的生命周期,平時(shí)iOS開(kāi)發(fā)較少使用,使用最多的是獲取當(dāng)前線程

NSThread特點(diǎn)

  • 面向?qū)ο蟮亩嗑€程編程
  • 簡(jiǎn)單易用,可直接操作線程對(duì)象
  • 需要手動(dòng)管理線程的生命周期

NSThread的詳細(xì)使用介紹

如何開(kāi)啟NSThread線程

NSThread初始化API

//初始化的API
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument 
- (instancetype)initWithBlock:(void (^)(void))block 

//類對(duì)象方法
+ (void)detachNewThreadWithBlock:(void (^)(void))block 
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

上述實(shí)例方法以及類對(duì)象方法,一樣都是創(chuàng)建一個(gè)新的線程,不一樣的是,類對(duì)象方法不需要?jiǎng)?chuàng)建完成以后調(diào)用start方法,而alloc創(chuàng)建的線程需要手動(dòng)start開(kāi)啟。

NSThread代碼實(shí)現(xiàn)

//方法一:
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo1Doing:) object:@"hello"];
[thread start];
    
//方法二:
NSThread *thread1 = [[NSThread alloc]initWithBlock:^{
    NSLog(@"%s",__func__);
}];
[thread1 start];
    
//方法三:
[NSThread detachNewThreadSelector:@selector(demo1Doing:) toTarget:self withObject:@"hello"];
    
//方法四
[NSThread detachNewThreadWithBlock:^{
    NSLog(@"%s",__func__);
}];

NSThread主線程的API和獲取主線程

  • 判斷是否是主線程,獲取主線程

    + (NSThread *)mainThread; // 獲得主線程
    - (BOOL)isMainThread; // 是否為主線程
    + (BOOL)isMainThread; // 是否為主線程
    
  • 獲取當(dāng)前線程

    NSThread *current = [NSThread currentThread];
    
    
  • 設(shè)置和獲取線程的名字

    - (void)setName:(NSString *)n;
    - (NSString *)name;
    

其他創(chuàng)建線程的方式

  • 自啟動(dòng)線程

    [NSThread detachNewThreadSelector:@selector(demo1Doing:) toTarget:self withObject:@"hello"];
        
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"%s",__func__);
    }];
    
  • 隱式創(chuàng)建并啟動(dòng)線程

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
    

總結(jié)

上述兩種創(chuàng)建線程方式的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):簡(jiǎn)單快捷
  • 缺點(diǎn):無(wú)法對(duì)線程進(jìn)行更詳細(xì)的設(shè)置和管理

控制線程狀態(tài)

  • 啟動(dòng)線程

    // 進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)。當(dāng)線程任務(wù)執(zhí)行完畢,自動(dòng)進(jìn)入死亡狀態(tài)
    - (void)start; 
    
  • 阻塞線程

    //進(jìn)入阻塞線程狀態(tài)  休眠
    + (void)sleepUntilDate:(NSDate *)date;
    + (void)sleepForTimeInterval:(NSTimeInterval)ti;
    
  • 強(qiáng)制停止線程

    //進(jìn)入死亡狀態(tài)
    + (void)exit;
    

線程的優(yōu)先級(jí)

  • 線程的優(yōu)先級(jí)API

    + (double)threadPriority;
    + (BOOL)setThreadPriority:(double)p;
    
  • 優(yōu)先級(jí)設(shè)置

    • Priorit的值 0.0~1.0之間
    • 優(yōu)先級(jí)只能保證CPU調(diào)度的可能性會(huì)高,歸根究底你還是無(wú)法控制多線程的順序,如果就靠?jī)?yōu)先級(jí),來(lái)誤認(rèn)為控制多線程額順序是不嚴(yán)謹(jǐn)?shù)摹?
      • 多線程的目的:是不阻塞UI線程
      • 建議不要修改優(yōu)先級(jí)
      • 多線程開(kāi)發(fā)中不能相信一次的運(yùn)行結(jié)果
      • 優(yōu)先級(jí)翻轉(zhuǎn),優(yōu)先級(jí)低的任務(wù)太耗時(shí)放到最后面,然后后面排的任務(wù)比較多,優(yōu)先級(jí)高的任務(wù)被堵死了

多線程的安全隱患問(wèn)題

安全隱患?

  • 資源共享
    • 一塊資源可能會(huì)被多個(gè)線程共享,也就是說(shuō)多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
    • 例如:多個(gè)線程同時(shí)訪問(wèn)修改同一個(gè)變量、同一個(gè)文件、同一個(gè)對(duì)象
  • 數(shù)據(jù)錯(cuò)亂
    • 同一個(gè)線程修改一個(gè)數(shù)據(jù),不同線程不同的運(yùn)行結(jié)束時(shí)間,有可能得到不一樣結(jié)果

安全隱患問(wèn)題分析

  • ThreadA去訪問(wèn)一塊內(nèi)容中的integer數(shù)據(jù),得到數(shù)據(jù)17,17+1 = 18寫(xiě)入內(nèi)存
  • ThreadB也在同一時(shí)間訪問(wèn)了integer數(shù)據(jù),得到數(shù)據(jù)17,17+1 = 18寫(xiě)入內(nèi)存
  • 結(jié)果非常有意思的是+1了兩次應(yīng)該是19才對(duì),最終結(jié)果是18,這就是多線程的安全隱患問(wèn)題,如下圖所示
    安全隱患.png

如何解決多線程的安全隱患(線程鎖)

互斥鎖

  • 當(dāng)ThreadA去訪問(wèn)一塊內(nèi)容中的integer數(shù)據(jù)的時(shí)候,首先上一把鎖lock得到數(shù)據(jù)17,17+1 = 18寫(xiě)入內(nèi)存,最后在unlock

  • ThreadB也在同一時(shí)間訪問(wèn)了integer數(shù)據(jù):

    • 由于ThreadA已經(jīng)在數(shù)據(jù)上面加了鎖lock,所以必須等到ThreadA完成以后才能去訪問(wèn)這個(gè)數(shù)據(jù)

    • ThreadA完成,訪問(wèn)integer數(shù)據(jù)時(shí),時(shí)候lock然后在獲取數(shù)據(jù)18,18+1 = 19寫(xiě)入內(nèi)存,最后在unlock

      //互斥鎖
      @synchronized (self) {
      }
      ``  
      
      
    • @synchronized的參數(shù):

      • 任意OC對(duì)象都可以加鎖
      • 加鎖一定要加鎖共有的對(duì)象,一般用self
  • 互斥鎖的優(yōu)缺點(diǎn):

    • 優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
    • 缺點(diǎn):需要消耗大量的CPU資源
  • 互斥鎖的使用前提

    • 多條線程搶奪同一塊資源
  • 線程同步

    • 線程同步的意思就是:多條線程同一條線上工作(按順序地執(zhí)行任務(wù))
    • 互斥鎖,就使用了線程同步技術(shù)

互斥鎖需要注意的地方

  • 保證代碼內(nèi)的代碼,同一時(shí)間,只有一條線程執(zhí)行

  • 互斥鎖的范圍應(yīng)該盡量小,范圍大了,效率就差

  • 結(jié)果本身是一個(gè)多線程開(kāi)發(fā),最后結(jié)果變成了同步去執(zhí)行,互斥鎖,也就是同步線程技術(shù),如下圖所示:

    互斥鎖.png

如何解決多線程的安全隱患(原子與非原子對(duì)象)

nonatomic 非原子屬性

  • 不會(huì)為setter方法加鎖
  • 非原子屬性,因?yàn)?code>atomic所有對(duì)這個(gè)對(duì)象的操作之前會(huì)加鎖,所以會(huì)很耗費(fèi)資源,在沒(méi)有安全隱患的問(wèn)題上在加鎖,是不必要的

atomic 原子屬性

  • 為setter方法加鎖(默認(rèn)是atomic)

  • 原子屬性,保證這個(gè)屬性的安全性(線程安全),多線程寫(xiě)入這個(gè)對(duì)象的時(shí)候,保證同一時(shí)間只有一個(gè)線程能夠執(zhí)行!

    • 模擬一個(gè)atomic原子屬性
    //模擬原子屬性
    - (void)setMyAtomic:(NSObject *)myAtomic{
        @synchronized (self) {
            _myAtomic = myAtomic;
        }
    }
    
    • 實(shí)際上,原子屬性內(nèi)部有一個(gè)鎖,自旋鎖:
    • 自旋鎖和互斥鎖不一樣的地方
      • 共同點(diǎn):都能夠保證線程的安全
      • 不同點(diǎn):互斥鎖:如果線程被鎖到外面,線程就會(huì)進(jìn)入休眠狀態(tài),等待鎖打開(kāi),打開(kāi)之后被喚醒;自旋鎖:如果線程被鎖在外面,就會(huì)用死循環(huán)的方式,一直等待鎖打開(kāi)。
    • 無(wú)論什么鎖,都會(huì)消耗新能,效率不高
    • 線程安全
    • 在多個(gè)線程進(jìn)行讀寫(xiě)操作時(shí),仍然保證數(shù)據(jù)正確
  • UI線程

    • 共同的約定,所有更新UI的操作都放在主線程執(zhí)行
    • 因?yàn)閁IKit 框架都是線程不安全的(因?yàn)榫€程安全效率低下)
  • nonatomic和atomic對(duì)比

    • atomic:線程安全,需要消耗大量的資源
    • nonatomic:非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備
  • iOS開(kāi)發(fā)使用nonatomic和atomic

    • 所有屬性都應(yīng)聲明nonatomic
    • 盡量避免多線程搶奪同一塊資源
    • 盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器處理,減小移動(dòng)客戶端的壓力

注意一個(gè)小細(xì)節(jié)

  • OC中:定義一個(gè)屬性,通常會(huì)生成成員變量,如果同時(shí)重寫(xiě)了getter、setter那么成員變量就不會(huì)自動(dòng)生成
    • 如果想要同時(shí)重寫(xiě)了getter、setter,那么就直接使用 @synthesize myAtomic = _myAtomic;

NSThread自定義

在NSThread的有init初始化方法:

//用alloc init 適用于自定義NSThread (子類)
NSThread * t = [[NSThread alloc]init];
需要?jiǎng)?chuàng)建一個(gè)新的子類繼承NSThread方法,然后重寫(xiě)main方法

多線程下載網(wǎng)絡(luò)圖片

-(void)loadView{
}

如果重新了上述方法,SB和XIB都無(wú)效

代碼如下:

#import "ViewController.h"

@interface ViewController ()<UIScrollViewDelegate>
@property(nonatomic,strong)UIScrollView * scrollView;
@property(nonatomic,weak) UIImageView * imageView;
@property(nonatomic,strong) UIImage * image;
@end

@implementation ViewController


/**
 加載視圖結(jié)構(gòu)的,純代碼開(kāi)發(fā)
 功能 SB&XIB 是一樣
 如果重寫(xiě)了這個(gè)方法,SB和XIB 都無(wú)效
 */
-(void)loadView{
    //搭建界面
    self.scrollView = [[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.view = self.scrollView;
    //MARK:- 設(shè)置縮放屬性
    self.scrollView.delegate = self;
    self.scrollView.minimumZoomScale = 0.1;
    self.scrollView.maximumZoomScale = 2.0;
    
    
    //imageView
    UIImageView * iv = [[UIImageView alloc]init];
    //會(huì)調(diào)用View的getter方法. loadView方法在執(zhí)行的過(guò)程中!如果self.view == nil,會(huì)自動(dòng)調(diào)用loadView加載!
    [self.view addSubview:iv];
    self.imageView = iv;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage) object:nil];
    [t1 start];
    

}

//MARK: - 下載圖片
-(void)downloadImage{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    //NSURL -> 統(tǒng)一資源定位符,每一個(gè)URL 對(duì)應(yīng)一個(gè)網(wǎng)絡(luò)資源!
    NSURL * url = [NSURL URLWithString:@"https://images.unsplash.com/photo-1496840220025-4cbde0b9df65?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80"];
    
    //下載圖片(在網(wǎng)絡(luò)上傳輸?shù)乃袛?shù)據(jù)都是二進(jìn)制!!)
    //為什么是二進(jìn)制:因?yàn)槲锢韺?!是網(wǎng)線!!網(wǎng)線里面是電流!!電流有高低電頻!!高低電頻表示二進(jìn)制!!!
    NSData * data = [NSData dataWithContentsOfURL:url];
    
    //將二進(jìn)制數(shù)據(jù)轉(zhuǎn)成圖片并且設(shè)置圖片
    //提示:不是所有的更新UI在后臺(tái)線程支持都會(huì)有問(wèn)題!!!
    //重點(diǎn)提示:不要去嘗試在后臺(tái)線程更新UI!!!出了問(wèn)題是非常詭異的!!
    //    self.image = [UIImage imageWithData:data];
    
    //在UI線程去更新UI
    /**
     * 1.SEL:在主線程執(zhí)行的方法
     * 2.傳遞給方法的參數(shù)
     * 3.讓當(dāng)前線程等待 (注意點(diǎn)!! 如果當(dāng)前線程是主線程!哥么YES沒(méi)有用!!)
     */
    // 線程間通訊
    [self performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageWithData:data] waitUntilDone:NO];
    
    
    
}


//這種寫(xiě)法 省略一個(gè) _image ,主要原因是因?yàn)閕mage 保存在了imageView里面了!
-(UIImage *)image{
    return self.imageView.image;
}


-(void)setImage:(UIImage *)image{
    NSLog(@"更新 UI 在====%@",[NSThread currentThread]);
    //直接將圖片設(shè)置到控件上
    self.imageView.image = image;
    //讓imageView和image一樣大
    [self.imageView sizeToFit];
    //指定ScrollView 的contentSize
    self.scrollView.contentSize = image.size;
    
    NSLog(@"\n\n\n\n\n\n\n\n\n\n\n%@",self.image);
}

#pragma mark - <scrollView代理>
//告訴 ScrollView 縮放哪個(gè)View
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
    return self.imageView;
}

/**
 * transform 矩陣
 *  CGFloat a(縮放比例), b, c, d(縮放比例);  共同決定角度!
 *  CGFloat tx(x方向位移), ty(y方向的位移);
 
 *
 */
-(void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    NSLog(@"%@",NSStringFromCGAffineTransform(self.imageView.transform));
}

@end

  • 提示:不是所有的更新UI在后臺(tái)線程支持都會(huì)有問(wèn)題!!!

  • 重點(diǎn)提示:不要去嘗試在后臺(tái)線程更新UI!!!出了問(wèn)題是非常詭異的!!

  • 在UI線程去更新UI

     /**
     * 1.SEL:在主線程執(zhí)行的方法
     * 2.傳遞給方法的參數(shù)
     * 3.是否讓當(dāng)前線程等待 (注意點(diǎn)!! 如果當(dāng)前線程是主線程!YES沒(méi)有用!!)
     * NO當(dāng)前線程不需要等待@selector(setImage:)執(zhí)行完成,YES當(dāng)前線程需要等待@selector(setImage:)執(zhí)行完成
     */
     // 線程間通訊
    [self performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageWithData:data] waitUntilDone:NO];
    

線程間的通信

@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
@end

NSPort實(shí)現(xiàn)線程通信

代碼如下:

@interface ViewController () <NSPortDelegate>
@property (nonatomic, strong) NSPort* subThreadPort;
@property (nonatomic, strong) NSPort* mainThreadPort;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.mainThreadPort = [NSPort port];
    self.mainThreadPort.delegate = self;
    [[NSRunLoop currentRunLoop] addPort:self.mainThreadPort forMode:NSDefaultRunLoopMode];
    [self task];
}

- (void) task {
    NSThread* thread = [[NSThread alloc] initWithBlock:^{
        self.subThreadPort = [NSPort port];
        self.subThreadPort.delegate = self;
        
        [[NSRunLoop currentRunLoop] addPort:self.subThreadPort forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    }];
    [thread setName:@"子線程"];
    [thread start];
}

- (void)handlePortMessage:(id)message {
    NSLog(@"%@", [NSThread currentThread]);
    
    if (![[NSThread currentThread] isMainThread]) {
        NSMutableArray* sendComponents = [NSMutableArray array];
        NSData* data = [@"world" dataUsingEncoding:NSUTF8StringEncoding];
        [sendComponents addObject:data];
        [self.mainThreadPort sendBeforeDate:[NSDate date] components:sendComponents from:self.subThreadPort reserved:0];
        return;
    }
    sleep(2);
    NSMutableArray* components = [message valueForKey:@"components"];
    
    if ([components count] > 0) {
        NSData* data = [components objectAtIndex:0];
        NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@", str);
    }

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSMutableArray* components = [NSMutableArray array];
    NSData* data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    [components addObject:data];
    
    [self.subThreadPort sendBeforeDate:[NSDate date] components:components from:self.mainThreadPort reserved:0];
}

@end

NSThread需要注意的地方

子線程執(zhí)行太快,還會(huì)調(diào)用線程通信的代碼嗎?

不會(huì)
有時(shí)候會(huì)出現(xiàn)這個(gè)問(wèn)題,代碼如下:

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    //不執(zhí)行地方原因,是因?yàn)?demo 方法執(zhí)行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];
    
-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
}
-(void)otherMethod{

    self.finished = YES;
}
  • Demo執(zhí)行額太快,因?yàn)樽泳€程是沒(méi)有RunLoop的,當(dāng)demo執(zhí)行完成以后就消失了,所以不會(huì)在執(zhí)行otherMethod

如何解決上述問(wèn)題

在子線程開(kāi)啟RunLoop,

    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    //不執(zhí)行地方原因,是因?yàn)?demo 方法執(zhí)行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];
    
-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
   [[NSRunLoop currentRunLoop] run];
}
-(void)otherMethod{
    NSLog(@"%s %@",__FUNCTION__,[NSThread currentThread]);
}
  • RunLoop開(kāi)啟了循環(huán),這樣就會(huì)無(wú)限制的進(jìn)行循環(huán),這樣這個(gè)子線程就永遠(yuǎn)不會(huì)釋放

  • 改進(jìn)的辦法就是,從外面創(chuàng)建一個(gè)BOOL來(lái)判斷是否需要關(guān)閉RunLoop

@interface ViewController ()
/** 循環(huán)條件 */
@property(assign,nonatomic,getter=isFinished)BOOL finished;
@end

@implementation ViewController


    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
    [t1 start];
    
    self.finished = NO;
    
    //不執(zhí)行地方原因,是因?yàn)?demo 方法執(zhí)行的快!""
    [self performSelector:@selector(otherMethod) onThread:t1 withObject:nil waitUntilDone:NO];

-(void)demo{
    NSLog(@"%@",[NSThread currentThread]);
    //啟動(dòng)當(dāng)前RunLoop  哥么就是一個(gè)死循環(huán)!!
    //使用這種方式,可以自己創(chuàng)建一個(gè)線程池!
    //    [[NSRunLoop currentRunLoop] run];
    
    //在OC中使用比較多的,退出循環(huán)的方式!
    while (!self.isFinished) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
}
-(void)otherMethod{
    for (int i = 0; i<10; i++) {
        
        NSLog(@"%s %@",__FUNCTION__,[NSThread currentThread]);
    }
    self.finished = YES;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言: 最近想回顧一下多線程問(wèn)題,看到一篇文章寫(xiě)的非常詳細(xì),為了便于以后查找以及加深印象,就照著原文摘錄了下文,原...
    FM_0138閱讀 1,078評(píng)論 1 1
  • iOS開(kāi)發(fā)中,多線程相關(guān)的知識(shí)點(diǎn)主要包括pthread、NSThread、NSOperation和GCD,我們經(jīng)常...
    Neebel閱讀 2,850評(píng)論 0 2
  • 總結(jié)ios開(kāi)發(fā)中一些知識(shí),有錯(cuò)或者有問(wèn)題的歡迎交流。 這些資料也是看其他的一些文章總結(jié)的,首先把別人文章地址貼出來(lái)...
    尋找最亮的光閱讀 561評(píng)論 0 1
  • 1、進(jìn)程 1)進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動(dòng),它是操作系統(tǒng)分配資源的基本單...
    Crics閱讀 557評(píng)論 0 0
  • 久違的晴天,家長(zhǎng)會(huì)。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,805評(píng)論 16 22

友情鏈接更多精彩內(nèi)容