iOS - Returning a variable from a block - iphone

I have an iOS project which is pulling images from a datasource method. I would like to be able to pull the image from the assets library (and the code chunk below does this just fine).
However, I need this dataSource method to return a UIImage, but when I use the assets library methods to get the image, the image is returned in a result block. Simply putting return image in the result block obviously does not work.
Does anyone have any idea how I can have the method return a UIImage from inside the result block? I have seen several other SO questions about returning images within blocks, but they are say to call another method. I - unfortunately - can't do that because this method is a nimbus datasource method which must return a UIImage.
Any help or advice would be greatly appreciated! Code below:
- (UIImage *)photoAlbumScrollView: (NIPhotoAlbumScrollView *)photoAlbumScrollView
photoAtIndex: (NSInteger)photoIndex
photoSize: (NIPhotoScrollViewPhotoSize *)photoSize
isLoading: (BOOL *)isLoading
originalPhotoDimensions: (CGSize *)originalPhotoDimensions {
__block UIImage *image = nil;
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:[_photos objectAtIndex:photoIndex]
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef imageRef = [rep fullScreenImage];
if (imageRef) {
image = [UIImage imageWithCGImage:imageRef];
}
}
failureBlock:^(NSError *error) {
//return nil;
}];
return image;
}

You should create an array for each image. When this data source method Is first called, you will not have an image for that index in the array. Kick off the asset call then return a place holder image. When the block returns, replace the place holder image with the asset image returned in the block. You may need to perform this on the main queue using GCD.

So I think I have a solution to your problem. The idea is to make use of a dispatch_group, since you can wait on a dispatch group - it gives you a way to block a thread until something happens. It may require that your datasource action NOT use the mainthread, but you are going to have to play with this. Lets assume that the object implementing photoAlbumScrollView is called 'obj'.
obj creates a serial dispatch queue (called queue)
datasource sends [obj photoAlbumScrollView] message
photoAlbumScrollView does what it does now, but before returning waits on the queue
the final block unblocks the queue letting the group finish
The code:
__block UIImage *image = nil;
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
dispatch_queue_t queue = dispatch_queue_create("com.myApp.assetFetch", DISPATCH_QUEUE_SERIAL);
[assetslibrary assetForURL:[_photos objectAtIndex:photoIndex]
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef imageRef = [rep fullScreenImage];
if (imageRef) {
image = [UIImage imageWithCGImage:imageRef];
}
dispatch_resume(queue);
}
failureBlock:^(NSError *error) {
dispatch_resume(queue);
}];
dispatch_suspend(queue);
dispatch_sync(queue, ^{ NSLog(#"UNSUSPEND!"); }); // ultimately a block with just a ';' in it
dispatch_release(queue);
return image;
I obviously did not test this but it or something close to it should work, assuming again that you can make this on a thread and not on the mainThread.

Related

How to run a thread in background of application which will not affect my application user interface

I have made an application for IOS, in which I am using sqlite database, database is local in application.
Now I have given the functionality the application is downloading data from internet & put it in local database & show infront of user. I have given this functionality on - (void)viewDidLoad so that when application is downloading data, it stop working till it finish downloading part, for this user need to wait to interact with application.
Now I wants to functionality a thread run in background of application which will connect the internet & update the application without interfering user.
please help me.
My code of download & save image is this:
-(void)Download_save_images:(NSString *)imagesURLPath :(NSString *)image_name
{
NSMutableString *theString = [NSMutableString string];
// Get an image from the URL below
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:NSURL URLWithString:imagesURLPath]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// If you go to the folder below, you will find those pictures
NSLog(#"%#",docDir);
[theString appendString:#"%#/"];
[theString appendString:image_name];
NSLog(#"%#",theString);
NSLog(#"saving png");
NSString *pngFilePath = [NSString stringWithFormat:theString,docDir];
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
NSLog(#"saving image done");
[image release];
// [theString release];
}
when i am debugging application i seen my application taking more time at below line:
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:NSURL URLWithString:imagesURLPath]]];
You can also use NSOperationQueue and NSBlockOperation if you find GCD difficult.
NSBlockOperation *operation=[[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
//Your code goes here
}];
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
[queue addOperation:operation];
In GCD You could achieve the same using
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Your Code to download goes here
dispatch_async(dispatch_get_main_queue(), ^{
//your code to update UI if any goes here
});
});
Use either of the API Depending upon your needs. Check this thread which discuss about NSOperationQueue vs GCD for more info.
Questions like these were asked hundreds of times before. I suggest you do a quick search on this. Also there is a topic in apple documentation covering this area thoroughly. here
Basically you can do this with operation queues or dispatch queues. There are some code snippets given above by Avi & Amar.
I'd like to add something since you seem to be unfamiliar with the topic & you mentioned there are web requesting involved.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Dont DIRECTLY request your NSURL Connection in this part because it will never return data to the delegate...
// Remember your network requests like NSURLConnections must be invoked in mainqueue
// So what ever method you're invoking for NSURLConnection it should be on the main queue
dispatch_async(dispatch_get_main_queue(), ^{
// this will run in main thread. update your UI here.
});
});
I've given small example below. you can generalise the idea to match your needs.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Do your background work here
// Your network request must be on main queue. this can be raw code like this or method. which ever it is same scenario.
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
dispatch_async(dispatch_get_main_queue(), ^{
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *res, NSData *dat, NSError *err)
{
// procress data
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// back ground work after network data received
dispatch_async(dispatch_get_main_queue(), ^{
// finally update UI
});
});
}];
});
});
You can us GCD, like that:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// put here your background code. This will run in background thread.
dispatch_async(dispatch_get_main_queue(), ^{
// this will run in main thread. update your UI here.
});
})
But you need to understand how blocks work for example. Try it.
Use GCD
create your dispatch object
dispatch_queue_t yourDispatch;
yourDispatch=dispatch_queue_create("makeSlideFast",nil);
then use it
dispatch_async(yourDispatch,^{
//your code here
//use this to make uichanges on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
});
Do ui on main thread only by using
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
Then release it
dispatch_release(yourDispatch);
Here is tutorial
There are a lot of ways:
1 . Grand Central Dispatch
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//Your code
});
2 . NSThread
[NSThread detachNewThreadSelector:#selector(yourMethod:) toTarget:self withObject:parameterArray];
3 . NSObject - performSelectorInBackground
[self performSelectorInBackground:#selector(yourMethod:) withObject:parameterArray];

I am using ZXingObjC lib for reading barcode from an image but its not returing any result the code i am using is below

-(void)decode:(CVImageBufferRef)BufferRef
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CGImageRef videoFrameImage = [ZXCGImageLuminanceSource createImageFromBuffer:BufferRef];
CGImageRef rotatedImage = [self rotateImage:videoFrameImage degrees:0.0f];
[NSMakeCollectable(videoFrameImage) autorelease];
//Decoding:
ZXLuminanceSource* source = [[[ZXCGImageLuminanceSource alloc] initWithCGImage:rotatedImage] autorelease];
[NSMakeCollectable(rotatedImage) autorelease];
ZXBinaryBitmap* bitmap = [ZXBinaryBitmap binaryBitmapWithBinarizer:[ZXHybridBinarizer binarizerWithSource:source]];
NSError* error = nil;
// There are a number of hints we can give to the reader, including
// possible formats, allowed lengths, and the string encoding.
ZXDecodeHints* hints = [ZXDecodeHints hints];
ZXMultiFormatReader* reader = [ZXMultiFormatReader reader];
ZXResult* result = [reader decode:bitmap
hints:hints
error:&error];
if (result)
{
// The coded result as a string. The raw data can be accessed with
// result.rawBytes and result.length.
NSString* contents = result.text;
// The barcode format, such as a QR code or UPC-A
ZXBarcodeFormat format = result.barcodeFormat;
}
else
{
// Use error to determine why we didn't get a result, such as a barcode
// not being found, an invalid checksum, or a format inconsistency.
}
[pool drain];
}
I ran into this problem which is easily resolved by down sampling the image that the camera generates. Apparently it's too high res for the library to process the bar code out of it. By reducing the size of my UIImage to 640x480 before call [image CGImage] everything worked perfectly.
You've commented that you should use the error to determine why you didn't get a result, but you don't actually do that. Look at what's in error.

__block variable not retained

I am trying to assign results within a block to a block variable. Here's my code:
__block UIImage *latestImage;
ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[assetLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop){
// Within the group enumeration bl ock, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
// Chooses the photo at the last index
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets]-1)]
options:0
usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop){
// the end of the enumeration is signaled by asset == nil.
if (alAsset){
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
latestImage = [UIImage imageWithCGImage:[representation fullResolutionImage]];
}
}];
}
failureBlock: ^(NSError *error){
// handle error
NSLog(#"No groups");
}
];
return latestImage;
I confirmed that the variable latestImage was set within the block, by displaying it on a UIImageView from inside the block. However, when I try to return that object as shown in the code above, it returns nothing.
What am I missing?
Thanks.
enumerateGroupsWithTypes:usingBlock:failureBlock: is asynchronous. You're returning latestImage before the block has had a chance to set the variable. From the documentation on ALAssestsLibrary:
Many of the methods declared by ALAssetsLibrary take a failure block
as an argument. These methods are all asynchronous, because the user
may need to be asked to grant access to the data.
You can either wrap the whole function in a dispatch_sync() or use a semaphore to wait for the answer. Either way, make sure you're not doing this on the main thread.

IPhone: File Upload using dispatch_async for background upload

I want to perform a image upload while the running application at the background. I am able to upload the image to the server using the code on this link.
How can I upload a photo to a server with the iPhone?
I heard the NSUrlConnection can be asynchronous and it was used in the EPUploader. In my code, I add some extra method that will create a file in the application directory used for the EPUploader. During this creation of the file, I don't want it to create on the main thread of the application so I wrap all the code including the EPUploader itself with the
dispatch_async on the global queue. That way I won't block the main thread while the file are creating.
It has no problem if I use dispatch_sync but dispatch_async I find something weird when I placed the breakpoint at NSUrlConnection connection :
- (void)upload
{
NSData *data = [NSData dataWithContentsOfFile:filePath];
//ASSERT(data);
if (!data) {
[self uploadSucceeded:NO];
return;
}
if ([data length] == 0) {
// There's no data, treat this the same as no file.
[self uploadSucceeded:YES];
return;
} /* blah blah */
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!connection) {
[self uploadSucceeded:NO];
return;
}
else
return;
I went to debug at the breakpoint and instead of going to if statement, the debugger jumps to the first return statement of this method. After that, the selectors I passed on to this class never gets called. This happen only on dispatch_async and it work on dispatch_sync on the global queue.
Does anybody know how to solve this issue?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
//[self singleUpload:self.uploadIndex];
[pool release];
});
There are a couple of things that should be changed.
Remove the NSAutoreleasePool, it is not needed.
Copy the block to the heap because it's life will probably exceed that of the calling code.
Example:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, [[^{
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
} copy] autorelease]);
If you are using ARC (which you certainly are since you should be) there is no need for the copy or autorelease.

NSAutoreleasePool issues on the iPhone

I have a small iPhone app which has a button on the first view. When I select this button I load up my new view which has an image on it. I'm currently using the following code to load the image from an online source on a separate thread, whilst allowing the user to continue controlling the application:
- (void) loadImageTest
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [[NSURL alloc] init];
url = [NSURL URLWithString:logoPath];
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
//[pool drain];
[pool release];
}
This is called from this line of code in the new view's init:
[NSThread detachNewThreadSelector:#selector(loadImageTest) toTarget:self withObject:nil];
Now this works fine (ish), but if I close the new view and then load a new one up again in quick succession (or just after-wards in some cases) it will bomb out with the classic EXC_BAD_ACCESS. I'm sure that this is due to the code within the thread pool, but can anyone see why this is happening?
Thanks
Yours:
// This is ok, you might try using NSURLConnections asynchronous methods instead of making
// your own thread.
[NSThread detachNewThreadSelector:#selector(loadImageTest) toTarget:self withObject:nil];
- (void)loadImageTest
{
// This is fine
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// you're making and then abandoning this url object so it will leak
NSURL *url = [[NSURL alloc] init];
// this is fine
url = [NSURL URLWithString:logoPath];
// again making and abandoning an object
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
// this works, but is not thread safe,
// can't operate on UIKit objects off the
// main thread
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
// this is fine
//[pool drain];
[pool release];
}
Cleaned up to make things thread safe, etc. Should help:
// I'm assuming you have a iVar for logoPath but we don't want to
// make threaded calls to an iVar (it's not mutable, so you could do it, but it's just bad form)
// If i'm wrong about logoPath being an iVar don't sweat copying it.
- (void)whateverMethodYouWant
{
NSString *aLogoPath = [[logoPath copy] autorelease];
[NSThread detachNewThreadSelector:#selector(loadImageForPath:) toTarget:self withObject:aLogoPath];
}
- (void)loadImageForPath:(NSString *)aLogoPath
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:aLogoPath]];
// the performSelector will retain the data until the method can be performed
[self performSelectorOnMainThread:#selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
[pool release];
}
- (void)setImageForTitleLogo:(NSData *)imgData
{
// This part is not strictly necessary
// but it's a nice way to wrap a method that needs to happen on the main thread.
if ([NSThread isMainThread])
{
// make the image (i'm assuming you meant loadingImage to be a local scope variable)
UIImage *loadingImage = [UIImage imageWithData:imgData];
// make sure the button still exists
// also make sure you're setting any references to this button to nil when you're releasing and making new views
// so that you're not addressing a button that's been released
// (assigning the image to the button will cause the button to retain it for you)
if (titleLogoImage != nil)
titleLogoImage.image = loadingImage;
}
else
{
[self performSelectorOnMainThread:#selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
}
}
You're doing weird stuff.
NSURL *url = [[NSURL alloc] init];
means you create an NSURL object, which you own.
url = [NSURL URLWithString:logoPath];
This means you create another NSURL object, which is autoreleased. The NSURL you just created, just leaks. The same goes for the NSData here.
The loadingImage must be retained by titleLogoImage, or it will be deallocated on the drain of your NSAutoReleasePool. What is titleLogoImage and does it retain image?
edit ps: what also disturbes me, is that loadingImage is not confined to the scope of the function. To cut things short:
NSURL *url = [NSURL URLWithString:logoPath];
NSData *data = [NSData dataWithContentsOfURL:url];
titleLogoImage.image = [UIImage imageWithData:data];
may save some headaches, at least.
There is nothing in the posted code that would trigger a crash. Depending on how titleLogoImage is defined, it might be affects by the autorelease.
However, beyond the problems outlined by mvds, you have no pressing needs for a localized autorelease pool. It will do nothing here but cause you trouble.
Autorelease pools are dangerous and not for beginners. They will kill any autoreleased objects in them. You generally only create your own pool when you are rapidly creating a large number of memory intensive objects. That does not appear to be the case here.
Despite the attention given them, custom pools are seldom used. After over a decade Apple API work, I can probably count on my fingers the number of times I have used my own custom pool.