调用NSURLConnectionDataDelegate方法来下载图像只能偶尔使用

时间:2013-08-11 15:44:41

标签: iphone ios facebook-graph-api nsurlconnection

我目前在故事板中有两个UIImageView,其中一个下载我自己的Facebook个人资料图片,另一个下载朋友的个人资料图片。

Storyboard layout

然而,我的问题是只有60%的时间按预期工作,而另外40%的时间我自己的个人资料图片显示在我朋友的图片应该显示在底部,顶盒仍然是空的。我不确定这是否是我在下载或完成视图时调用NSURLConnectionDataDelegate方法的结果,或者是我的请求调用Facebook的性质。

我已将我的两个请求的精简版本粘贴到viewDidLoad的Facebook,一个获取我自己的个人资料照片,另一个获取我朋友的照片:

// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
    //handle response
    if(!error){
        //Some methods not included for breveity, includes facebookID used below
        NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];
        self.imageData = [[NSMutableData alloc] init];
        switcher = 1;
        NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:pictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
        NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
        if (!urlConnection){
            NSLog(@"Failed to download picture");
        }
    }
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends]; 
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
    if(!error){
        //Other methods not included, including facebookFriendID
        NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookFriendID]];
        self.supportImageData = [[NSMutableData alloc] init];
        switcher = 2;
        NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:friendPictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
        NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
        if (!urlConnection){
            NSLog(@"Failed to download picture");
        }
    }
}];

这两个请求都调用NSURLConnectionDataDelegate方法,我使用switcher来决定何时加载哪个图片:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // As chuncks of the image are received, we build our data file

    if (switcher == 1) [self.imageData appendData:data];
    if (switcher == 2)[self.supportImageData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{

    //When the entire image is finished downloading
    if (switcher == 1) {
        UIImage *image = [UIImage imageWithData:self.imageData]; //puts the completed picture into the UI
        self.titleImageView.image = image;
        [self.titleImageView setClipsToBounds:YES];
    }

    if (switcher == 2) {
        UIImage *supportImage = [UIImage imageWithData:self.supportImageData];
        self.supportImageView.image = supportImage;
        [self.titleImageView setClipsToBounds:YES];
    }
}

1 个答案:

答案 0 :(得分:3)

您有两个异步进程,这两个进程都可能导致NSURLConnectionDataDelegate中的self方法被调用。但如果它们同时发生,它们就会相互踩踏(你可能会使用一个NSMutableData变量来引用正在下载的数据)。

创建专用类,您可以针对两个NSURLConnection请求(基于NSOperation的方法,例如AFNetworking)实例化一次,或者使用{{3}而是。但是,不要同时使用单个对象作为delegate两个并发NSURLConnection请求。


如果您想查看简约下载操作,可能会显示如下:

//  NetworkOperation.h

#import <Foundation/Foundation.h>

typedef void(^DownloadCompletion)(NSData *data, NSError *error);

@interface NetworkOperation : NSOperation

- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)completionBlock;

@property (nonatomic, copy) NSURL *url;
@property (nonatomic, copy) DownloadCompletion downloadCompletionBlock;

@end

//  NetworkOperation.m

#import "NetworkOperation.h"

@interface NetworkOperation () <NSURLConnectionDataDelegate>

@property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
@property (nonatomic, readwrite, getter = isFinished)  BOOL finished;
@property (nonatomic, strong) NSMutableData *data;

@end

@implementation NetworkOperation

@synthesize finished  = _finished;
@synthesize executing = _executing;

- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)downloadCompletionBlock
{
    self = [super init];
    if (self) {
        self.url = url;
        self.downloadCompletionBlock = downloadCompletionBlock;

        _executing = NO;
        _finished = NO;
    }
    return self;
}

#pragma mark - NSOperation related stuff

- (void)start
{
    if ([self isCancelled]) {
        self.finished = YES;
        return;
    }

    self.executing = YES;

    NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
    NSAssert(request, @"%s: requestWithURL failed for URL '%@'", __FUNCTION__, [self.url absoluteString]);
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [connection start];
}

- (void)setExecuting:(BOOL)executing
{
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)setFinished:(BOOL)finished
{
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isConcurrent
{
    return YES;
}

#pragma mark NSURLConnectionDataDelegate methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.data = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if ([self isCancelled]) {
        [connection cancel];
        self.executing = NO;
        self.finished = YES;
        return;
    }

    [self.data appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if (self.downloadCompletionBlock) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.downloadCompletionBlock(self.data, nil);
            self.downloadCompletionBlock = nil;
        }];
    }

    self.executing = NO;
    self.finished = YES;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    if (self.downloadCompletionBlock) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.downloadCompletionBlock(nil, error);
            self.downloadCompletionBlock = nil;
        }];
    }

    self.executing = NO;
    self.finished = YES;
}

@end

然后,当你想使用它时,它可能看起来像:

NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;

// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
    //handle response
    if(!error) {
        //Some methods not included for breveity, includes facebookID used below
        NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];

        [networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:pictureURL completion:^(NSData *data, NSError *error) {
            if (!error) {
                self.meImageView.image = [UIImage imageWithData:data];
            }
        }]];
    }
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends]; 
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
    if(!error){
        //Other methods not included, including facebookFriendID
        NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookFriendID]];

        [networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:friendPictureURL completion:^(NSData *data, NSError *error) {
            if (!error) {
                self.friendImageView.image = [UIImage imageWithData:data];
            }
        }]];
    }
}];