returning an NSObject - bad exec - xcode

I've been struggling with a problem that I hope someone can help me with.
I have Class called 'GameObjectDefinitionTable', where I set all my object properties, which is in another class called 'Product'. In my 'HelloWorldScene' I allocate 'GameObjectDefinitionTable' which in turn creates several 'Product's. Like this:
HelloWorldScene -> GameObjectDefinitionTable -> Product
I then want to return a 'Product' to 'HelloWorldScene. But here is where I get problems. Some code:
HelloWorldScene:
GameObjectDefinitionTable *god = [[GameObjectDefinitionTable alloc]init];
Product* currentProduct = [god getProductWithNum:0];
NSLog(#"currenProduct (name): %#",currentProduct.name); //Crash
GameObjectDefinitionTable:
-(void) createProducts {
Product *product;
for (int i=0; i<[allProductsWithDefinitions count];i++ ) {
product = [[Product alloc]init];
product.name = [[allProductsWithDefinitions objectAtIndex:i] objectAtIndex:0];
product.density = [[[allProductsWithDefinitions objectAtIndex:i] objectAtIndex:1] floatValue];
product.scoreValue = [[[allProductsWithDefinitions objectAtIndex:i] objectAtIndex:2] intValue];
product.fileName = [[allProductsWithDefinitions objectAtIndex:i] objectAtIndex:3];
[products addObject:product];
[product release];
}
[allProductsWithDefinitions release];
}
-(Product *) getProductWithNum:(int)tNum {
Product *tempProduct;
tempProduct = [products objectAtIndex:tNum];
return tempProduct;
[tempProduct release];
}
The arrays and all in 'GameObjectDefinitionTable' is working fine if I log in that class.
Would be really grateful for an answer :)

Is it the case you need something like:
- (Product *)getProductWithNum:(int)tNum
{
Product *tempProduct = [[products objectAtIndex:tNum] retain];
return [tempProduct autorelease];
}

Product *tempProduct;
tempProduct = [products objectAtIndex:tNum];
return tempProduct;
[tempProduct release];
Is that what you mean to have? You kind of have two big problems cancelling each other out here. The [tempProduct release]; line is unreachable. The second, if you were to actually execute [tempProduct release]; before the return, you would be releasing the memory, and then accessing the currentProduct.name property of what is essentially a dangling pointer. This illegal memory access could cause your Bad Exec.
Since you are not allocating, copying, or retaining tempProduct, you must NOT release it. Why not just a simple return [products objectAtIndex:tNum];?

Related

MapView Problems - Pin change color when map reloded

I have a problem with the PIn color mapView when a refresh is done.
In my I app i display some point with two color in order to identify if a service is available.
On the first start, no problems appear. The code is the follower:
- (void)viewDidLoad
{
[super viewDidLoad];
[self dowloadPoint]; // here I exucte the first start
}
- (void)dowloadPoint{
NSURL *url1 =[NSURL URLWithString:#"http:MYUSRL"];
NSData *datos1 =[[NSData alloc] initWithContentsOfURL:url1];
[self plotBarPosition:datos_string1]; //Here I call the plotBarPosition method
}
- (void)plotBarPosition:(NSString *)datos_string1 {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
// Parse the string into JSON
NSDictionary *json = [(NSDictionary*)[datos_string1 JSONValue]objectForKey:#"features"];
// Get the objects you want, e.g. output the second item's client id
NSArray *items_properties = [json valueForKeyPath:#"properties"];
NSArray *items_geo = [json valueForKeyPath:#"geometry"];
for (int i = 0; i < [json count]; i++){
NSString *nomprePunto =[[items_properties objectAtIndex:i] objectForKey:#"title"];
NSNumber *lat =[[[items_geo objectAtIndex:i] objectForKey:#"coordinates"] objectAtIndex:0];
NSNumber *lon =[[[items_geo objectAtIndex:i] objectForKey:#"coordinates"] objectAtIndex:1];
CLLocationCoordinate2D coordinate;
coordinate.latitude = lat.doubleValue;
coordinate.longitude = lon.doubleValue;
//ESTADO
NSString *description = [[items_properties objectAtIndex:i] objectForKey:#"description"];
NSString *estado_punto = [[NSString alloc]init];
if ([description rangeOfString:#"Averiado"].location == NSNotFound) {
estado_punto = #"Available";
} else {
estado_punto = #"NOt Available";
averiados ++;
}
NSString *averiadosStr = [NSString stringWithFormat:#"%d",averiados];
averiadosLabel.text = averiadosStr;
MyLocation *location =[[MyLocation alloc] initWithName:nomprePunto coordinate:coordinate estado:estado_punto];
[_mapView addAnnotation:location];
}
}
- (MKPinAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(MyLocation *)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MyLocation class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
if([[annotation estado] isEqualToString:#"En Servicio"])
annotationView.pinColor = MKPinAnnotationColorGreen;
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
But whe I add a refres button that is function is simply a refreshcalling the dowloadPoint once again,
- (IBAction)refresh{
[self dowloadPoint];
}
the color of pins change in a "random manner", not corrisponding with the real state of point.
Any ideas about what is happening? Thanks in advance.
EDIT: It seemps pproblems is due to:
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
erasing it, the app work properly but pins area drown abow the previous ones...:S
The default color of the pin is red. You set it to green if the estado property of your MyLocation object is equal to #"En Servicio". I understand that sometimes the color is red, when your estado property is equal to #"En Servicio", or sometimes green when it is not.
One reason could be that your MyLocation object simply does no longer exist when you press the refresh button. In this case, you might still have a pointer to the memory location where it once existed, but this location may have been overwritten by anything, causing a random color.
This can happen e.g. if your MyLocation object has been created as an autorelease object that has been released when you returned to the main event loop, i.e. to handle user interactions.
This should not be the case if you are using ARC.

MKMapAnnotations didSelectAnnotationView not responding

I am creating an app that pulls data from a server and pinpoints the different houses on a map. My problem is that it does display the annotations but when I click on them they do not respond
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
I put my annotations in an array by:
int s;
self.mapAnnotations = [[NSMutableArray alloc] init];
for(s=0; s < numberOfAnnotations; s++)
{
//NSDictionary *dict2 = [parser objectWithString:[[arrayOfResults objectAtIndex:0] description]];
CLLocationDegrees daLong = [[[[[arrayOfResults objectAtIndex:s] objectForKey:#"map"] objectForKey:#"longitude"] description] floatValue];
CLLocationDegrees daLat = [[[[[arrayOfResults objectAtIndex:s] objectForKey:#"map"] objectForKey:#"latitude"] description] floatValue];
/*self.customAnnotation = [[BasicMapAnnotation alloc] initWithLatitude:daLat andLongitude:daLong];
[self.mapView addAnnotation:self.customAnnotation];*/
BasicMapAnnotation *m = [[BasicMapAnnotation alloc] initWithLatitude:daLat andLongitude:daLong];
[self.mapAnnotations addObject:m];
}
[self.mapView addAnnotations:self.mapAnnotations];
NSLog(#"this number of annotations %d", [self.mapAnnotations count]);
I also noticed when I created a separate house to place on the map in my viewDidLoad:
self.normalAnnotation = [[BasicMapAnnotation alloc] initWithLatitude:38 andLongitude:-90.2045];
self.normalAnnotation.title = #" ";
[self.mapView addAnnotation:self.normalAnnotation];
It did work when I clicked on it, but the ones passed through the array didn't work.
Can anyone help me figure out why it's not responding?
That's because annotations should have a title for them to display a callout. In your for loop, set the title property like you did with self.normalAnnotation.title = #" ".

Adding multiple strings to a string

How do I add multiple strings to a string ?
Whats the easiest way to do that ?
If I don't want to create a new line of code every time I add something to a string, I'd like to do something like that :
NSString *recipeTitle = [#"<h5>Recipe name: " stringByAppendingFormat:recipe.name, #"</h5>"];
NSLog(#"%#", recipeTitle);
// This shows: <h5>Recipe name: myrecipe
// Where's the </h5> closing that header ? It will only show up with the next line of code
recipeTitle = [recipeTitle stringByAppendingFormat:#"</h5>"];
//my problem is that will result in more than 1k lines of programming
Do I have to necessarily add a new line appending the appended every time ?
Is there a faster/more productive way to do that ?
I'm trying to compose the email body with my tableview in it and that will result in a huge set of programming lines. Isthere anybody that could give me any hint or anything better than composing a huuuge string just so i can populate my email body with a table containing my tableview data ?
Any help to make this more productive is appreciated. Thanks !
Carlos Farini.
// After working on it a bit i got:
-(IBAction)sendmail{
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
[composer setMailComposeDelegate:self];
NSString *recipeTitle = #"<h5>Recipe name: ";
recipeTitle = [recipeTitle stringByAppendingFormat:recipe.name];
recipeTitle = [recipeTitle stringByAppendingFormat:#"</h5>"];
NSString *ingredientAmount = #"";
NSString *ingredientAisle = #"";
NSString *ingredientTitle = #"";
NSString *tableFirstLine = #"<table width='90%' border='1'><tr><td>Ingredient</td><td>Amount</td><td>Aisle</td></tr>";
NSString *increments = #"";
int i=0;
for (i=0; i < [ingredients count]; i++) {
Ingredient *ingredient = [ingredients objectAtIndex:i];
ingredientTitle = ingredient.name;
ingredientAmount = ingredient.amount;
ingredientAisle = ingredient.aisle;
increments = [increments stringByAppendingFormat:recipeTitle];
increments = [tableFirstLine stringByAppendingFormat:#"<tr><td>"];
increments = [increments stringByAppendingFormat:ingredientTitle];
increments = [increments stringByAppendingFormat:#"</td><td>"];
increments = [increments stringByAppendingFormat:ingredientAmount];
increments = [increments stringByAppendingFormat:#"</td><td>"];
increments = [increments stringByAppendingFormat:ingredientAisle];
increments = [increments stringByAppendingFormat:#"</td></tr>"];
if (i == ([ingredients count]-1)) {
//IF THIS IS THE LAST INGREDIENT, CLOSE THE TABLE
increments = [increments stringByAppendingFormat:#"</table>"];
}
}
NSLog(#"CODE:: %#", increments);
if ([MFMailComposeViewController canSendMail]) {
[composer setToRecipients:[NSArray arrayWithObjects:#"123#abc.com", nil]];
[composer setSubject:#"subject here"];
[composer setMessageBody:increments isHTML:YES];
[composer setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentModalViewController:composer animated:YES];
[composer release];
}else {
[composer release];
}
}
But then again, it's showing just one row in the table. What am I doing wrong here ?
How about something like this:
NSString *recipeTitle = [NSString stringWithFormat:#"<h5>Recipe name: %# </h5>", recipe.name];

Core Data, caching NSManagedObjects in NSMutableDictionary, Problems

I am writing a dictionary application, and i am trying to import raw data from strings, one string per word. Amongst other things, the raw input strings contain the names of the parts of speech the corresponding word belongs to. In my datamodel I have a separate entity for Words and PartOfSpeech, and i want to create one entity of the type PartOfSpeech for each unique part of speech there may be in the input strings, and establish the relationships from the Words to the relevant pars of speech. The PartOfSpeech entity has just one Atribute, name, and one-to-many relationship to the Word:
My first implementation of getting unique PartOfSpeech entities involved caching them in a mutable array and filtering it each time with a predicate. It worked, but it was slow. I decided to speed it up a bit by caching the PartsOfSpeech in an NSDictionary, and now when i try and save the datastore after the import, i get the error "Cannot save objects with references outside of their own stores.". It looks like the problem is in the dictionary, but how can i solve it?
Here is the code that worked:
(in both sniplets managedObjectContext is an ivar, and processStringsInBackground: method runs on a background thread using performSelectorInBackground:withObject: method)
- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
NSError *err = NULL;
NSFetchRequest *req = [[NSFetchRequest alloc] init];
[req setEntity:[NSEntityDescription entityForName:#"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
err = NULL;
NSMutableArray *selectedPartsOfSpeech = [[managedObjectContext executeFetchRequest:req error:&err] mutableCopy];
NSPredicate *p = [NSPredicate predicateWithFormat:#"name like[c] $name"];
// NSPredicate *formNamePredicate = [NSPredicate predicateWithFormat:<#(NSString *)predicateFormat#>]
...
for (int i = 0; i < count; i++){
...
currentPos = [self uniqueEntityWithName:#"PartOfSpeech" usingMutableArray:selectedPartsOfSpeech predicate:p andDictionary:[NSDictionary dictionaryWithObject:partOfSpeech forKey:#"name"]];
...
}
}
- (NSManagedObject *) uniqueEntityWithName:(NSString *) entityName usingMutableArray:(NSMutableArray *)objects predicate:(NSPredicate *)aPredicate andDictionary:(NSDictionary *) params {
NSPredicate *p = [aPredicate predicateWithSubstitutionVariables:params];
NSArray *filteredArray = [objects filteredArrayUsingPredicate:p];
if ([filteredArray count] > 0) {
return [filteredArray objectAtIndex:0];
}
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
NSArray *dicKeys = [params allKeys];
for (NSString *key in dicKeys) {
[newObject willChangeValueForKey:key];
[newObject setPrimitiveValue:[params valueForKey:key] forKey:key];
[newObject didChangeValueForKey:key];
}
[objects addObject:newObject];
return newObject;
}
And here is the same, but with caching using NSMutableDictionary, which fails to save afterwards:
- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
NSError *err = NULL;
[req setEntity:[NSEntityDescription entityForName:#"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
NSArray *selectedPartsOfSpeech = [managedObjectContext executeFetchRequest:req error:&err];
NSMutableDictionary *partsOfSpeechChache = [[NSMutableDictionary alloc] init];
for (PartOfSpeech *pos in selectedPartsOfSpeech) {
[partsOfSpeechChache setObject:pos forKey:pos.name];
}
...
for (int i = 0; i < count; i++){
...
currentPos = [self uniqueEntity:#"PartOfSpeech" withName:partOfSpeech usingDictionary:partsOfSpeechChache];
...
}
}
- (NSManagedObject *)uniqueEntity:(NSString *) entityName withName:(NSString *) name usingDictionary:(NSMutableDictionary *) dic {
NSManagedObject *pos = [dic objectForKey:name];
if (pos != nil) {
return pos;
}
NSManagedObject *newPos = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
[newPos willChangeValueForKey:#"name"];
[newPos setPrimitiveValue:name forKey:#"name"];
[newPos didChangeValueForKey:#"name"];
[dic setObject:newPos forKey:name];
return newPos;
}
Could you help me to find the problem?
Best regards,
Timofey.
The error is caused by forming a relationship between managedObjects that don't share the same persistent store. You can do that by:
Creating a managed object with initialization without inserting it into a context.
Deleting a managed object from a context while retaining it in another object e.g. array, and then forming a relationship with it.
Accidentally creating two Core Data stacks so that you have two context and two stores.
Confusing configurations in a multi-store context.
I don't see any part of the code you provided that would trigger the problem.
It turns out, that it is wrong to pass NSManagedContext to a thread different from the one it was created in. Instead, one should pass the NSPersistenceStroreCoordinator to another thread, and create a new managed object context there. In order to merge the changes into the "main" context, one should save the other thread's context, receive the notification on the completion of the save on the main thread and merge the changes (see apple docs regarding Core Data and concurrency, can't give you the link, because i read it in Xcode). So here are the changes i made to my code to make it work (only posting the changed lines):
— (void) processStringsInBackground:(NSDictionary *) params {
NSFetchRequest *wordStringsReq = [params objectForKey:#"wordStringsReq"];
NSPersistentStoreCoordinator *coordinator = [params objectForKey:#"coordinator"];
NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
[localContext setPersistentStoreCoordinator:coordinator];
(all the references to the managedObjectContext were replaced by localContext
And on the main thread, i call this method thusly:
.......
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:req, #"wordStringsReq", persistentStoreCoordinator, #"coordinator", nil]; //the params i pass to the background method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"NSManagingContextDidSaveChangesNotification" object:nil]; //register to receive the notification from the save
[self performSelectorInBackground:#selector(processStringsInBackground:) withObject:dict];
}
- (void) handleNotification:(NSNotification *) notific {
NSLog(#"got notification, %#", [notific name]);
[managedObjectContext mergeChangesFromContextDidSaveNotification:notific];
}
Good luck!
Good answers, though a bit dated. The fine documentation notes that the main NSManagedObjectContext should never be used in worker threads. Instead, create a separate NSManagedObjectContext private to the worker using the "main" MOC as a parent, and then that instead. Here's the relevant "Concurrency" page from the Core Data Programming Guide:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Snippet (Swift)
let jsonArray = … //JSON data to be imported into Core Data
let moc = … //Our primary context on the main queue
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock {
for jsonObject in jsonArray {
let mo = … //Managed object that matches the incoming JSON structure
//update MO with data from the dictionary
}
do {
try privateMOC.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}

Memory problems with NSMutableDictionary, causing NSCFDictionary memory leaks

Help me please with the following problem:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
// NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
// [gamesDictionary retain];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
The leak starts in another method of another class, there the getGamesList method is called, like this:
NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];
After that there are a lot of leaks that points to NSCFArray in the string:
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
if ([keys count] != 0) return [[keys objectAtIndex:section] uppercaseString];
return #"";
}
I assume these things are connected to each other, but I still can not understand all of the memory management tips.
Thanks a lot!
Didn't use Cocoa for years (that's why I can't tell you an exact answer :/). But I guess your problem is that you systematically use retain on your objects.
Since the object reference count never get to 0, all dictionaries are keep in memory and not freed.
Try to remove the retain on [NSArray arrayWithArray] and [NSMutableDictionary dictionaryWithDictionary
http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release
It does look like you are over-retaining your array.
When you create the gamesDictionary it is created with an retain count of +1. You then retain it (count becomes +2). When you get the value outside this function you retain again (count becomes +3).
You are correct that if you create an object you are responsible for it's memory management. Also, when you get an object from a method, you should retain it if you want to keep it around for longer than the span of the function. In your case, you just want to get at some of the properties of the object, so you don't need to retain it.
Here is a suggestion:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
This next bit is messy. you create a new dictionary and retain it. The original dictionary is not autoreleased, so the count isn't decremented and it always hangs around. Just assign the dictionary rather than create a new one.
NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
// Retaining it, becuase it looks like it's used elsewhere.
Now, in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *returnString;
// Don't need to retain the keys because you are only using it within the function
// and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
if ([keys count] != 0) {
returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
return [returnString autorelease];
}
return #"";
}

Resources