加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ios – sendAsynchronousRequest使UI冻结

发布时间:2020-12-14 18:02:13 所属栏目:百科 来源:网络整理
导读:download Images是一个按钮,每当我按下它时,一个微调器应该开始滚动,异步请求应该ping谷歌(以确保有连接)并且在收到响应后,我开始同步下载图像. 不知怎的,微调器不会去,似乎请求是同步而不是异步. - (IBAction)downloadImages:(id)sender { NSString *ping=@
download Images是一个按钮,每当我按下它时,一个微调器应该开始滚动,异步请求应该ping谷歌(以确保有连接)并且在收到响应后,我开始同步下载图像.

不知怎的,微调器不会去,似乎请求是同步而不是异步.

- (IBAction)downloadImages:(id)sender {

    NSString *ping=@"http://www.google.com/";

    GlobalVars *globals = [GlobalVars sharedInstance];
    [self startSpinner:@"Please Wait."];
    NSURL *url = [[NSURL alloc] initWithString:ping];
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response,NSData *data,NSError *error) {
        if (data) {
            for(int i=globals.farmerList.count-1; i>=0;i--)
            {
            //Definitions
            NSString * documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0];

            //Get Image From URL
                NSString *urlString = [NSString stringWithFormat:@"https://myurl.com/%@",[[globals.farmerList objectAtIndex:i] objectForKey:@"Image"]];
            UIImage * imageFromURL = [self getImageFromURL:urlString];



            //Save Image to Directory
            [self saveImage:imageFromURL withFileName:[[globals.farmerList objectAtIndex:i] objectForKey:@"Image"] ofType:@"jpg" inDirectory:documentsDirectoryPath];
            }
            [self stopSpinner];

        }
    }];
}

微调器代码:

//show loading activity.
- (void)startSpinner:(NSString *)message {
    //  Purchasing Spinner.
    if (!connectingAlerts) {
        connectingAlerts = [[UIAlertView alloc] initWithTitle:NSLocalizedString(message,@"")
                                                     message:nil
                                                    delegate:self
                                           cancelButtonTitle:nil
                                           otherButtonTitles:nil];
        connectingAlerts.tag = (NSUInteger)300;
        [connectingAlerts show];

        UIActivityIndicatorView *connectingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
        connectingIndicator.frame = CGRectMake(139.0f-18.0f,50.0f,37.0f,37.0f);
        [connectingAlerts addSubview:connectingIndicator];
        [connectingIndicator startAnimating];

    }
}
//hide loading activity.
- (void)stopSpinner {
    if (connectingAlerts) {
        [connectingAlerts dismissWithClickedButtonIndex:0 animated:YES];
        connectingAlerts = nil;
    }
    // [self performSelector:@selector(showBadNews:) withObject:error afterDelay:0.1];
}

问:getImageFromURL代码

-(UIImage *) getImageFromURL:(NSString *)fileURL {
    UIImage * result;

    NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
    result = [UIImage imageWithData:data];

    return result;
}
-(void) saveImage:(UIImage *)image withFileName:(NSString *)imageName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
    if ([[extension lowercaseString] isEqualToString:@"png"]) {
        [UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",imageName,@"png"]] options:NSAtomicWrite error:nil];
    } else if ([[extension lowercaseString] isEqualToString:@"jpg"] || [[extension lowercaseString] isEqualToString:@"jpeg"]) {
        [UIImageJPEGRepresentation(image,1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@",@"jpg"]] options:NSAtomicWrite error:nil];
    } else {
        NSLog(@"Image Save FailednExtension: (%@) is not recognized,use (PNG/JPG)",extension);
    }
}

解决方法

这是一个异步问题.异步是有感染力的.这意味着,如果问题的任何一小部分是异步的,整个问题就变得异步.

也就是说,你的按钮动作会调用这样的异步方法(它本身也变成“异步”):

- (IBAction)downloadImages:(id)sender 
{
    self.downloadImagesButton.enabled = NO;
    [self asyncLoadAndSaveImagesWithURLs:self.urls completion:^(id result,NSError* error){
        if (error != nil) {
            NSLog(@"Error: %@",error);
        }
        dispatch_async(dispatch_get_main_queue(),^{        
            self.downloadImagesButton.enabled = YES;
        };
    }];
}

因此,您的异步问题可以描述为:

给定URL列表,异步加载每个URL并以异步方式将它们保存到磁盘.加载并保存所有URL后,通过调用完成处理程序异步通知调用站点,并向其传递一组结果(对于每次下载和保存操作).

这是你的异步方法:

typedef void (^completion_t)(id result,NSError* error);

- (void) asyncLoadAndSaveImagesWithURLs:(NSArray*)urls 
                             completion:(completion_t) completionHandler;

只有找到合适的异步模式才能正确解决异步问题.这涉及到异步问题的每个部分.

让我们从你的getImageFromURL方法开始.加载远程资源本质上是异步的,因此您的包装器方法最终也将是异步的:

typedef void (^completion_t)(id result,NSError* error);

- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;

我不确定该方法最终将如何实现.您可以使用NSURLConnection的异步方便类方法,第三方帮助工具或您自己的HTTPRequestOperation类.它没关系,但它必须是异步的,以实现一个理智的方法.

有目的地,您可以并且应该使您的saveImage方法同步.使此异步的原因是,此方法可能会同时调用,我们应该*序列化*磁盘绑定(I / O绑定)任务.这提高了系统资源的利用率,也使您的方法成为友好的系统公民.

这是异步版本:

typedef void (^completion_t)(id result,NSError* error);

-(void) saveImage:(UIImage *)image fileName:(NSString *)fileName ofType:(NSString *)extension 
                                inDirectory:(NSString *)directoryPath 
                                 completion:(completion_t)completionHandler;

为了序列化磁盘访问,我们可以使用专用队列disk_queue,我们假设它已经被self自己正确初始化为一个串行队列:

-(void) saveImage:(UIImage *)image fileName:(NSString *)fileName ofType:(NSString *)extension 
                                inDirectory:(NSString *)directoryPath 
                                 completion:(completion_t)completionHandler
{
    dispatch_async(self.disk_queue,^{
        // save the image
        ...
        if (completionHandler) {
            completionHandler(result,nil);
        }
    });
}

现在,我们可以定义一个加载和保存图像的异步包装器:

typedef void (^completion_t)(id result,NSError* error);

- (void) loadAndSaveImageWithURL:(NSURL*)url completion:(completion_t)completionHandler
{
    [self loadImageWithURL:url completion:^(id image,NSError*error) {
        if (image) {
            [self saveImage:image fileName:fileName ofType:type inDirectory:directory completion:^(id result,NSError* error){
                if (result) {
                    if (completionHandler) {
                        completionHandler(result,nil);
                    }
                }
                else {
                    DebugLog(@"Error: %@",error);
                    if (completionHandler) {
                        completionHandler(nil,error);
                    }
                }
            }];
        }
        else {
            if (completionHandler) {
                completionHandler(nil,error);
            }
        }
    }];
}

这个loadAndSaveImageWithURL方法实际上执行了两个异步任务的“延续”:

首先,异步加载图像.
??那么,如果成功,则异步保存图像.

重要的是要注意这两个异步任务是按顺序处理的.

直到这里,这一切都应该是非常全面的,并且是直截了当的.现在,我们尝试以异步方式调用许多异步任务,这是棘手的部分.

异步循环

假设我们有一个URL列表.每个URL都应异步加载,并且当加载所有URL时,我们希望通知呼叫站点.

传统的for循环不适合实现这一点.但是想象一下,我们将使用这样的方法为NSArray创建一个类别:

NSArray的类别

- (void) forEachApplyTask:(task_t)transform completion:(completion_t)completionHandler;

这基本上是:对于数组中的每个对象,应用异步任务转换,并且当所有对象都已“转换”时,返回转换对象的列表.

注意:这个方法是异步的!

通过适当的“转换”功能,我们可以将其“翻译”为您的具体问题:

对于数组中的每个URL,应用异步任务loadAndSaveImageWithURL,并且在加载并保存所有URL后返回结果列表.

forEachApplyTask的实际实现:完成:可能看起来有点棘手,为简洁起见,我不想在这里发布完整的源代码.一种可行的方法需要大约40行代码.

我稍后会提供一个示例实现(在Gist上),但是让我们解释一下如何使用这个方法:

task_t是一个“块”,它接受一个输入参数(URL)并返回结果.
由于所有内容都必须异步处理,因此该块也是异步的,最终结果将通过完成块提供:

typedef void (^completion_t)(id result,NSError* error);

typedef void (^task_t)(id input,completion_t completionHandler);

完成处理程序可以定义如下:

如果任务成功,则参数错误等于nil.否则,参数错误是NSError对象.也就是说,有效结果也可能是零.

我们可以很容易地包装我们的方法loadAndSaveImageWithURL:completion:并创建一个块:

task_t task = ^(id input,completion_t completionHandler) {
    [self loadAndSaveImageWithURL:input completion:completionHandler];
};

给定一系列URL:

self.urls = ...;

你的按钮动作可以实现如下:

- (IBAction)downloadImages:(id)sender 
{
    self.downloadImagesButton.enabled = NO;

    task_t task = ^(id input,completion_t completionHandler) {
        [self loadAndSaveImageWithURL:input completion:completionHandler];
    };

    [self.urls forEachApplyTask:task ^(id results,NSError*error){
        self.downloadImagesButton.enabled = YES;
        if (error == nil) {
            ... // do something
        }
        else {
            // handle error
        }
    }];
}

再次注意,方法forEachApplyTask:completion:是一个异步方法,它立即返回.呼叫站点将通过完成处理程序得到通知.

downloadImages方法也是异步的,但是没有完成处理程序.此方法在启动时禁用按钮,并在异步操作完成后再次启用它.

这个forEachApplyTask方法的实现可以在这里找到:(https://gist.github.com/couchdeveloper/6155227).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读