iOS 中的作用域和线程

Posted

技术标签:

【中文标题】iOS 中的作用域和线程【英文标题】:Scope and threading in iOS 【发布时间】:2013-04-23 13:36:00 【问题描述】:

我对 ios 中的 Web 服务调用和线程有点陌生。我的应用程序中有一个ViewController,其中包含一个表格视图控件。我正在使用通过 JSON Web 服务获得的数据填充表。 JSON Web 服务在其自己的线程上调用,在此期间我填充了NSArrayNSDictionary

我的数组和字典似乎超出了范围,因为我的 NSLog 语句为数组计数返回零,即使在 fetchedData 中数组已完全填充。

有人可以解释为什么我的数组和字典对象在线程之外是空的吗?


- (void)viewDidLoad

    [super viewDidLoad];

    NSString *serviceEndpoint = [NSString stringWithFormat:
                                 @"http://10.0.1.12:8888/platform/services/_login.php?un=%@&pw=%@&ref=%@",
                                 [self incomingUsername], [self incomingPassword], @"cons"];
    NSURL *url = [NSURL URLWithString:serviceEndpoint];
    dispatch_async(kBgAdsQueue, ^
        NSData *data = [NSData dataWithContentsOfURL:url];
        [self performSelectorOnMainThread:@selector(fetchedData:) withObject:data waitUntilDone:YES];
    );

    NSLog(@"ARRAY COUNT: %d\n", [jsonArray count]);


-(void)fetchedData:(NSData*)responseData
    NSError *error;
    jsonDict = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
    jsonArray = [[jsonDict allKeys]sortedArrayUsingSelector:@selector(compare:)];
    for(NSString *s in jsonArray)
        NSLog(@"%@ = %@\n", s, [jsonDict objectForKey:s]);
    

【问题讨论】:

ViewDidLoad 仅在屏幕首次加载时被调用。您确定在此之前调用了fetchedData 吗? 【参考方案1】:

当您使用dispatch_async 时,该位代码不会阻塞。这意味着您的数组计数日志语句在调用fetchedData 之前触发,因此您的字典和数组仍然为空。查看日志语句的顺序 - 您应该在记录字典之前看到数组计数。

// Executes on another thread. ViewDidLoad will continue to run.
dispatch_async(kBgAdsQueue, ^
    NSData *data = [NSData dataWithContentsOfURL:url];
    [self performSelectorOnMainThread:@selector(fetchedData:) withObject:data waitUntilDone:YES];
);

// Executes before the other thread has finished fetching the data. Objects are empty.
NSLog(@"ARRAY COUNT: %d\n", [jsonArray count]);

您需要在数据返回后完成对TableView 的填充(即在FetchData: 中)。

【讨论】:

是的,这很有道理,但现在我不知道如何纠正这个问题。我需要在线程上执行此操作,因为这可能需要一些时间... 只需从 fetchedData 函数中调用 TableView 上的 reloadData。 您应该在您的 UI 上显示它正在下载的某种指示 - 可能是一个微调器或一个读取类似“正在获取数据。这可能需要一分钟”的单元格。这样用户就知道发生了什么事。当fetchData: 触发时,您可以调用reloadData。或者,如果请求失败,您可以触发新的显示消息。 将 fetch 从视图控制器中移出到模型对象中。视图控制器不应该做网络访问;他们应该显示在模型中找到的数据。因此模型根据需要获取数据,视图控制器只显示模型当前包含的任何内容。【参考方案2】:

viewDidLoad 中的日志语句应该报告该数组是空的,因为当时它还没有被填充。调用 dispatch_async 会导致该代码块异步运行,并允许 viewDidLoad 函数在该块之前完成。这就是为什么在 viewDidLoad 结束时数组中没有任何内容的原因。

【讨论】:

【参考方案3】:

您正在尝试在填充之前打印 jsonArray 元素的计数。这是发生了什么:

    你准备网址 您创建新线程来获取某个日期。此线程的执行可能需要一些时间(取决于连接速度和数据量)。 您正在访问 jsonArray,而线程正在执行并且 fetchedData: was not called

另外,建议: 不要使用 dataWithContentsOfURL: 方法。最好看看一些网络框架,例如 AFNetworking。

【讨论】:

以上是关于iOS 中的作用域和线程的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript中的作用域和闭包

Javascript中的块作用域、函数作用域和局部作用域

Spring Bean 作用域和生命周期

Go 语言入门很简单:Go 中的作用域和变量隐藏

JavaScript中的作用域和作用域链(边学边写)[看着别人的博客纯手敲]

jmeter元件作用域和执行顺序