一個進程中,線程並非單獨存在,往往需要與其他線程進行通訊以執行特定的任務。接下來就用一個簡單的例子來實現線程之間最簡單的通訊,並借此探究一下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 更新是能及時的,如換標題,換背景圖,但這沒有任何意義。