ios多線程操作(三)—— 線程通訊
一個進程中,線程並非單獨存在,往往需要與其他線程進行通訊以執行特定的任務。接下來就用一個簡單的例子來實現線程之間最簡單的通訊,並藉此探究一下UI控制項下得常見設定 需求:從網路上下載一張圖片在螢幕上顯示,圖片可以滾動,可以捏合縮放大小 項目開搞 建立一個新項目。 因為視圖有滾動的需求,所以需要添加一個UIScrollView以及一個顯示圖片的UIImageView
@interface ViewController () @property (nonatomic,strong) UIScrollView *scrollView;@property (weak,nonatomic) UIImageView *imgV;@end
在loadView方法裡面初始化UI控制項,將scrollView設為根視圖
- (void)loadView{ _scrollView = [[UIScrollView alloc] init]; self.view = _scrollView; self.view.backgroundColor = [UIColor brownColor]; // 初始化UIImageView UIImageView *imgV = [[UIImageView alloc] init]; [self.view addSubview:imgV]; self.imgV = imgV;}
設定scrollView的代理,圖片的縮放操作將由代理的方法來實現,在後台開啟一條線程執行下載圖片的耗時操作:
- (void)viewDidLoad { [super viewDidLoad]; _scrollView.delegate = self; // 最小縮放比例 _scrollView.minimumZoomScale = 0.5; // 最大縮放比例 _scrollView.maximumZoomScale = 2.0; // 在後台開啟一條線程執行下載圖片的耗時操作 [self performSelectorInBackground:@selector(downloadImage) withObject:nil]; // 列印所線上程 NSLog(@"viewDidLoad - %@",[NSThread currentThread]);}
將下載圖片抽取為一個方法downloadImage,該方法中實現了線程間的通訊 ———— 讓主線程更新UI,通知主線程區執行setImage:方法,通訊對象是從網路上下載的image圖片對象
- (void)downloadImage{ NSURL *url = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/4afbfbedab64034f06bd8359acc379310a551d78.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; // 更新UI都要在主線程執行 [self performSelectorOnMainThread:@selector(setImg:) withObject:image waitUntilDone:NO]; NSLog(@"downloadImage - %@",[NSThread currentThread]);}
在setImg:方法裡面讓圖片顯示
- (void)setImg:(UIImage *)img{ self.imgV.image = img; // 設定UIImageView根據image自動調節frame的大小 [self.imgV sizeToFit]; // 設定scrollView滾動範圍 self.scrollView.contentSize = img.size; NSLog(@"setImg - %@",[NSThread currentThread]);}
代理方法:
/** * 該方法返回需要縮放的view */- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{ return self.imgV;}
如此這般就簡單實現了線程間的通訊,程式會從網路上下載圖片顯示,控制台列印的結果如下
number = 1 代表在主線程上執行。 number = 2(只要number!=1)代表在子線程上執行。
耗時操作在後檯子線程執行,更新UI在主線程執行。 為何在後檯子線程執行,如果在主線程執行耗時操作,那麼該操作執行期間,主線程一直被佔用,使用者的操作就無法響應,這樣的後果可想而知。 為什麼更新UI要在主線程執行?在子線程中是不能進行UI 更新的,而可以更新的結果只是一個幻像:因為子線程代碼執行完畢了,又自動進入到了主線程,執行了子線程中的UI更新的函數棧,這中間的時間非常的短,就讓大家誤以為分線程可以更新UI。如果子線程一直在運行,則子線程中的UI更新的函數棧 主線程無法獲知,即無法更新。只有極少數的UI能更新,因為開闢線程時會擷取當前環境,如點擊某個按鈕,這個按鈕響應的方法是開闢一個子線程,在子線程中對該按鈕進行UI 更新是能及時的,如換標題,換背景圖,但這沒有任何意義。