CoreData Relationships Clarification: How to effectively set an inverse relationship - iphone

I have a CoreData relationship set up as follows:
(sorry, I'm new to stackoverflow so I have to draw it with ascii).
Story (object)
Attributes:
creationDate,
order,
Relationships:
sentences (one to many with Sentence)
Sentence (object)
Attributes: image, order, text
Relationships: belongsTo (one to one with Story)
Notes:
the sentences relationship is an NSSet
the belongsTo relationship is a Story
Simply put, a Story has many Sentences, but a Sentence can only belong to one Story.
I'm having trouble effectively setting the belongsTo property - and I'm aware I'm missing the paradigm a bit. I'm afraid that when I set belongsTo I'll be creating another Story object with it's own NSSet, creating a recursive loop of doom. In any case, my problem appears to be effectively setting the belongsTo property / relationship of each sentence.
The code to insert my data into the CoreData repository looks like this:
-(void)addStoryItemAction:(Sentence*)sentence{
[self dismissModalViewControllerAnimated:YES];
Story *story = [[Story alloc] initWithSentence: sentence];
sentence.belongsTo = story;
//crash and burn here.
Story *storySetter = (Story*) [NSEntityDescription insertNewObjectForEntityForName:#"Story" inManagedObjectContext:managedObjectContext];
[storySetter setSentences: story.sentences];
[storySetter setCreationDate: story.creationDate];
[storySetter setOrder: story.order];
NSError *error;
BOOL isSaved = [managedObjectContext save:&error];
NSLog(#"isSaved? %#", (isSaved ? #"YES" :#"NO ") );
if (!isSaved) {
NSLog(#"%#:%s Error saving context: %#", [self class], _cmd, [error localizedDescription]);
return;
}
}
I get the following error when it runs:
-[Sentence setBelongsTo:]: unrecognized selector sent to instance 0x5b5aa00
I'm aware that I'm missing the point somewhere along the way - could someone clear up where I'm going wrong so that I can implement the insert of this CoreData set effectively?
FYI:
Sentence.h:
#import <Foundation/Foundation.h>
#import "Story.h"
#class Story;
#interface Sentence : NSManagedObject {
}
#property (assign) NSString *text;
#property (assign) NSString *image;
#property (assign) NSInteger *order;
#property (nonatomic, retain) Story *belongsTo;
- (id)initWithContent:(NSString*)sentenceText image:(NSString*)sentenceImage order: (NSInteger*)sentencePosition;
#end
Sentence.m:
#import "Sentence.h"
#implementation Sentence
#synthesize text;
#synthesize image;
#synthesize order;
#dynamic belongsTo;
- (id)initWithContent:(NSString*)sentenceText image:(NSString*)sentenceImage order: (NSInteger*)sentencePosition{
[self setText: sentenceText];
[self setImage: sentenceImage];
[self setOrder: sentencePosition];
return self;
}
#end
story.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Sentence.h"
#class Sentence;
#interface Story : NSManagedObject {
}
#property (assign) NSSet *sentences;
#property (assign) NSDate *creationDate;
#property (assign) NSInteger *order;
- (id)initWithSentence:(Sentence*)sentence;
#end
story.m
#import "Story.h"
#implementation Story
#synthesize sentences;
#synthesize creationDate;
#synthesize order;
- (id)initWithSentence: (Sentence*) sentence{
NSLog(#"initWithSencence: sentence: %#", sentence);
sentences = [[NSSet alloc] initWithObjects:sentence, nil];
//[sentences setByAddingObject:sentence];
[self setSentences:sentences];
NSLog(#"sentences (in story object): %#", sentences);
creationDate = [NSDate date];
[self setCreationDate:creationDate];
//later expansion, position should be max(position of all stories) ++
[self setOrder:0];
return self;
}
#end

I'm a bit confused about why you're calling alloc / init on Story directly. I suspect that if you're doing the same thing elsewhere in your code (which you should publish for us to gawk at) then the "sentence" object passed in to addStoryItemAction: is probably NOT a properly initialized NSManagedObject.
The error you are encountering means that the underlying class has not properly implemented the accessors for that property "belongsTo", so it would seem something went afoul in your initialization code. You should be using:
NSEntityDescription *entityFromModel = [[aModel entitiesByName] valueForKey:#"Sentence"];
Sentence *sentence = (Sentence *)[[NSManagedObject alloc] initWithEntity:entityFromModel insertIntoManagedObjectContext:context];

Related

add data to my data member

Hello I'm new to iPhone development.
I try to add move data from NSDictionary to data member of calls that i created.
When i "setWeightMeasure" nothing happened.
any suggestions?
the code that don't work:
NSDictionary *responseBodyProfile = [responseBody objectFromJSONString];
NSLog(#"%#",responseBodyProfile);
// the output is :
"{ "profile": {"goal_weight_kg": "77.0000", "height_cm": "179.00",
"height_measure": "Cm", "last_weight_date_int": "15452",
"last_weight_kg": "99.0000", "weight_measure": "Kg" }}""
[responseBody release];
if (responseBodyProfile != nil ){
NSDictionary *profile =[responseBodyProfile valueForKey:#"profile"];
NSLog(#"%#\n",[profile objectForKey:#"weight_measure"]);// Output : "kg"
[self.myUser setWeightMeasure:[profile objectForKey:#"weight_measure"]];
NSLog(#"%#", [self.myUser WeightMeasure]); // Output : "(null)"
}
the H file properyty:
#property (nonatomic, retain) UserData* myUser;
UserData.h:
#import <Foundation/Foundation.h>
#interface UserData : NSObject{
NSString* Weight;
NSString* Height;
NSString* GolWeight;
NSString* WeightMeasure;
}
#property (nonatomic, retain) NSString* Weight;
#property (nonatomic, retain) NSString* Height;
#property (nonatomic, retain) NSString* GolWeight;
#property (nonatomic, retain) NSString* WeightMeasure;
#end
UserData.m
#import "UserData.h"
#implementation UserData
#synthesize Weight, Height, GolWeight, WeightMeasure;
-(id)init{
self.Weight = #"0";
self.Height = #"0";
self.GolWeight = #"0";
self.WeightMeasure = #"0";
return self;
}
-(void)dealloc{
[Weight release];
[Height release];
[GolWeight release];
[WeightMeasure release];
[super dealloc];
}
#end
Use valueForKey instead of objectForKey in this line:
[self.myUser setWeightMeasure:[profile objectForKey:#"weight_measure"]];
like this:
[self.myUser setWeightMeasure:[profile valueForKey:#"weight_measure"]];
You might also want to use, since the values could be read as NSNumbers
[self.myUser setWeightMeasure:[[profile valueForKey:#"weight_measure"] stringValue]];
And why do you use strings instead of floats? Wouldn't that make your life easier when you'd need to perform some comparisons?
Also check if you have allocated memory for "myUser", that might be the case as well.
As Eugene mentioned, you should use valueForKey instead of objectForKey
The other thing is you might wanna use property and dot notation whenever you reference your object members, as Apple recommend. It is generally good for you to manage memory.
The previous answer about not initialize your string members in your -init() was totally wrong, if that cause some confusion, I do apologize for it.

Converting HTML to Plain Text for MapKit Annotations

I have a problem when I use an annotation to see information with MapKit.
I ran into a similar issue. You're not crazy. I believe it's a bug in the MapKit code. The Annotation object doesn't create it's own copy of the strings you pass to it. When your string goes out of scope, the map makes a bad reference. Try re-allocating the strings before you pass them. Like so:
NSString *tempT = [[NSString alloc] initWithString:itemT];
NSString *tempA = [[NSString alloc] initWithString:itemA];
addAnnotation = [[MapAnnotation alloc] initWithCoordinate:essai :tempT :tempA];
And then don't release them until you're finished displaying the map.
I ran into the exact same problem, as Jonesy mentioned, but there is a fix. I'm not sure what kind of class you have for your annotations, but I use this:
Annotation.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import <UIKit/UIKit.h>
#interface LocationAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString* title;
NSString* subtitle;
}
#property (nonatomic, assign) MKPinAnnotationColor pinColor;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString* title;
#property (nonatomic, copy) NSString* subtitle;
- (id)initWithCoordinate:(CLLocationCoordinate2D) c
title:(NSString*) t
subtitle:(NSString*) st;
- (void)moveAnnotation:(CLLocationCoordinate2D) newCoordinate;
- (NSString*)subtitle;
- (NSString*)title;
#end
And Annotation.m:
#import "LocationAnnotation.h"
#implementation LocationAnnotation
#synthesize coordinate, pinColor, title, subtitle;
- (NSString *)subtitle {
return subtitle;
}
- (NSString *)title {
return title;
}
-(id)initWithCoordinate:(CLLocationCoordinate2D) c
title:(NSString*)t
subtitle:(NSString*)st
{
coordinate = c;
self.title = t;
self.subtitle = st;
return self;
}
- (void)moveAnnotation:(CLLocationCoordinate2D)newCoordinate
{
coordinate = newCoordinate;
}
- (void)dealloc
{
[title release];
[subtitle release];
[super dealloc];
}
#end
To implement it:
Annotation* ann = [[[Annotation alloc] initWithCoordinate:startLocation title:someStringAutoreleasedOrNot subtitle:someOtherStringAutoreleasedOrNot] autorelease];
[yourMapView addAnnotation:ann];
Really, the key here is that in the annotation class, the title and subtitle properties are declared as type copy. This makes a new copy of the string you assign it, so it can be released without causing the crash that you are having.
I dont see stringByStandardizingWhitespace method call in the above code you pasted... It would be helpful if you can post the code which has the error. Use debugger to know where the error is occuring...
Also one error which is not related to the syntax/error you specified but would effect the logic later:
you are assigning the gps_long tag tpo lat and vice versa ...

accessing class file crashes when scrolling starts (UIScrollview)

hopefully someone will be able to help me. I have a UIScrollView on my page. The .h file has set the UIscrollviewdelegate.
I have a class file called "Picture.h / Picture.m".
- (id)initWithName:(NSString *)aName filename:(NSString *)aFilename {
self.name = aName;
self.filename = aFilename;
return self;
}
In this class file, I simply set a couple of strings. I load an array with object of this picture class, for example
Picture *image2 = [[Picture alloc] initWithName:#"Apple" filename:#"apple.png"];
[pictureArray addObject: image2];
[image2 release];
Within my viewController, I call this class and assign is as such
Picture *thisPicture = (Picture *)[appDelegate.pictureArray objectAtIndex:0];
view2image.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#", thisPicture.filename]];
The above works fine. The image is set to what ever I put, example, "apple.png". However, when I tried to set this in the - (void) scrollViewDidScroll:(UIScrollView *) method within my viewController, I get a bad exec error and the app crashes.
Yet, if I had an array of filenames (so not storing my class object in the array) and access objectAtIndex:0 in the scrollViewDidScroll - it works fine.
So, this code is OK
nextImageView.image = [UIImage imageNamed: [NSString stringWithFormat:#"%#", [appDelegate.pictureCardsArray objectAtIndex:0]]];
but this crashes
Picture *image3 = (Picture *)[appDelegate.pictureArray objectAtIndex:0];
nextImageView.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#", image3.filename]];
Interestingly though, if I don't try to access the element of image3 (eg image3.filename) it doesn't crash. This is useless though! Also, if I disable the delegate = self for the uiscrollview, then this code works, but none of the scrolling actions are fired. I came across this post (http://stackoverflow.com/questions/1734720/uiscrollview-on-a-uiviewcontroller) when searching for the solution, but cannot see where I might be releasing the viewController early. To be safe, nothing is getting released (yet!)
Hopefully someone might be able to shed some light on it!!
[edit]Just adding in the full class files][/edit]
Picture.h
#import <UIKit/UIKit.h>
#interface Picture : NSObject {
NSString *name;
NSString *filename;
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *filename;
- (id)initWithName:(NSString *)aName filename:(NSString *)aFilename;
#end
Picture.m
#import "Picture.h"
#implementation Picture
#synthesize name, filename;
- (id)initWithName:(NSString *)aName filename:(NSString *)aFilename {
self.name = aName;
self.filename = aFilename;
return self;
}
#end
Just for completeness... the solution was as Antwen Van Houdt said - I changed copy to retain and it worked fine.
#import <UIKit/UIKit.h>
#interface Picture : NSObject {
NSString *name;
NSString *filename;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *filename;
- (id)initWithName:(NSString *)aName filename:(NSString *)aFilename;
#end

passing NSString from one class to the other

I have a NSString that is taken from a UITextField in a ViewController. Every of my other ViewController will use this NSString as well. How can I pass this NSString to others ViewControllers?
You want to have a property in each of your controllers
#interface MyViewController : UIViewController{
NSString *title;
}
#property (retain) NSString *title;
#end;
#implementation MyViewController
#synthesize title;
#end;
Use it like:
MyViewController *myVC = [[MyViewController alloc] initWithFrame:...];
myVC.title = #"hello world";
You should be familiar with Memory Management
Create a class for sharing your common objects. Retrieve it using a static method, then read and write to its properties.
#interface Store : NSObject {
NSString* myString;
}
#property (nonatomic, retain) NSString* myString;
+ (Store *) sharedStore;
#end
and
#implementation Store
#synthesize myString;
static Store *sharedStore = nil;
// Store* myStore = [Store sharedStore];
+ (Store *) sharedStore {
#synchronized(self){
if (sharedStore == nil){
sharedStore = [[self alloc] init];
}
}
return sharedStore;
}
// your init method if you need one
#end
in other words, write:
Store* myStore = [Store sharedStore];
myStore.myString = #"myValue";
and read (in another view controller):
Store* myStore = [Store sharedStore];
myTextField.text = myStore.myString;
If the string remains the same, and never changes, you could make a file named defines.h (without the .m file) and have this line:
#define kMyString #"Some text"
Then wherever you need the string, just import the defines file and use the constant.
#import "defines.h"
Much simpler than making custom classes.
EDIT:
Didn't see you needed to grab from the text field.
In that case, you could have it stored as property of your app delegate class and get it from there. The delegate can be accessed from anywhere in your app.

iPhone SDK: NSMutableArray count causes EXC_BAD_ACCESS

This is really twisting my mind… I'm trying to access an NSMutableArray in an IBAction which I defined in viewDidLoad. Unfortunately I keep getting a EXC_BAD_ACCESS.
I'm new to all this so I'd really appreciate some insight in what I'm doing wrong.
Below find the corresponding code excerpts.
CounterViewController.h:
#interface CounterViewController : UIViewController{
NSMutableArray *countHistoryArray;
}
#property(nonatomic, retain) NSMutableArray *countHistoryArray;
CounterViewController.m:
#implementation CounterViewController
#synthesize countHistoryArray;
- (void)viewDidLoad {
[super viewDidLoad];
//Fill array with some dummy data
self.countHistoryArray = [[NSMutableArray alloc] init];
NSDate *now = [[[NSDate alloc] init] autorelease];
CurrentCount *historicCount = [[[CurrentCount alloc]
initWithCount:[NSNumber numberWithInteger:22]
description:#"Testcount"
dateAndTime:now] autorelease];
[self.countHistoryArray addObject: historicCount];
//Do some logging - everything is working fine here!
NSLog(#"%#", [self.countHistoryArray description]);
}
//Later on we click on a button and want to use the array
- (IBAction)doSomeStuff {
//Let's look at the array again - and now it crashes with EXC_BAD_ACCESS
NSLog(#"%#", [self.countHistoryArray description]);
}
Thanks a lot!
Manuel
EDIT Additional code as asked for by #jamapag
CurrentCount.h
#import <Foundation/Foundation.h>
#interface CurrentCount : NSObject {
NSNumber *counterLevel;
NSString *description;
NSDate *dateAndTime;
}
- (id)initWithCount:(NSNumber *)newCounterLevel description:(NSString *)newDescription dateAndTime:(NSDate *)newDateAndTime;
#property(nonatomic, copy) NSNumber *counterLevel;
#property(nonatomic, copy) NSString *description;
#property(nonatomic, copy) NSDate *dateAndTime;
#end
CurrentCount.m
#import "CurrentCount.h"
#implementation CurrentCount
#synthesize counterLevel;
#synthesize description;
#synthesize dateAndTime;
- (id)initWithCount:(NSNumber *)newCounterLevel description:(NSString *)newDescription dateAndTime:(NSDate *)newDateAndTime{
self = [super init];
if(nil != self){
self.counterLevel = newCounterLevel;
self.description = newDescription;
self.dateAndTime = newDateAndTime;
}
return self;
}
-(void) dealloc{
self.counterLevel = nil;
self.description = nil;
self.dateAndTime = nil;
[super dealloc];
}
#end
Are you sure that your code actually looks like this?
- (IBAction)doSomeStuff {
//Let's look at the array again - and now it crashes with EXC_BAD_ACCESS
NSLog(#"%#", [self.countHistoryArray description]);
}
Your question title says "NSMutableArray count causes EXC_BAD_ACCESS" - if that line of code actually says NSLog(#"%#", [self.countHistoryArray count]);, you'll almost certainly get a crash, since NSLog will attempt to treat a primitive type (the type returned by -[NSArray count]) as an object. In order to use -[NSArray count] in NSLog, use %u instead of %#:
- (IBAction)doSomeStuff {
// This time it should work!
NSLog(#"Array Count = %u", [self.countHistoryArray count]);
}
Remove autorelease from:
currentCount *historicCount = [[[CurrentCount alloc]
initWithCount:[NSNumber numberWithInteger:22]
description:#"Testcount"
dateAndTime:now] autorelease];
It looks like you are accidentally releasing countHistoryArray somewhere. Try removing all calls to it except for those two you showed. Additionally you can try enabling zombies to debug the problem.
Oh and by the way you probably don't really want a public NSMutableArray property and if you do you probably want it to be copy, not retain. Otherwise incapsulation kinda goes down the drain.
I know this question has already been solved and accepted but its for others who are or will face this issue.
I was facing the same issue, I tried all solutions but no solution worked for me. The project I am working on is NON-ARC.
I tried and made a simple change in property
Previously my property for NSMUTABLEARRAY was
#property (nonatomic, assign) NSMutableArray * dataArray;
I changed it to:
#property (nonatomic, retain) NSMutableArray * dataArray;
Changed it from ASSIGN to RETAIN
And it solved my problem