2

我的程序「ThreadsNQueues」 - 見下文 - 在後臺線程中同時使用「A」和「B」填充文本行。當一個文本行被填充10個字符時,它會被附加到一個輸出文本緩衝區(這是一個NSMutableString),並且這個緩衝區應該被寫入UITextView(self.outView)。如何從線程更新UI

問題:行self.outView.text=_output;永遠蜂執行:-(對不起,我完全新的多線程在iOS(模擬器上的iOS 9.2測試)...

所以我的問題是:什麼是不對的代碼,我怎麼可以做的更好(GCD是必需的)

#import "ViewController.h" 

@interface ViewController() 

@end 

@implementation ViewController 

NSMutableString *_output; 
NSMutableString *_line; 
NSLock* _lineLock; 
dispatch_queue_t _myQueue; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    _output=[NSMutableString string]; 
    _lineLock=[[NSLock alloc] init]; 
    _line=[NSMutableString string]; 
    _myQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     do { 
      dispatch_async(_myQueue, ^{ 
       if([_lineLock tryLock]) { 
        [self append:'A']; 
        [_lineLock unlock]; 
       } 
      }); 
      dispatch_async(_myQueue, ^{ 
       if([_lineLock tryLock]) { 
        [self append:'B']; 
        [_lineLock unlock]; 
       } 
      }); 
     } while (true); 
    }); 
} 

- (void)append:(char)c { 
    [_line appendFormat:@"%c",c]; 
    if (_line.length==10) { 
     // line is complete 
     [_line appendString:@"\n"]; 
     [_output appendString:_line]; 
     NSLog(@"%@",_line); 
     [_line setString:@""]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // update ui (never called !!!) 
      self.outView.text=_output; 
     }); 
    } 
} 

@end 

在Xcode控制檯日誌看起來不錯:?

2016-02-14 09:09:38.607 ThreadsNQueues[2807:62067] BAABAAAAAB 
2016-02-14 09:09:38.611 ThreadsNQueues[2807:62067] BBAAAABAAA 
2016-02-14 09:09:38.614 ThreadsNQueues[2807:62047] BABBAAABBA 
2016-02-14 09:09:38.617 ThreadsNQueues[2807:62044] BBAAABAAAB 
2016-02-14 09:09:38.619 ThreadsNQueues[2807:62067] BABBAABBAB 
2016-02-14 09:09:38.622 ThreadsNQueues[2807:62047] ABABABABAB 
2016-02-14 09:09:38.623 ThreadsNQueues[2807:62047] BBABBBABAA 
2016-02-14 09:09:38.624 ThreadsNQueues[2807:62047] BBBBBBABBA 
2016-02-14 09:09:38.627 ThreadsNQueues[2807:62047] AABAABAABA 
... 

非常感謝

+0

你在該行上放置了一個斷點?視圖引用是否爲零? – Wain

+0

@ Wain:是的,我把一個bp,它永遠不會到達。不,這個參考很好。 –

回答

0

更新下面和理由提及不更新你的UI代碼你的代碼已經在回答上述:

你在你的代碼的開始分派到主線程,然後進入無限循環,所以主線程runloop將永遠不會再運行。

運行你的線程在後臺添加字符串,並在主線程上更新你的UI。

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    _output=[NSMutableString string]; 
    _lineLock=[[NSLock alloc] init]; 
    _line=[NSMutableString string]; 


    _myQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ 
     //Background Thread 

     do { 
      dispatch_async(_myQueue, ^{ 
       if([_lineLock tryLock]) { 
        [self append:'A']; 
        [_lineLock unlock]; 
       } 
      }); 
      dispatch_async(_myQueue, ^{ 
       if([_lineLock tryLock]) { 
        [self append:'B']; 
        [_lineLock unlock]; 
       } 
      }); 
     } while (true); 

    }); 

} 
+0

@Wain感謝您的評論!你說得對,你的建議是更新UI。但是現在我可以看到越來越多的後臺線程無法發佈(內存使用量也在迅速增加)。因此,在模擬器上運行約20秒的代碼,然後暫停它,產生> 300個後臺線程。所以我不確定你的建議是否是正確的答案: - /另一個問題是:是否有可能使UI響應,以便可以滾動文本視圖。 –

+1

嘿,我認爲這是最好的答案:)所以謝謝你的答案和代碼自適應。但是,使用GCD確實不是解決這類任務的最佳方法。創建兩行寫「同時互斥」的行,或者像@Wain提到的那樣使用NSOperation可能會更好。這裏使用GCD會導致數百個臨時線程(如果您在Xcode中暫停程序,則可以重現此問題)。 –

2

您在代碼開始時分派到主線程,然後進入無限循環,因此主線程runloop將永遠不會再運行。

如果您想使用相同的結構,則派發到您的自定義隊列而不是main,然後在內部將每個異步塊分派到提供的優先級隊列之一。

更好的解決方案可能是使用操作隊列,並在完成時讓每個操作將自己的新副本添加到隊列中。