Background computation on iPad blocks user interface - ios

I'm trying to use background threds to do some computations on an iPad.
The thing is even thou the computationa are running. The UI is blocked while they run...
What am I doing wrong.
[mc evaluateFormula:adapted runNo:10000];
This is called from an IBAction.
This is the code that is called:
-(void)evaluateFormula:(NSDictionary *)frm runNo:(NSUInteger)runCount
{
self.runCount = runCount;
self.frm = frm;
[self performSelectorInBackground:#selector(backgroundEvalFrm) withObject:nil];
// for (int i = 0; i < runCount; i++) {
// [self runFormula:frm];
// }
//
}
-(void)backgroundEvalFrm
{
percentVal = self.runCount / 100;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:20];
for (int i = 0; i<self.runCount; i++) {
NSInvocationOperation *op =[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(runFormula:) object:self.frm];
[queue addOperation:op];
}
}
So why is the UI blocked?
Here is the thread return code... it's all in the same class
-(void)runFormula:(NSDictionary *)frm
{
NSMutableString *formula = [[frm objectForKey:kFormulaExpresion] mutableCopy];
NSArray *variables = [frm objectForKey:kVariableArray];
NSArray *evals = [self evaluateVariables:variables];
for (NSDictionary *var in evals) {
NSString *sym = [var objectForKey:kVariableSymbol];
[formula replaceOccurrencesOfString:sym withString:[[var objectForKey:#"numVal"] stringValue] options:NSCaseInsensitiveSearch range:NSMakeRange(0, [formula length])];
}
//parse formula
//NSLog(#"formula to parse:%#",formula);
NSNumber *resNo = [formula numberByEvaluatingString];
// NSLog(#"formula %# the result : %f",formula,[resNo doubleValue]);
//NSNumber *resNo = [NSNumber numberWithDouble:result];
[self performSelectorOnMainThread:#selector(addNewResult:) withObject:resNo waitUntilDone:NO];
}
#pragma mark -- data aggregation delegate
-(void)addNewResult:(NSNumber *)nr
{
NSLog(#"index : %i result: %f",currentIndex,[nr doubleValue]);
[[self delegate] didReceiveResult:nr];
resultsArray[currentIndex]=[nr doubleValue];
currentIndex ++;
if ( (currentIndex % percentVal) == 0) {
[[self delegate] percentCompleted];
}
}

if your calculations are all competing for the same resource (i.e. CPU or I/O) in an uncoordinated manner (highly probable), then you should significantly lower the maximum concurrent operation count -- try 2. chances are, they will complete using less time/energy. furthermore, the main thread will not be reduced to less than 5% of the CPU time during the period that the calculations are executing (result: more responsive UI).

Related

Executes same code more than once. How to use Goto statement inside for loop

I have code which is used more than two times in different condition in same function. So I decided to use Goto statement. But that code will be executed inside for loop. So I don't understand how to call same code in same function. I don't want to create one more function. My code is...
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int maxTag = 0;
if (sTagArray.count != 0)
{
maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
for (int i = maxTag + 1; i <= [D_Tag_Value intValue]; i++)
goto add_value;
}
else
goto add_value;
add_value:
{
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}
Code inside add_value executed in for loop and also in else part. So I don't know how to manage this.
Define a block inside your function
void(^theBlock)(void) = ^(){
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
};
I don't fully understand what do you do in your add_value. If it can change to a block receive some parameters and return some value that would be better
after that you simply call the block
theBlock();
The code doesn't actually depend on the loop counter, so it isn't too hard to refactor the code so that you can simply execute the loop the appropriate number of times.
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int loopCount = 1;
if (sTagArray.count != 0) {
int maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
loopCount = [D_Tag_Value intValue] - maxTag;
}
for (int i = 0; i < loopCount ; i++) {
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}

Memory leak using dataWithContentsOfURL in a loop

I'm using Xcode 4.6.3 and iOS 5.5/6.1.6 .
I am using a background thread to load large quantities of jpg from a server to iOS devices.
dispatch_async(kBgQueue, ^
{
// get the array of filenames to download
NSURL* url = [NSURL URLWithString:webPath];
NSArray* theArray = [NSArray arrayWithContentsOfURL:url];
if( theArray )
{
dispatch_async(dispatch_get_main_queue(), ^{
// disable screen buttons
[self setButtons:false];
});
[self loadImagesFromList:theArray sourceBundle:bundlePath destBundle:localBundlePath manager:manager];
if (!stopFlag) {
// if no memory error has occurred
NSLog(#"calling refresh after load_images");
dispatch_async(dispatch_get_main_queue(), ^{
[self refresh];
});
}
theArray = nil;
}
else
{
NSLog(#"Error loading bundle");
}
});
The background method:
-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndictor startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self.progressIndictor setProgress:0 animated:NO];
});
NSURL *url;
NSString *srcFile;
NSString *destFile;
NSError *error = nil;
int counter = 0;
float prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
for (NSString *file in theArray)
{
if (stopFlag) {
NSLog(#"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
srcFile = [bundlePath stringByAppendingPathComponent:file];
destFile = [localBundlePath stringByAppendingPathComponent:file];
counter += 1;
prog += increment;
if (counter == stepSize) {
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndictor.progress = prog;
});
counter = 0;
}
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:#"jpg"]) {
url = [NSURL URLWithString:srcFile];
data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
data = nil;
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}
}
If the files exist, the loop zips through the array and exits back to the main thread ok.
If any files are missing, the download/write part seems to chew up the RAM and cause low memory warning to trigger. It takes several thousand files to do it.
I've tried declaring the variables outside the loop, and even doing the whole thing in the main thread to test if that was causing the leak.
I tried using the alternate dataWithContentsOfURL:options:error call.
I tried Instruments, but it is really slow and crashes often. Before crashing, it does show allocation going up, up, up slowly.
After several days on this, I'm stumped.
The first thing I'd suggest is using an #autoreleasepool to control the peak amount of memory consumed. Right now, you're downloading the contents into the NSData as an autorelease object, and when done, you're nil-ing that variable, which simple flags it to be deallocated once the autorelease pool is drained (which will not happen until loadImagesFromList is done). By (a) moving the variable declarations inside the for loop; and (b) wrapping this in an #autoreleasepool, your memory will be deallocated as the individual downloads finish.
-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
// your UI update here
});
int counter = 0;
float prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
for (NSString *file in theArray)
{
#autoreleasepool {
if (stopFlag) {
NSLog(#"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
NSString *srcFile = [bundlePath stringByAppendingPathComponent:file];
NSString *destFile = [localBundlePath stringByAppendingPathComponent:file];
counter += 1;
prog += increment;
if (counter == stepSize) {
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndictor.progress = prog;
});
counter = 0;
}
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
NSError *error = nil;
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:#"jpg"]) {
NSURL *url = [NSURL URLWithString:srcFile];
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}
}
}
You might want to refactor this code to use NSOperationQueue. This addresses the peak memory issue, but also let's you enjoy a degree of concurrency. Because iOS only allows 4-5 concurrent requests anyway, you want to limit the maximum number of concurrent operations to a reasonable number, and this mitigates network timeout risks if trying to run too many concurrent requests. (This maxConcurrentOperationCount feature is the main reason I suggest using operation queues.)
Anyway, that might look like:
-(void)loadImagesFromList:(NSArray *)theArray
sourceBundle:(NSString *)bundlePath
destBundle:(NSString *)localBundlePath
manager:(NSFileManager *)manager {
// initialize the progress and activity indicator
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// your UI update here
}];
int __block counter = 0;
float __block prog = 0;
float increment = 1.0 / [theArray count];
float stepSize = [theArray count] / 10;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
for (NSString *file in theArray)
{
[queue addOperationWithBlock:^{
if (stopFlag) {
NSLog(#"I see stopFlag = true, counter = %d, prog = %f", counter, prog);
return;
}
NSString *srcFile = [bundlePath stringByAppendingPathComponent:file];
NSString *destFile = [localBundlePath stringByAppendingPathComponent:file];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
counter += 1;
prog += increment;
if (counter == stepSize) {
self.progressIndictor.progress = prog;
counter = 0;
}
}];
// only download if file isn't already here
BOOL fileExists = [manager fileExistsAtPath:destFile]; // check if we already have it
if (!fileExists) {
NSError *error = nil;
// jpg or folder check
if ([[destFile pathExtension] isEqualToString:#"jpg"]) {
NSURL *url = [NSURL URLWithString:srcFile];
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
[data writeToFile:destFile options:NSDataWritingAtomic error:&error];
} else {
[manager createDirectoryAtPath:destFile withIntermediateDirectories:YES attributes:nil error:&error];
}
}
}];
}
}
There are other refinements I might suggest (e.g. implementing cancelation logic rather than looking at stopFlag), but I was trying to minimize the code changes. I'm just taking advantage of the fact that one can easily replace dispatch_async:
dispatch_async(dispatchQueue, ^{ ... });
with NSOperationQueue method addOperationWithBlock:
[operationQueue addOperationWithBlock:^{ ... }];
But now we can use a concurrent NSOperationQueue with maxConcurrentOperationCount of 4 or 5, and you suddenly enjoy a nice, constrained degree of concurrency. You may find that this is observably faster than downloading files sequentially.

Allocating NSString repeatedly in a loop while avoiding memory leak

I am playing around with NSOperationQueue in order to run some code in the background and have it update a UILabel. Here's the viewDidLoad.
- (void)viewDidLoad
{
[super viewDidLoad];
queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(counterTask) object:nil];
[queue addOperation:operation];
}
And here's the method called as the invocation operation:
- (void)counterTask {
for (int i=0; i<5000000; i++) {
if (i % 100 == 0) {
[self.firstLabel performSelectorOnMainThread:#selector(setText:)
withObject:[NSString stringWithFormat:#"%d", i]
waitUntilDone:YES];
}
}
[self.firstLabel performSelectorOnMainThread:#selector(setText:) withObject:#"finished." waitUntilDone:NO];
}
As the loop counts up, and more and more #"%d" NSStrings are created, the memory usage naturally goes up. Once the loop finishes however, the memory doesn't seem to deallocate. I expected the memory to fall as the setText: message uses new instances of NSString and releases the old ones.
If I change the loop condition to i<5000000*2, the memory usage is roughly double by the end – so it's definitely something happening on each iteration causing the leak.
Why is memory leaking here?
EDIT: Forgot to mention that I'm using ARC.
ARC doesn't remove retain / release / autorelease, it just controls the calling of these methods. You can add your own autorelease pool into your loop to force cleanup as it goes:
for (int i=0; i<5000000; i++) {
if (i % 100 == 0) {
#autoreleasepool {
[self.firstLabel performSelectorOnMainThread:#selector(setText:)
withObject:[NSString stringWithFormat:#"%d", i]
waitUntilDone:YES];
}
}
}
Let's try:
- (void)counterTask {
#autoreleasepool {
for (int i=0; i<5000000; i++) {
if (i % 100 == 0) {
[self.firstLabel performSelectorOnMainThread:#selector(setText:)
withObject:[NSString stringWithFormat:#"%d", i]
waitUntilDone:YES];
}
}
}
[self.firstLabel performSelectorOnMainThread:#selector(setText:) withObject:#"finished." waitUntilDone:NO];
}
What happening in your loop that you are creating NSString and ARC add it to auto release pool.
not releases the memory(NSString*) immediately , will release later.
One more thing is that, actually performSelectorOnMainThread retains both target and objects.
So best way is that you create nsstring instance after sending it to selector set it to nil so ARC will release it.
NSString* strText=[[NSString alloc]initWithFormat:#"%d",i ];
[self.firstLabel performSelectorOnMainThread:#selector(setText:)
withObject:strText
waitUntilDone:YES];
strText=nil;
IMO, the suggested approach of #Wain should fix the issue.
But you may also use this:
- (void)counterTask {
assert([NSThread currentThread] != [NSThread mainThread]);
for (int i=0; i<5000000; i++) {
if (i % 100 == 0) {
dispatch_sync(dispatch_get_main_queue(), ^{
#autoreleasepool {
self.firstLabel.text = [NSString stringWithFormat:#"%d", i];
}
});
}
}
dispatch_async(dispatch_get_main_queue, ^{
self.firstLabel.text = #"finished";
});
}
Try this
(void)counterTask {
__weak NSString *str;
for (int i=0; i<50000; i++) {
if (i % 100 == 0) {
str = [NSString stringWithFormat:#"%d", i];
[self.logInTxtField performSelectorOnMainThread:#selector(setText:)
withObject:str
waitUntilDone:YES];
}
}
[self.logInTxtField performSelectorOnMainThread:#selector(setText:) withObject:#"finished." waitUntilDone:NO];
}

iOS - NJSONSerialization causing a memory leak??(according to Instruments)

Below is the method with the code, where I am getting a memory leak using manual memory management. The memory leak is detected by using Xcode instruments and specifically points to the line where I am using NSJSONSerialization. I am running the target app (on a device with iOS 6.1).
The first time that i tap on the refreshButton there is no leak. Any subsequent tap generates the leak(and more leaks on top of that if i continue tapping the button). Below is the code - This is basic stuff for consuming JSON web services(the web service link is bogus but the real one that I am using works). You will notice that I am using Grand Central Dispatch so that I can update the UI without waiting for the parsing of the JSON to finish.
The line detected by instruments is surrounded by the asterisks. I would like to get some help to anyone who might have an idea of what is going on here. The full stack trace(as mentioned in the below comments i will put here:)
+(NSJSONSerialization JSONObjectWithData:option:error:] -> -[_NSJSONReader parseData:options:] -> -[_NSJSONReader parseUTF8JSONData:skipBytes:options]->newJSONValue->newJSONString->[NSPlaceholde‌​rString
initWithBytes:length:encoding:]
-(void)parseDictionary:(NSDictionary *)dictonary{
self.transactions = [dictonary objectForKey:#"transactions"];
if(!self.transactions){
NSLog(#"Expected 'transactions' array");
return;
}
for (int arrayIndex = 0; arrayIndex < [self.transactions count]; arrayIndex++) {
TransactionResult *result = [[[TransactionResult alloc] init] autorelease];
result.transactionID = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"ID"];
result.transactionDescription = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"description"];
result.transactionPrice = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"price"];
self.totalPrice += [result.transactionPrice doubleValue];
NSLog(#"total price: %f", self.totalPrice);
[self.transactionResults addObject:result];
result = nil;
}
}
- (IBAction)refreshButtonPressed:(UIBarButtonItem *)sender {
__block id resultObject;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:#"http://mywebservice.php"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
***resultObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];***
if(!error){
if([resultObject isKindOfClass:[NSDictionary class]]){
NSDictionary *dictonary = resultObject;
[self parseDictionary:dictonary];
NSLog(#"Done parsing!");
dispatch_async(dispatch_get_main_queue(), ^{
self.isLoading = NO;
[self.transactionsTableView reloadData];
});
}
else{
NSLog(#"JSON Error: Expected Dictionary");
resultObject = nil;
return;
}
}
else{
NSLog(#"JSON Error: %#", [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
resultObject = nil;
[self.transactionsTableView reloadData];
[self showError];
});
return;
}
});
}
I used a ARC as soon as it came out with 4.3, and put an app in the store with it - point being you could switch to ARC. That said, I tried to reproduce your problem by creating a class/file that has the no-arc flag applied to it, but cannot reproduce the problem. This makes me believe your problem is elsewhere. In the code below, I create a Test object in another file, retain it, and send it the test message. No matter what I set "i" to, it always deallocs the object:
#import "Tester.h"
#interface Obj : NSObject <NSObject>
#end
#implementation Obj
- (id)retain
{
NSLog(#"retain");
id i = [super retain];
return i;
}
- (oneway void)release
{
NSLog(#"release");
[super release];
}
- (void)foo
{
}
- (void)dealloc
{
NSLog(#"Obj dealloced");
[super dealloc];
}
#end
#implementation Tester
- (void)test
{
int i = 2;
__block Obj *obj;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
obj = [[Obj new] autorelease];
if(i == 0) {
Obj *o = obj;
dispatch_async(dispatch_get_main_queue(), ^
{
[o foo];
} );
} else if(i == 1) {
obj = nil;
} else if(i == 2) {
dispatch_async(dispatch_get_main_queue(), ^
{
obj = nil;
} );
}
} );
}
#end

Coredata working with data (Multithreading)

I need to place hotspots to the Map. All of them are saved with CoreData (it's about 25000).
So I need 25000 annotations. I also implement MKAnnotation protocol for HotSpot Entity.
Selected solution for this situation was multithreading. But after pins are on the map some of them has no data (data ).
here is code that populate data in array
- (void)addAnnotationsForCurrentLocation {
NSInteger hotSpotsCount = [HotSpot MR_countOfEntities];
self.testSpotsArray = [[NSMutableArray alloc] initWithCapacity:hotSpotsCount];
NSInteger lastThread;
NSInteger threads = 5;
//calculate how much spots will be in the
NSInteger spotsInThread = hotSpotsCount/threads;
//calclulate how mush threads will be in one spot
lastThread = hotSpotsCount - spotsInThread*(threads-1);
dispatch_queue_t firstThreadQueue = dispatch_queue_create("com.thecloud.firstThreadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t secondThreadQueue = dispatch_queue_create("com.thecloud.secondThreadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t thirdThreadQueue = dispatch_queue_create("com.thecloud.thirdThreadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t forthThreadQueue = dispatch_queue_create("com.thecloud.forthThreadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t fifthThreadQueue = dispatch_queue_create("com.thecloud.fifthThreadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t fillArrayGroup = dispatch_group_create();
NSLock *arrayLock = [[NSLock alloc] init];
dispatch_group_async(fillArrayGroup, firstThreadQueue, ^{
[self fetchWithOffest:0 andLimit:spotsInThread andLock:arrayLock];
DDLogInfo(#"com.thecloud.firstThreadQueue and self.testSpotsArray objects - %i", [self.testSpotsArray count]);
});
//Other Queue
dispatch_group_notify(fillArrayGroup, dispatch_get_main_queue(), ^{
[self.treeController setAnnotations:self.testSpotsArray];
});
dispatch_release(fillArrayGroup);
}
- (void)fetchWithOffest:(NSInteger)offset andLimit:(NSInteger)limit andLock:(NSLock *)arrayLock {
NSFetchRequest *request = [HotSpot MR_requestAll];
[request setFetchOffset:offset];
[request setFetchLimit:limit];
[request setReturnsObjectsAsFaults:NO];
NSArray *array = [HotSpot MR_executeFetchRequest:request];
for (int i=0; i < [array count]; i++) {
HotSpot *spot = (HotSpot *)[array objectAtIndex:i];
[spot convertLogitudeAndLattitudeToLocationCoordinate];
[arrayLock lock];
[self.testSpotsArray addObject:spot];
[arrayLock unlock];
}
}
After this when I tap on some of pins no popup with description appears.
Any solution to have full data in each object in the array of annotations?
UPD
Problem solved. I did change in my entity model. Now when I add the entity to annotations array instead of
-(NSString *)title {
return self.title;
}
-(NSString *)subtitle {
return self.spotToAddress.addressLine1;
}
-(CLLocationCoordinate2D) coordinate {
return CLLocationCoordinate2DMake([self.latitude doubleValue], [self.longitude doubleValue]);
}
I use direct assignment
- (void)prepareAnnotation {
_title = self.name;
_subtitle = self.spotToAddress.addressLine1;
_coordinate = CLLocationCoordinate2DMake([self.latitude doubleValue], [self.longitude doubleValue]);
}
Problem solved. I did change in my entity model. Now when I add the entity to annotations array instead of
-(NSString *)title {
return self.title;
}
-(NSString *)subtitle {
return self.spotToAddress.addressLine1;
}
-(CLLocationCoordinate2D) coordinate {
return CLLocationCoordinate2DMake([self.latitude doubleValue], [self.longitude doubleValue]);
}
I use direct assignment
- (void)prepareAnnotation {
_title = self.name;
_subtitle = self.spotToAddress.addressLine1;
_coordinate = CLLocationCoordinate2DMake([self.latitude doubleValue], [self.longitude doubleValue]);
}

Resources