Waiting for CLGeocoder to finish on concurrent enumeration - ios5

I have the following bit of code in a class method
NSDictionary *shopAddresses = [[NSDictionary alloc] initWithContentsOfFile:fileName];
NSMutableArray *shopLocations = [NSMutableArray arrayWithCapacity:shopAddresses.count];
[shopAddresses enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key, ShopLocation *shopLocation, BOOL *stop) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:shopLocation.address completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"Geocode failed with error: %#", error);
}
else {
shopLocation.placemark = [placemarks objectAtIndex:0];
}
[shopLocations addObject:shopLocation];
}];
}
After execution of this code, I want to return the shopLocations array as a result for the method. However I need to somehow wait until all geocoder searches have finished if I don't want the array to be empty.
How can I do this?
I have tried different GCD approaches, but haven't been successful so far.

This can be handled by the dispatch_group_... functions:
…
dispatch_group_t group = dispatch_group_create();
[shopAddresses enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) {
dispatch_group_enter(group);
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:shopLocation.address completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"Geocode failed with error: %#", error);
}
else {
shopLocation.placemark = [placemarks objectAtIndex:0];
}
[shopLocations addObject:shopLocation];
dispatch_group_leave(group);
}];
}];
while (dispatch_group_wait(group, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.f]];
}
dispatch_release(group);
…
I'm using these kind of blocks to accumulate some network requests.
I hope this can help.

Related

Object is being saved before I can set the variable. How do I get around this?

I have view controller that prompts the user to enter in some location information, then click submit. When that happens, the data is thrown into a place dictionary and then geocoded through the methods updatePlaceDictionary and geocode. [userListing saveInBackground] then sends the object to an online database. Here is the submit method, which is called when the user fills in the information and clicks submit, along with the updatePlaceDictionary and geocode methods:
- (void)submit{
PFObject* userListing = [PFObject objectWithClassName:#"userListing"];
[self updatePlaceDictionary];
[self geocode];
[userListing setObject:listingLocation forKey:#"location"];
[userListing saveInBackground];
[listings addObject:userListing];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)updatePlaceDictionary {
[self.placeDictionary setValue:self.streetField.text forKey:#"Street"];
[self.placeDictionary setValue:self.cityField.text forKey:#"City"];
[self.placeDictionary setValue:self.stateField.text forKey:#"State"];
[self.placeDictionary setValue:self.zipField.text forKey:#"ZIP"];
}
- (void)geocode{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressDictionary:self.placeDictionary completionHandler:^(NSArray *placemarks, NSError *error) {
if([placemarks count]) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D coordinate = location.coordinate;
listingLocation = [PFGeoPoint geoPointWithLatitude:coordinate.latitude longitude:coordinate.longitude];
} else {
NSLog(#"error");
}
}];
}
All three methods work perfectly fine. The problem is, in the submit method, the line:
[userListing setObject:listingLocation forKey#"location"];
just ends up giving the key "location" a value of (0,0). This is occurring because geocode runs asynchronously, and does not finish by the time the above line is reached. How can I have this value set AFTER geocode is finished running?
You entire code here is going to have to be a little asynchronous. Your geocode method can have a block callback passed in, and call it when you are done with the geocode.
- (void)submit{
PFObject* userListing = [PFObject objectWithClassName:#"userListing"];
[self updatePlaceDictionary];
[self geocode:^{
[userListing setObject:listingLocation forKey:#"location"];
[userListing saveInBackground];
[listings addObject:userListing];
[self.navigationController popViewControllerAnimated:YES];
}];
}
- (void)updatePlaceDictionary {
[self.placeDictionary setValue:self.streetField.text forKey:#"Street"];
[self.placeDictionary setValue:self.cityField.text forKey:#"City"];
[self.placeDictionary setValue:self.stateField.text forKey:#"State"];
[self.placeDictionary setValue:self.zipField.text forKey:#"ZIP"];
}
- (void)geocode:(void (^)(void))callback {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressDictionary:self.placeDictionary completionHandler:^(NSArray *placemarks, NSError *error) {
if([placemarks count]) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D coordinate = location.coordinate;
listingLocation = [PFGeoPoint geoPointWithLatitude:coordinate.latitude longitude:coordinate.longitude];
if (callback) {
callback();
}
} else {
NSLog(#"error");
}
}];
}

Multiple sharedInstance called failed

In my application i have called sharedinstance multiple time in multiple method deffinition ,
Here my code,
Method 1
-(void) showActionSheet:(id)sender forEvent:(UIEvent*)event
{
if(isQuantity==YES)
{
[[WebService sharedInstance] getQuantity:^(BOOL result)
{
if(result)
{
NSLog(#"success");
NSManagedObjectContext *context = [[DataAccessLayer sharedInstance] managedObjectContext];
Quantity = [context fetchObjectsForEntityName:NSStringFromClass([GetQuantity class]) withSortColumn:nil withSortDescending:TRUE withPredicate:nil];
NSLog(#"array ->%#",Quantity);
isQuantity=NO;
}
}];
}
popoverController1 = [[TSPopoverController alloc]initWithContentViewController:tableViewController1];
popoverController1.cornerRadius = 5;
popoverController1.titleText = #"Quantity";
popoverController1.popoverBaseColor = [UIColor blackColor];
popoverController1.popoverGradient= NO;
[popoverController1 showPopoverWithTouch:event];
}
Method 2
-(void) showActionSheetw:(id)sender forEvent:(UIEvent*)events
{
if(isSize==YES)
{
[[WebService sharedInstance] getDimension:^(BOOL result)
{
if(result){
NSLog(#"success");
NSManagedObjectContext *context = [[DataAccessLayer sharedInstance] managedObjectContext];
dime = [context fetchObjectsForEntityName:NSStringFromClass([Getdimension class]) withSortColumn:nil withSortDescending:FALSE withPredicate:nil];
NSLog(#"array ->%#",dime);
}
}];
}
popoverController2 = [[TSPopoverController alloc] initWithContentViewController:tableViewController2];
popoverController2.cornerRadius = 5;
popoverController2.titleText = #"Size";
popoverController2.popoverBaseColor = [UIColor blackColor];
popoverController2.popoverGradient= NO;
// popoverController.arrowPosition = TSPopoverArrowPositionHorizontal;
[popoverController2 showPopoverWithTouch:events];
}
EDIT
- (void) getDimension:(void (^)(BOOL))handler
{
JBContainedURLConnection *connection = [[JBContainedURLConnection alloc]init ];
[connection initWithGETUrl:IP methodName:GETDIMENSION param:nil andCompletionHandler:^(JBContainedURLConnection *connection, NSError *error, NSString *urlString, NSDictionary *userInfo, NSData *response)
{
if(error)
{
NSLog(#"Error: %#", error);
handler(FALSE);
}
else
{
if(response == nil)
handler(FALSE);
else
{
NSManagedObjectContext *context = [[DataAccessLayer sharedInstance] managedObjectContext];
NSArray *existingResults = [context fetchObjectsForEntityName:NSStringFromClass([Getdimension class]) withSortColumn:nil withSortDescending:FALSE withPredicate:nil];
for (NSManagedObject *obj in existingResults)
[context deleteObject:obj];
[[DataAccessLayer sharedInstance] saveContext];
id responseData = [self DictionaryFromResponse:response];
if(responseData == nil)
handler(FALSE);
else
{
NSLog(#"Dimension Response: %#", [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]);
NSArray *data=[responseData objectForKey:#"GetDimensionResult"];
NSLog(#"GetDimensionResult :%#",data);
for( NSDictionary *dict in data){
Getdimension *userDetails = [Getdimension newObject];
[userDetails fillFromDictionary:dict];
}
[[DataAccessLayer sharedInstance] saveContext];
handler(TRUE);
}
} }
}];
}
- (void) getQuantity:(void (^)(BOOL))handler
{
JBContainedURLConnection *connection = [[JBContainedURLConnection alloc]init ];
[connection initWithGETUrl:IP methodName:GETQUANTITY param:nil andCompletionHandler:^(JBContainedURLConnection *connection, NSError *error, NSString *urlString, NSDictionary *userInfo, NSData *response)
{
if(error)
{
NSLog(#"Error: %#", error);
handler(FALSE);
}
else
{
if(response == nil)
handler(FALSE);
else
{
NSManagedObjectContext *context = [[DataAccessLayer sharedInstance] managedObjectContext];
NSArray *existingResults = [context fetchObjectsForEntityName:NSStringFromClass([GetQuantity class]) withSortColumn:nil withSortDescending:FALSE withPredicate:nil];
for (NSManagedObject *obj in existingResults)
[context deleteObject:obj];
[[DataAccessLayer sharedInstance] saveContext];
id responseData = [self DictionaryFromResponse:response];
if(responseData == nil)
handler(FALSE);
else
{
NSLog(#"GetQuantityResult Response: %#", [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]);
NSArray *data=[responseData objectForKey:#"GetQuantityResult"];
// NSLog(#"GetDimensionResult :%#",data);
for( NSDictionary *dict in data){
GetQuantity *userDetails = [GetQuantity newObject];
[userDetails fillFromDictionary:dict];
}
[[DataAccessLayer sharedInstance] saveContext];
handler(TRUE);
}
} }
}];
}
Instance method
+ (id)sharedInstance
{
#synchronized(self)
{
if (manager == nil)
manager = [[self alloc] init];
}
return manager;
}
-(id)init
{
if(self = [super init])
{
}
return self;
}
-(NSString *)NSStringFromDictionaryUsingJSON:(id)dictionary
{
SBJsonWriter *writer = [[SBJsonWriter alloc]init];
return [writer stringWithObject:dictionary];
}
-(id)DictionaryFromResponse:(NSData *)response
{
NSString *responseBody = [[NSString alloc] initWithData:response encoding:NSASCIIStringEncoding];
SBJsonParser *parser = [[SBJsonParser alloc]init];
return [parser objectWithString:responseBody error:nil];
}
sharedInstance only works one time,ie,. if i call any of the method first its worked,if calls other method second time app gets crashed.Can any one please help me to sort it out
I guess sharedInstance method is messy.
It should be
+ (id)sharedInstance
{
if (manager == nil)
manager = [[self alloc] init];
return manager;
}
Enjoy Programming !
Have you declared instance of your class as static,
Declare you class object as :
static ClassName *manager;
And the allocate the same object in your sharedInstance method.
The reason may be that the memory object was released but reference is still there in the memory.So when you execute the shared instance method it found a reference to !nil object and leads your application to crash.
This is singleton class feature of iOS(objective C)

iOS forward geocoding block not being executed

Why this block of code isn't being executed? I copied and pasted it from another project of mine, where it works just fine. I also tried it in my other app with the same addressString I'm plugging in here, and it worked perfectly.
NSString *addressString = [NSString stringWithFormat:#"%# and %#, %#, NY", street, rightBound, [boroughs objectForKey:borough]];
NSLog(#"Address string: %#",addressString);
[geocoder geocodeAddressString:addressString completionHandler:^(NSArray *placemarks, NSError *error)
{
NSLog(#"Placemark count:%d",[placemarks count]);
for(CLPlacemark *placemark in placemarks)
{
NSLog(#"%#",placemark);
}
if(anError)
{
NSLog(#"Error: %#",[error description]);
}
}];
Neither any placemarks nor an error message is logged to the console.
Here is my entire AppDelegate.m:
#implementation AppDelegate
#synthesize window = _window;
- (void)dealloc
{
[_window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSError *error = nil;
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSString *JSONString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Streets" ofType:#"json"] encoding:NSUTF8StringEncoding error:&error];
if(error)
{
NSLog(#"%#",[error description]);
NSLog(#"Break");
}
NSDictionary *dict = [parser objectWithString:JSONString error:&error];
if(error)
{
NSLog(#"%#",[error description]);
NSLog(#"Break");
}
NSArray *addresses = [[dict objectForKey:#"results"] retain];
NSDictionary *boroughs = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"Bronx",#"Brooklyn",#"New York", #"Queens",#"Staten Island",nil] forKeys:[NSArray arrayWithObjects:#"B",#"K",#"M",#"Q",#"S", nil]];
int i = 1;
for(NSDictionary *file in addresses)
{
NSString *borough = [file objectForKey:#"Borough"];
NSString *ID = [file objectForKey:#"ID"];
NSString *leftBound = [file objectForKey:#"LeftBound"];
NSString *rightBound = [file objectForKey:#"RightBound"];
NSString *sideOfStreet = [file objectForKey:#"SideOfStreet"];
NSString *street = [file objectForKey:#"Street"];
NSString *addressString = [NSString stringWithFormat:#"%# and %#, %#, NY", street, rightBound, [boroughs objectForKey:borough]];
// NSLog(#"Address string: %#",addressString);
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:addressString completionHandler:^(NSArray *placemarks, NSError *anError)
{
NSLog(#"AAAAAAAAAAAAAAAAAAAAAAAA");
NSLog(#"Placemark count:%d",[placemarks count]);
for(CLPlacemark *placemark in placemarks)
{
NSLog(#"Placemark: %#",placemark);
}
if(anError)
{
NSLog(#"Error: %#",[error description]);
}
}];
[geocoder release];
NSLog(#"%d",i++);
}
[parser release];
[addresses release];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"geocoder"])
{
NSLog(#"AAAAAAA");
}
}
#end
Have you made sure your "geocoder" instance is not nil?
Nothing will happen if you send a message to a "nil" object... :)
NSLog(#"%#",geocoder);
I'm still not sure what's going on. I ran the following code (original code minus the JSON operations) on my simulator and it worked perfectly. Maybe you need a fresh install of Xcode & iOS Simulator?
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *addressString = [NSString stringWithFormat:#"1 Infinite Loop, Cupertino, CA"];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:addressString completionHandler:^(NSArray *placemarks, NSError *anError)
{
NSLog(#"AAAAAAAAAAAAAAAAAAAAAAAA");
NSLog(#"Placemark count:%d",[placemarks count]);
for(CLPlacemark *placemark in placemarks)
{
NSLog(#"Placemark: %#",placemark);
}
if(anError)
{
NSLog(#"Error: %#",[anError description]);
}
}];
[geocoder release];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#end
Output was:
2011-12-21 18:56:30.311 Test[44445:f803] AAAAAAAAAAAAAAAAAAAAAAAA
2011-12-21 18:56:30.312 Test[44445:f803] Placemark count:1
2011-12-21 18:56:30.314 Test[44445:f803] Placemark: 1 Infinite Loop, Cupertino, CA 95014-2083, United States # <+37.33168400,-122.03075800> +/- 100.00m, region (identifier <+37.33168400,-122.03075800> radius 71.01) <+37.33168400,-122.03075800> radius 71.01m
The only thing I can think of is that you may be releasing your geocoder too early! Maybe try moving the release into the block? This way, you'll know the geocoder is only being released once after it has finished the geocode operation.
Also, you made a mistake with your error handling inside the block.
It should be NSLog(#"Error: %#",[anError description]);
instead of NSLog(#"Error: %#",[error description]);.
Also, make sure you're not using ARC...
I know this is an old question but the reason this doesn't work is you cannot make multiple forward requests simultaneously. You must check the property isGeocoding before sending another one.

Wait for assetForURL blocks to be completed

I would like to wait this code to be executed before to continue but as these blocks are called assynchronously I don't know how to do???
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
}
GCD semaphore approach:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for (NSURL *url in self.assetUrls) {
dispatch_async(queue, ^{
[library assetForURL:url resultBlock:^(ALAsset *asset) {
[self.assets addObject:asset];
dispatch_semaphore_signal(sema);
} failureBlock:^(NSError *error) {
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
dispatch_release(sema);
/* Check out ALAssets */
NSLog(#"%#", self.assets);
The solution is here
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/
Note that assetForURL:resultBlock:failureBlock: will stuck if the main thread is waiting without RunLoop running. This is alternative ( cleaner :-) ) solution:
#import <libkern/OSAtomic.h>
...
ALAssetsLibrary *library;
NSMutableArray *assets;
...
__block int32_t counter = 0;
for (NSURL *url in urls) {
OSAtomicIncrement32(&counter);
[library assetForURL:url resultBlock:^(ALAsset *asset) {
if (asset)
[assets addObject:asset];
OSAtomicDecrement32(&counter);
} failureBlock:^(NSError *error) {
OSAtomicDecrement32(&counter);
}];
}
while (counter > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
The easiest thing to do is to move your code to inside (at the end of) the resultBlock or the failureBlock. That way, your code will run in the correct order, and you will also retain asynchronous behaviour.
This is an easy way to do it. Maybe not as elegant as using GCD but it should get the job done ... This will make your method blocking instead of non-blocking.
__block BOOL isFinished = NO;
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
if (objectsToRemove.count + tmpListAsset.count == assetsList.count) {
isFinished = YES;
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
isFinished = YES;
}];
}
while (!isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
}

Problem passing NSError back as a return parameter

I am having a problem passing an NSError object back. The first line of code to access the object (in this case, I inserted an NSLog) causes "EXC_BAD_ACCESS".
Is this because I am not explicitly creating an NSError object, but rather getting one from the NSURLRequest and passing it back? In this particular function (downloadFile:), some errors I want to retrieve from other functions, but I create an NSError on two other occasions in the function.
Any help is appreciated.
Here is the offending code:
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
//"EXC_BAD_ACCESS" on calling NSLog on the next line?
NSLog(#"Error downloading Plist: %#", [err localizedDescription]);
[self performSelectorOnMainThread:#selector(fetchPlistFailed:) withObject:err waitUntilDone:NO];
[localPool drain], localPool = nil;
return NO;
}
//Removed the remainder of the code for clarity.
[localPool drain], localPool = nil;
return YES;
}
-(BOOL)downloadFile:(NSString *)fileName
withRemoteDirectory:(NSString *)remoteDirectory
withLocalDelete:(BOOL)withLocalDelete
withContentType:(NSString *)contentTypeCheckString
Error:(NSError **)error {
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
NSError *localError = nil;
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSString *urlString = [NSString stringWithFormat:#"http://XXXXX/%#", fileName];
NSLog(#"Downloading file: %#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&localError];
[req release];
if (response == nil || localError) {
NSLog(#"Error retrieving file:%#", [localError localizedDescription]);
if (error != NULL) {
*error = localError;
//THIS NSLog call works just fine.
NSLog(#"Error copied is:%#", [*error localizedDescription]);
}
[localPool drain], localPool = nil;
app.networkActivityIndicatorVisible = NO;
return NO;
}
//Rest of function omitted for simplicity.
}
I guess your NSError object is autoreleased and put on your localPool. You drained that localPool, thus destroying the NSError.
Do you really need localPool in every method? If not, just remove the localPools.
Also, it looks like you forgot to drain the localPool in someCode. Hopefully you just didn't copy it...
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
....
[localPool drain], localPool = nil;
return NO;
}
[localPool drain], localPool = nil; // missing
}