背景
我的应用程序从网络流中获取数据,并更改屏幕上UI元素的值。一个元素是UITextView,它用作传入数据的一种日志。每当应用程序收到具有传入数据性质的“HasBytesAvailable”NSStreamEvent时,它都会更新。 (例如,如果进来的数据与蛋糕有关,textview会更新为“6/22/12 8:00 - 得到蛋糕”)下面显示了如何更新的示例。
[logString insertString:@"This is an update\n" atIndex:0];
//logstring is a MutableString I use to hold my UITextView's text
[logString insertString:timeString atIndex:0]; //timestring is current time
logView.text = logString; //logView is my UITextView
[logView flashScrollIndicators];
//logstring and logview declaration and implementation
@property (nonatomic,retain) IBOutlet UITextView *logView;
@property (nonatomic,retain) NSMutableString *logString;
logString = [[NSMutableString alloc] initWithString:@"-logging started\n"];
问题
只要我不尝试滚动TextView,更新就像我想要的那样工作。但是,如果我滚动文本并按住它,大概足以让我的更新代码被调用,当我停止滚动时应用程序崩溃。我可以轻松浏览文本,只是当它必须处理传入的数据包时,我仍在滚动它会崩溃。此外,当我滚动时,没有别的东西会更新。应该由收到的数据更新的所有标签保持不变。
我的想法
就好像应用程序无法同时处理滚动和处理传入数据一样。我不确定这是因为我在做内存管理方面有问题,或者我需要覆盖一些滚动功能,或者完全不需要其他功能。任何帮助或想法都表示赞赏。
解决方案
正如trumpetlicks所说,我需要为我的网络任务实现多线程 要做到这一点,我做了以下几点:
初始化:
NSOperationQueue *networkQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(initNetworkCommunication) object:nil];
[networkQueue addOperation:operation];
[operation release];
初始化CFSocketpair和流后,在initNetworkCommunication中:
[[NSRunLoop currentRunLoop] run]; //necessary to handle stream events
答案 0 :(得分:0)
从这里开始。
developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/...你可以
还希望添加您的网络设置和使用代码,以便我们都可以看到您在那里做什么!
答案 1 :(得分:0)
我可以提出3个可以帮助你的想法:
1)DONT从后台线程调用UI元素,总是在主线程上运行,例如使用:
dispatch_async(dispatch_get_main_queue(), ^{
self.textview.text = .....
});
2)在线程之间使用某种同步(稍后会详细介绍..)
3)NSOperation对于此任务来说过于复杂,只需使用NSURLSession回调,并在完成时调用一个必须序列化的“中心”方法(如2中所述))
假设在每个网络回调中:
...
// create Session and request...
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString * s = [[NSString alloc]initWithData:data encoding: NSUTF8StringEncoding];
[self serializer:s];
}
];
[task resume];
...
4)现在让'尝试同步(并转到UI的主线程......)
在“init”/ ViewDidLoad中创建一个
self.queue = dispatch_queue_create("com.acme.myQueue", DISPATCH_QUEUE_CONCURRENT);
5)在序列化方法中使用它:
-(void) serializer:(NSString*)s
{
dispatch_barrier_async(self.queue, ^{
[self updateUI:s];
});
}
6)更新UI:
-(void) updateUI:(NSString*)s
{
dispatch_async(dispatch_get_main_queue(), ^{
NSString * currText = self.textView.text;
self.textView.text = [NSString stringWithFormat:@"%@\n%@", currText, s];
});
}
所以回顾一下我的所有控制器都可以(有些细节留给你了。)
@interface ViewController ()
@property dispatch_queue_t queue;
@property UITextView * textView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.queue = dispatch_queue_create("com.acme.myQueue", DISPATCH_QUEUE_CONCURRENT);
[self startDownloadFromURL:@"https://www.apple.com"];
[self startDownloadFromURL:@"https://www.google.com"];
}
-(void)startDownloadFromURL:(NSString*)urlString{
NSURLSession * session;
NSURLRequest * request;
// create Session and request...
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString * s = [[NSString alloc]initWithData:data encoding: NSUTF8StringEncoding];
[self serializer:s];
}
];
[task resume];
}
-(void) serializer:(NSString*)s
{
dispatch_barrier_async(self.queue, ^{
[self updateUI:s];
});
}
-(void) updateUI:(NSString*)s
{
dispatch_async(dispatch_get_main_queue(), ^{
NSString * currText = self.textView.text;
self.textView.text = [NSString stringWithFormat:@"%@\n%@", currText, s];
});
}
@end