ios - 模型、 block 和完成处理程序 Oh MY

标签 ios objective-c objective-c-blocks completionhandler

我正在从头开始学习 iOS 编程。

我希望我的应用程序从网站提取 XML。我认为为了符合 MVC 模式,我应该有一个模型类,它只提供一个方法来完成该任务(也许它也解析 XML 并返回一个数组)。

问题是我发现的所有教程都在 View 和 Controller 的上下文中教授 NSURLSession - 因此编辑 appdelegate,或创建 View Controller 等。

我从苹果文档中获得了以下方法,目前我在按下按钮时将其作为 IBAction 运行(这样我就可以运行它并轻松测试它)。我想让它工作然后把它放在它自己的类中:

__block NSMutableData *webData;

NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"Got response %@ with error %@.\n", response, error);
    NSLog(@"DATA:\n%@\nEND DATA\n", [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);
        webData = [[NSMutableData alloc] initWithData:data];
}
]resume];

我直接的问题是:

有人可以解释一下完成处理程序是如何工作的以及如何从中获取数据吗?它正在工作,数据正在从网站抓取 xml 并将其记录在控制台上,但是将其复制到 webData 不起作用,它会编译但不会复制。 (我仍在弄清楚为什么 __block 声明允许 webData 首先潜入其中!)

我更大的问题是,是否每个人都认为为这个过程建立一个单独的模型类是一个好主意。有更好的设计方法吗?

谢谢!

最佳答案

这可能只是对异步 block 如何工作的一些混淆。如果您这样做:

__block NSMutableData *webData;
// ...

[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"within the block, did I get data: %@", data);
    webData = [[NSMutableData alloc] initWithData:data];
}]resume];

NSLog(@"after the block, did I get data: %@", webData);

您可能会看到如下所示的输出:

after the block, did I get data: (null)
within the block, did I get data: <NSData ...

什么给了?为什么 block 后面的代码先运行?数据在哪里?问题在于我们对“之后”的定义。在 block 之后出现的 NSLog 实际上在 block 运行之前运行。一旦 dataRequest 启动,它就会运行。 block 内的代码在请求完成后运行。

将数据结果保存在该方法的本地 block 变量中没有任何好处。当您到达方法末尾时,该值未初始化。 block 在运行时对其进行初始化,但一旦 block 完成,该值就会被丢弃。

修复:处理 block 内的数据。不要指望它在 block 运行之后才有效(也就是在方法运行之后):

编辑 - 在这个 block 中使用 self 来调用方法、设置属性等是 100% 好的。仅当 block 本身是 self 的属性时,您才需要注意保留周期(或 self 保留的东西的属性),但它不是......

// don't do this
//__block NSMutableData *webData;
// ...

[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"within the block, did I get data: %@", data);
    NSMutableData *webData = [[NSMutableData alloc] initWithData:data];
    // do whatever you plan to do with web data
    // write it to disk, or save it in a property of this class
    // update the UI to say the request is done

    [self callAMethod:data];     // fine
    [self callAnotherMethod];    // fine
    self.property = data;        // fine
}]resume];

// don't do this, there's no data yet
//NSLog(@"after the block, did I get data: %@", webData);

关于ios - 模型、 block 和完成处理程序 Oh MY,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25937578/

相关文章:

ios - makeRenderPipelineState 失败 [ushort 类型的输出与 MTLPixelFormatR16Float 颜色附件不兼容。]

ios - 圆弧 : "Pointer to non-const type ' id' with no explicit ownership"

objective-c - Xcode 不会打开我的项目

ios - dispatch_async 是否复制内部 block

objective-c - NSInvocation 和 block 有什么区别?

ios - React Native - 组件在 IOS 上有不需要的白色背景

ios - Swift - 如何获取 iPhone 上所有照片和视频的列表?

objective-c - 将分隔符颜色更改为背景色或将其从 NSTableView 中删除

objective-c - 操作系统 : How to detect if Mission Control is running?

objective-c - 同步块(synchronized block)内的 block ?