问题
I am new to blocks programming.I am trying to download an image and display it on an image view whose property i have already made in my .h file.as far as i know the class property can only be accessed in the form of getter/setter inside the block.So i have created an imageView "imgVw" in viewDidload method and am currently unable to access it inside the block using self. Please find below my code.
/*dispatch the queue asynchronously for non ui related tasks image downloading*/
void(^downloadimage)(void)=^{
dispatch_queue_t concurrentqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentqueue,^void{
__block UIImage *image=nil;
dispatch_sync(concurrentqueue, ^{
//download image
NSString *stringUrl=@"http://www.avajava.com/images/avajavalogo.jpg";
NSURL *url=[NSURL URLWithString:stringUrl];
NSURLRequest *request=[NSURLRequest requestWithURL:url];
NSError *error=nil;
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if(error== nil && data !=nil)
{
image=[UIImage imageWithData:data];
}
else if (error!=nil)
{
NSLog(@"Error %@",[error description]);
}
else
{
NSLog(@"No data could be downloaded");
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/*show the image on main queue*/
if(image!=nil)
{
**/*set image downloaded to imageView imgVw*/
[[self imgVw] setImage:image];**
}
else
{
NSLog(@"image is not downloaded");
}
});
});
};
回答1:
You say that you "have created an image view imgVw
in viewDidLoad
". If you want to instantiate the image view programmatically (rather than putting it into your storyboard/NIB), that's fine. If you're going to instantiate it programmatically, you'd first want a property associated for that image view, e.g.:
@property (nonatomic, weak) UIImageView *imgVw;
Then, viewDidLoad
would instantiate it:
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:...]; // instantiate it any way you want
[self.view addSubview:imageView]; // add as subview
self.imgVw = imageView; // now set your property
}
Then you can download the image.
Unrelated to your original question, your download code is a little more complicated than it needs to be. You don't need to dispatch to your concurrent queue from the concurrent queue. Also, when you dispatch to the main queue, you can do that asynchronously. Thus, get rid of that outer dispatch_async
and just do:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image=nil;
NSString *stringUrl = @"http://www.avajava.com/images/avajavalogo.jpg";
NSURL *url = [NSURL URLWithString:stringUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if(error== nil && data !=nil)
{
image = [UIImage imageWithData:data];
}
else if (error != nil)
{
NSLog(@"Error %@",[error description]);
}
else
{
NSLog(@"No data could be downloaded");
}
dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil)
{
self.imgVw.image = image;
}
else
{
NSLog(@"image is not downloaded");
}
});
});
Or, even easier, use sendAsynchronousRequest
, which eliminates yet another dispatch_async
:
NSString *stringUrl = @"http://www.avajava.com/images/avajavalogo.jpg";
NSURL *url = [NSURL URLWithString:stringUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError == nil && data != nil)
{
UIImage *image = [UIImage imageWithData:data];
if (image != nil)
{
dispatch_async(dispatch_get_main_queue(), ^{
self.imgVw.image = image;
});
}
else
{
NSLog(@"image is not downloaded");
}
}
else if (connectionError != nil)
{
NSLog(@"Error %@",[connectionError description]);
}
else
{
NSLog(@"No data could be downloaded");
}
}];
By the way, in your question, you put this above download code in a block variable, downloadimage
. I'm unclear why you did that (rather than just defining a method that does the download). I'm worried that you're thinking that you do something like dispatch_async(queue, downloadimage);
which only adds yet another redundant level of dispatching. But if it's so you can pass it to you own method that will call it directly, that's fine. You might want to show us how you're using this block variable.
回答2:
Thanks guys for your help.
I figured out that i was implemented an independent block and was trying to access self inside but it required one more thing.
"You cannot refer to self in independent block objects implemented in an Objective-C class. If you need to access self, you must pass that object to the block object as a parameter."
so i passed self as parameter to this independent block and got it working, there was no issue with the imgVw. Here is the code.
/**dispatch thequeue asynchronously for non ui related tasks image downloading*/
void(^downloadimage)(id)=^(id self){
// NSString *stringUrl=@"http://www.avajava.com/images/avajavalogo.jpg";
dispatch_queue_t concurrentqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentqueue, ^{
__block UIImage *image = nil;
dispatch_sync(concurrentqueue, ^{
/* Download the image here */
/* iPad's image from Apple's website. Wrap it into two
lines as the URL is too long to fit into one line */
NSString *urlAsString =
@"http://images.apple.com/mobileme/features"\
"/images/ipad_findyouripad_20100518.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection
sendSynchronousRequest:urlRequest
returningResponse:nil
error:&downloadError];
if (downloadError == nil &&
imageData != nil){
image = [UIImage imageWithData:imageData];
/* We have the image. We can use it now */
}
else if (downloadError != nil){
NSLog(@"Error happened = %@", downloadError);
} else {
NSLog(@"No data could get downloaded from the URL.");
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Show the image to the user here on the main queue*/
if (image != nil){
/* Create the image view here */
UIImageView *imageView = [[UIImageView alloc]
initWithFrame:[[self view] bounds]];
/* Set the image */
[imageView setImage:image];
/* Make sure the image is not scaled incorrectly */
[imageView setContentMode:UIViewContentModeScaleAspectFit];
/* Add the image to this view controller's view */
[[self view] addSubview:imageView];
} else {
NSLog(@"Image isn't downloaded. Nothing to display.");
}
});
});
};
来源:https://stackoverflow.com/questions/23890573/access-the-control-inside-the-synchronous-block-in-xcode-5-0-iphone