应用程序在UITextView滚动后崩溃。

3

背景

我的应用程序从网络流中获取数据,并更改屏幕上 UI 元素的值。其中一个元素是 UITextView,它作为传入数据的一种日志。每当应用程序接收到带有传入数据本质的 "HasBytesAvailable" NSStreamEvent 时,它都应该更新。例如,如果传入的数据与蛋糕有关,则文本视图将更新为类似于 "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,更新工作就像我想要的那样。然而,如果我滚动文本并按住它,可能足够长时间调用我的更新代码,当我停止滚动时,应用程序会崩溃。我可以轻松地 flick through the text,只有在处理传入数据并且我仍在滚动时才会崩溃。此外,在我滚动时,没有其他内容会更新。所有应该由接收到的数据更新的标签都保持不变。

我的想法

好像这个应用程序不能同时处理滚动和处理传入数据。我不确定这是因为我在内存管理方面做错了什么,还是需要覆盖一些滚动功能,或者完全是其他原因。任何帮助或想法都将不胜感激。

解决方案

如trumpetlicks所说,我需要为我的网络任务实现多线程处理。为此,我执行了以下操作:

初始化时:

NSOperationQueue *networkQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(initNetworkCommunication) object:nil];
[networkQueue addOperation:operation];
[operation release];

在initNetworkCommunication函数中,在初始化CFSocketpair和流之后:
[[NSRunLoop currentRunLoop] run]; //necessary to handle stream events

我理解,当你在主动滚动时,UI线程繁忙且无法进行其他更新。(我假设你的网络请求是在其他线程中进行的。)那么崩溃看起来是什么样子的(异常和堆栈信息)? - Phillip Mills
你的网络流量有多大?如果不是很大的话,似乎轻负载不应该导致你所看到的问题。如果是重负载,那就另当别论了! - trumpetlicks
@trumpetlicks 这不应该是流量负载的问题,我们正在谈论每条消息64字节,大约每秒钟进来一次。 - Chance R.
它是崩溃还是仅仅卡住了滚动?进一步的问题是,当你的logView中的文本很少时,它是崩溃/卡住的,还是只有在有相当多的文本之后才会出现这种情况? - trumpetlicks
@trumpetlicks 视图必须有足够的文本才能滚动,但这并不多(视图只有280x240),所以我认为它不会溢出内存。 它似乎在崩溃 - 至少退出到主屏幕,但没有崩溃消息。 值得一提的是,我用从数据包获取的数据进行一些字节级操作(字节交换等),我不知道Xcode是否可以很好地处理那个级别的内存错误。 - Chance R.
显示剩余6条评论
2个回答

0
我可以提供三个建议来帮助你:
1)不要从后台线程调用UI元素,始终在主线程上操作,例如使用:

dispatch_async(dispatch_get_main_queue(), ^{
    // Your code here
});

   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) 更新用户界面:

-(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

0

从这里开始。

developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… 您可能还希望添加您的网络设置和使用代码,以便我们都可以看到您在那里做了什么!!!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接