iPhone memory management, EXC_BAD_ACCESS NSString between views - iphone

I have some problems with memory management, I think my problem is the assignment, and object releases. I want to assign a NSString (userID) value to another NSString in other view (user_id).
MainViewController.h
#import "OnlineCheckViewController.h"
#interface MainViewController : UIViewController
{
NSString *userID;
}
#end
MainViewController.m
#import "MainViewController.h"
- (IBAction)OnlineCheck:(id)sender
{
OnlineCheckViewController *controller = [[OnlineCheckViewController alloc] initWithNibName:#"OnlineCheckViewController" bundle:nil];
controller.delegate = self;
controller.user_id = userID;
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}
- (void)viewDidUnload
{
userID = nil;
[super viewDidUnload];
}
- (void)dealloc
{
[userID release];
[super dealloc];
}
OnlineCheckViewController.h
#import <UIKit/UIKit.h>
#import "OnlineCheckResultsViewController.h"
#protocol OnlineCheckViewControllerDelegate;
#interface OnlineCheckViewController : UIViewController
<OnlineCheckResultsViewControllerDelegate>
{
NSString *user_id;
}
#property (nonatomic, assign) id <OnlineCheckViewControllerDelegate> delegate;
#property (nonatomic, retain) NSString *user_id;
#end
OnlineCheckViewController.m
#import "OnlineCheckViewController.h"
#synthesize user_id;
#synthesize delegate=_delegate;
- (void)dealloc
{
[user_id release];
[super dealloc];
}
- (void)viewDidUnload
{
[self setUser_id:nil];
[super viewDidUnload];
}
Thanks!

#interface MainViewController : UIViewController
{
NSString *userID;
}
I'm going to bet that this is your problem. Note that when you create the property "user_id", you specify "retain". Retain makes sure that the processor doesn't release the string until you tell it to.
An NSString is automatically set to auto release. If you plan to keep the value in the non property NSString (userID) then you need to call [userID retain]; after you set the value of it.
I'm guessing that you never retained userID and when you try to set user_id to it, it calls the error or more likely, it throws the error because you release it in dealloc.

Thanks to all your answers.
I have resolved this issue.
In MainViewController.h
I have added userID IBOutlet
#property (nonatomic, retain) IBOutlet NSString *userID;
In MainViewController.m
#synthesize userID;
- (IBAction)OnlineCheck:(id)sender
{
controller.user_id = self.userID;
}
- (void)viewDidUnload
{
self.userID = nil;
}
- (void)dealloc
{
[userID release];
}
In OnlineCheckViewController.h
Changed to assign
#property (nonatomic, assign) NSString *user_id;
In OnlineCheckViewController.m
- (void)dealloc
{
// removed
// [user_id release];
}
- (void)viewDidUnload
{
// removed
// [self setUser_id:nil];
}

Try this code
- (IBAction)OnlineCheck:(id)sender
{
    OnlineCheckViewController *controller = [[OnlineCheckViewController alloc] initWithNibName:#"OnlineCheckViewController" bundle:nil];
    controller.delegate = self;
    controller.user_id = userID;
[controller.user_id retain];
    controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    [self presentModalViewController:controller animated:YES];
    [controller release];
}
i think this may solve your problem...

Related

Can't write in a .plist

I have a little app that have by default youtube as homepage. You can change it in a little menu, but it doesn't work and I don't know why.
Here's the code
viewController
.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UIWebView *webView;
}
#property (retain, nonatomic) NSArray *array;
#property (retain, nonatomic) NSString *string1;
-(NSString *) dataFilePath;
- (IBAction)settings:(id)sender;
- (IBAction)home:(id)sender;
#end
.m
#import "ViewController.h"
#import "settings.h"
#implementation ViewController
#synthesize array;
#synthesize string1;
- (void)viewDidLoad
{
[super viewDidLoad];
if ([string1 isEqual:#"youtube"]) {
NSMutableArray *Array = [[NSMutableArray alloc] init];
[Array addObject:#"http://www.youtube.com"];
[Array writeToFile:[self dataFilePath] atomically:YES];
}
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
array = [[NSArray alloc] initWithContentsOfFile:filePath];
}
NSString *string = [NSString stringWithString:[array objectAtIndex:0]];
[webView loadRequest:[[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:string]]];
}
-(NSString *) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"url.plist"];
string1 = #"youtube";
}
- (IBAction)settings:(id)sender {
settings *NView = [[settings alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:NView animated:YES];
}
- (IBAction)home:(id)sender {
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
array = [[NSArray alloc] initWithContentsOfFile:filePath];
}
NSString *string = [NSString stringWithString:[array objectAtIndex:0]];
NSLog(#"%#\n",string);
[webView loadRequest:[[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:string]]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
Settings //here's the menu where I can change the homepage
.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface settings : UIViewController{
ViewController *viewCont;
IBOutlet UITextField *field;
}
-(IBAction)back:(id)sender;
- (IBAction)setHP:(id)sender;
#end
.m
#import "settings.h"
#import "ViewController.h"
#implementation settings
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib
}
- (void)viewDidUnload
{
field = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(IBAction)back:(id)sender{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)setHP:(id)sender {
NSMutableArray *Array = [[NSMutableArray alloc] init];
[Array addObject:field.text];
[Array writeToFile:[viewCont dataFilePath] atomically:YES];
[viewCont.string1 initWithString:#"other" ];
NSLog(#"%#\n", viewCont.string1);// HERE XCODE SAYS string1 = null!!! WHY?
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
So anyone knows what am I doing wrong? Please help me, I have searched around the web for 4 days and I didn't find anything!
I don't understand well your problem but i notice that you are not initializing viewCont in the setHP: action.
try to add this line:
ViewController *viewCont=[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
just before
[viewCont.string1 initWithString:#"other" ];

How to copy an array to appdelegate array and then retrieve it again elsewhere?

I want to store the array of urls in my appdelegate array i.e. logoArray from myMutableArray and then use it in other viewcontroller, but i am uable to copy as may be i am doing shallow copy, i have tried othes ways also like initwithArray:copyItems.
code:-
#class FirstViewController;
#interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
UIWindow *window;
FirstViewController *viewController;
NSMutableArray *logoArray;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) NSMutableArray *logoArray;
#end
// NO initialization of logoArra is done in .M file
#class AppDelegate_iPhone;
#interface FirstViewController : UIViewController {
NSMutableArray *array;
NSString *logoString;
AppDelegate_iPhone *appDelegate;
}
#end
#implementation FirstViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
int x=5,y=10;
UIApplication *app = [UIApplication sharedApplication];
appDelegate=app.delegate;
NSLog(#" Array ====== %d",[appDelegate.logoArray count]);
array = [[NSMutableArray alloc]initWithArray:appDelegate.logoArray];
NSLog(#"array at 0 ===== %#",[array objectAtIndex:0]);
for (int i=0; i<[array count]; i++) {
logoString = [array objectAtIndex:i];
NSLog(#"%#",logoString);
UIImage *imageFromUrl = [UIImage imageWithContentsOfFile:[NSURL fileURLWithPath:logoString]];
UIImageView *imgView = [[UIImageView alloc] initWithImage:imageFromUrl];
[imgView setFrame:CGRectMake(x, y, 196, 90)];
[self.view addSubview:imgView];
// UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapImage)];
// [imgView addGestureRecognizer:tgr];
// [tgr release];
//Do the rest of your operations here, don't forget to release the UIImageView
x = x + 200;
// [imgView release];
}
}
#class Litofinter,AppDelegate_iPhone;
#interface ParsingViewController : NSObject<NSXMLParserDelegate> {
NSString *myString;
NSMutableArray *myMutableArray;
Litofinter *obj;
NSString *currentElement;
AppDelegate_iPhone *appDelegate;
}
#property(nonatomic, retain) NSString *myString;
#property(nonatomic, retain) NSArray *myMutableArray;
#end
#import "ParsingViewController.h"
#import "Litofinter.h"
#import "AppDelegate_iPhone.h"
#implementation ParsingViewController
#synthesize myMutableArray, myString;
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
myMutableArray = [[NSMutableArray alloc]init];
}
// I have parsed here my XML and array gets stored in myMutableArray
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
UIApplication *app = [UIApplication sharedApplication];
appDelegate=app.delegate;
appDelegate.logoArray = [[NSMutableArray alloc]initWithArray:myMutableArray];
// NSLog(#"appDelegate.logoArray count %d",[appDelegate.logoArray count]);
for (Litofinter *lito in appDelegate.logoArray) {
NSLog(#"Array Elements :----- %#",lito.cLogo);
}
}
Personally I wouldn't create an array in a viewcontroller and then store it in the appdelegate. I'd be more inclined to create a model for the data ( a class that gets and stores the data and provides it to the view controllers).
this thread may help:
iPhone: Using a NSMutableArry in the AppDelegate as a Global Variable

I am unable to copy my NSMutable array to appDelegate_iPhone array(Universal app)

Actually I have parsed an XML and store URL's of images as an NSMutableArray object, but I want this array to be used in another ViewController (to give to UIImage in UIImageView to show Images at runtime), so I am trying to copy that Mutable array to myAppDelegate_iPhone's NSMutableArray. And I want to again copy that Appdelegate's array to my next or other ViewControllers NSMutableArray.
so can anyone help me out pleaseeeeee? Here is my code :-
code:-
#class FirstViewController;
#interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
UIWindow *window;
FirstViewController *viewController;
NSMutableArray *logoArray;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) NSMutableArray *logoArray;
#end
#import "AppDelegate_iPhone.h"
#import "FirstViewController.h"
#import "ParsingViewController.h"
#implementation AppDelegate_iPhone
#synthesize window,logoArray;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
viewController = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
viewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
NSURL *url = [[NSURL alloc] initWithString:#"http://litofinter.es.milfoil.arvixe.com/displayxml1.aspx"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
//Initialize the delegate.
ParsingViewController *parser = [[ParsingViewController alloc] init];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
logoArray = [[NSMutableArray alloc]init];
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
return YES;
}
// dealloc done
#end
#class Litofinter,AppDelegate_iPhone;
#interface ParsingViewController : NSObject<NSXMLParserDelegate> {
NSString *myString;
NSMutableArray *myMutableArray;
Litofinter *obj;
NSString *currentElement;
AppDelegate_iPhone *appDelegate;
}
#import "ParsingViewController.h"
#import "Litofinter.h"
#import "AppDelegate_iPhone.h"
#implementation ParsingViewController
#synthesize myMutableArray, myString;
-(id)init{
if(self == [super init]){
myMutableArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
//myMutableArray = [[NSMutableArray alloc]init];
}
// Parsing done here
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
//UIApplication *app = [UIApplication sharedApplication];
//appDelegate=app.delegate;
appDelegate.logoArray = [[NSMutableArray alloc]initWithArray:myMutableArray];
NSLog(#"appDelegate.logoArray count %d",[appDelegate.logoArray count]);
for (Litofinter *lito in appDelegate.logoArray) {
NSLog(#"Array Elements :----- %#",lito.cLogo);
}
}
#end
#import <UIKit/UIKit.h>
#class AppDelegate_iPhone,Litofinter,ParsingViewController;
#interface FirstViewController : UIViewController {
NSMutableArray *array;
//Litofinter *lito;
NSString *logoString;
AppDelegate_iPhone *appDelegate;
ParsingViewController *obj;
}
#end
#import "FirstViewController.h"
#import "AppDelegate_iPhone.h"
#import "Litofinter.h"
#import "ParsingViewController.h"
#implementation FirstViewController
-(id)init{
if(self == [super init]){
obj = [[ParsingViewController alloc] init];
array = [[NSArray alloc] initWithArray: obj.myMutableArray];
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
int x=5,y=10;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
// UIApplication *app = [UIApplication sharedApplication];
// appDelegate=app.delegate;
NSLog(#"delegate Array ====== %d",[appDelegate.logoArray count]);
NSLog(#"New Array ====== %d",[obj.myMutableArray count]);
/*
array = [[NSMutableArray alloc]initWithArray:appDelegate.logoArray];
NSLog(#"array at 0 ===== %#",[array objectAtIndex:0]);
for (Litofinter *lito1 in obj.myMutableArray) {
NSLog(#"Array Elements in Lito1 are :------------- %#",lito1.cLogo);
}
for (Litofinter *lito2 in array) {
NSLog(#"Array Elements in Lito1 are :------------- %#",lito2.cLogo);
}
*/
for (Litofinter *lito in obj.myMutableArray) {
//for (int i=0; i<[appDelegate.logoArray count]; i++) {
// lito.cLogo = [array objectAtIndex:i];
NSLog(#"%#",lito.cLogo);
UIImage *imageFromUrl = [UIImage imageWithContentsOfFile:[NSURL fileURLWithPath:lito.cLogo]];
UIImageView *imgView = [[UIImageView alloc] initWithImage:imageFromUrl];
[imgView setFrame:CGRectMake(x, y, 196, 90)];
[self.view addSubview:imgView];
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapImage)];
[imgView addGestureRecognizer:tgr];
// [tgr release];
//Do the rest of your operations here, don't forget to release the UIImageView
x = x + 200;
}
}
-(void)onTapImage
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Message from mAc" message:#"Trail" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok",nil];
[alert show];
}
- (void)dealloc {
[super dealloc];
}
#end
You can use this.
UIImage *imageFromUrl = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:lito.cLogo]]];

UITableView custom class huge leak

I am using a custom class to display some info on a table view.
The problem is that as long as I scroll the tableview memory is leaking...
I guess I have something wrong at my class.
Please have a look:
#interface Person : NSObject {
NSString *name;
NSString *surname;
NSString *address;
NSString *email;
}
#property (nonatomic, copy) NSString *name, *surname, *address, *email;
#implementation Person
#synthesize name, surname, address, email;
-(id)init {
[super init];
name = [[NSString alloc] init];
surname = [[NSString alloc] init];
address = [[NSString alloc] init];
email = [[NSString alloc] init];
return self;
}
- (void)dealloc
{
[name release];
[surname release];
[address release];
[email release];
[super dealloc];
}
#import "Person.h"
#interface Group : NSObject {
NSString *groupTitle;
NSMutableArray *persons;
}
#property (readwrite, copy) NSString *groupTitle;
- (void)addPerson:(Person *)person;
- (void)removeAll;
- (NSArray *)getPersons;
- (int)PersonsCount;
#end
#implementation Group
#synthesize groupTitle;
-(id)init {
[super init];
persons = [[NSMutableArray alloc] init];
return self;
}
-(void)addPerson:(Person *)person {
[persons addObject:person];
}
-(void)removeAll {
[persons removeAllObjects];
}
-(NSArray *) getPersons {
return [persons copy];
[persons release];
}
-(int)personsCount {
return [persons count];
}
-(void)dealloc {
[groupTitle release], groupTitle = nil;
[persons release], persons = nil;
[super dealloc];
}
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
…….
Group *groupForRow = [[Group alloc] init];
Person *personForRow = [[Person alloc] init];
personForRow = [[groupForRow getPersons] objectAtIndex:indexPath.row];
_personName = personForRow.name;
_personSurname = personForRow.surname;
_personAddress = personForRow.address;
_personEmail = personForRow.email;
[groupForRow release], groupForRow = nil;
[personForRow release], personForRow = nil;
…..
return cell
Few corrections (read the comments):
#interface Person : NSObject {
NSString *name;
NSString *surname;
NSString *address;
NSString *email;
}
// copy is OK for strings...
#property (nonatomic, copy) NSString *name, *surname, *address, *email;
#end
#implementation Person
#synthesize name, surname, address, email;
- (id)init {
if (self = [super init]) {
// There is no need to allocate the strings
// In addition, once you write 'name = [[NSStrin alloc] init];' you don't use the property.
// If you do want to use the property setter then you should write 'self.name = #"some string";'
}
return self;
}
- (void)dealloc {
[name release];
[surname release];
[address release];
[email release];
[super dealloc];
}
#end
#import "Person.h"
#interface Group : NSObject {
NSString *groupTitle;
NSMutableArray *persons;
}
// Any special reason for "readwrite" instead of "nonatomic"?
#property (readwrite, copy) NSString *groupTitle;
// This property is more important than the string:
#property (nonatomic, retain) NSMutableArray *persons;
- (void)addPerson:(Person *)person;
- (void)removeAll;
- (NSArray *)getPersons;
- (int)PersonsCount;
#end
#implementation Group
#synthesize groupTitle, persons;
- (id)init {
if (self = [super init]) {
// Use the autoreleased array instance ([NSMutableArray array]) and set it to the property setter that will retain the object:
self.persons = [NSMutableArray array];
}
return self;
}
- (void)addPerson:(Person *)person {
// I prefer using properties (the "self." in the beginning) instead of the members directly...
[self.persons addObject:person];
}
- (void)removeAll {
[self.persons removeAllObjects];
}
// I think that this getter is unnecessary - use the property instead...
- (NSArray *) getPersons {
// There is no need to copy
return [persons copy];
// Don't you have a warning for this line? It is never executed
[persons release];
}
- (int)personsCount {
return [self.persons count];
}
- (void)dealloc {
[groupTitle release], groupTitle = nil;// The "groupTitle = nil" is unnecessary.
[persons release], persons = nil;// The "persons = nil" is unnecessary.
[super dealloc];
}
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
…….
Group *groupForRow = [[Group alloc] init];// Do you REALLY have to allocate this object each "cellForRowAtIndexPath"??
Person *personForRow = [[Person alloc] init];// Get rid of the "= [[Person alloc] init]" - this is a leak (because of the next line)
personForRow = [[groupForRow getPersons] objectAtIndex:indexPath.row];// If you will use the property persons instead of the "getPersons" (that copies the array) then you will get rid of another leak
// What are these?
_personName = personForRow.name;
_personSurname = personForRow.surname;
_personAddress = personForRow.address;
_personEmail = personForRow.email;
// The " = nil" is unnecessary here...
[groupForRow release], groupForRow = nil;// If you won't allocate the group then you won't need this line...
[personForRow release], personForRow = nil;// NSZombie - you release object that you don't owe (do you have crashes, that you don't know why they are happen?)
…..
return cell;
}
There is a lot wrong here, please delve a little into objective-C to get a grasp of the use of #property and #synthesize to get correctly functioning getter/setter methods.
As for your memory leak when scrolling, it is caused by the allocs in cellForRowAtIndexPath which are not balanced by either a release or an autorelease.
This:
Group *groupForRow = [[[Group alloc] init] autorelease];
Person *personForRow = [[[Person alloc] init] autorelease];
should fix most of your leaks. Browse around on SO for more info.

[UIView didCreateWorkout:Type:Distance:Time:Message:]: unrecognized selector sent to instance

I'm getting the above error and have been looking at it all day, I'm getting no where fast. Anyone any ideas ? I'm new to IPhone Development. Code Below:
WorkoutAppDelegate.h...:
#import "WorkoutViewController.h"
#interface WorkoutAppDelegate : NSObject <UIApplicationDelegate> {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
UIWindow *window;
WorkoutViewController *viewController;
}
- (IBAction)saveAction:sender;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (nonatomic, readonly) NSString *applicationDocumentsDirectory;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet WorkoutViewController *viewController;
#end
WorkoutAppDelegate.m....:
#import "WorkoutAppDelegate.h"
#implementation WorkoutAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
viewController.managedObjectContext = [self managedObjectContext];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
/**
applicationWillTerminate: saves changes in the application's managed object context before the application terminates.
*/
- (void)applicationWillTerminate:(UIApplication *)application {
NSError *error;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort(); // Fail
}
}
}
/**
Performs the save action for the application, which is to send the save:
message to the application's managed object context.
*/
- (IBAction)saveAction:(id)sender {
NSError *error;
if (![[self managedObjectContext] save:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort(); // Fail
}
}
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"WorkoutCoreData.sqlite"]];
NSError *error;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
// Handle error
}
return persistentStoreCoordinator;
}
/**
Returns the path to the application's documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
- (void)dealloc {
[managedObjectContext release];
[managedObjectModel release];
[persistentStoreCoordinator release];
[viewController release];
[window release];
[super dealloc];
}
#end
WorkoutViewController.h...:
#import <UIKit/UIKit.h>
#import "Workout.h"
#protocol CreateWorkoutDelegate <NSObject>
-(void)didCancelWorkout;
-(void)didCreateWorkout:(NSString *)thisRoute
Type:(NSString *)thisType
Distance:(NSString *)thisDistance
Time:(NSString *)thisTime
Message:(NSString *)thisMessage;
#end
#interface WorkoutViewController : UIViewController {
// IBOutlet UIlabel *Speed;
// IBOutlet UIlabel *Calories;
IBOutlet UILabel *DBContents;
IBOutlet UITextField *route;
IBOutlet UITextField *type;
IBOutlet UITextField *distance;
IBOutlet UITextField *time;
IBOutlet UITextField *message;
IBOutlet UIButton *saveWorkout;
IBOutlet UIButton *cancelWorkout;
NSMutableArray *workoutArray;
id workoutDelegate;
Workout *currentWorkout;
NSManagedObjectContext *managedObjectContext;
}
//#property (retain,nonatomic) UILabel *Speed;
//#property (retain,nonatomic) UILabel *Calories;
#property (retain,nonatomic) UILabel *DBContents;
#property (retain,nonatomic) UITextField *route;
#property (retain,nonatomic) UITextField *type;
#property (retain,nonatomic) UITextField *distance;
#property (retain,nonatomic) UITextField *time;
#property (retain,nonatomic) UITextField *message;
#property (retain,nonatomic) NSMutableArray *workoutArray;
//#property (retain,nonatomic) UIButton *saveWorkout;
//#property (retain,nonatomic) UIButton *cancelWorkout;
#property (nonatomic, assign) id<CreateWorkoutDelegate> workoutDelegate;
#property (nonatomic, assign) NSManagedObjectContext *managedObjectContext;
-(IBAction)hideKeyboard;
-(IBAction)saveWorkout;
-(IBAction)cancelWorkout;
#end
WorkoutViewController.m...:
#import "WorkoutViewController.h"
#import "Workout.h"
#implementation WorkoutViewController
#synthesize workoutDelegate;
//#synthesize Speed;
//#synthesize Calories;
#synthesize route;
#synthesize type;
#synthesize distance;
#synthesize time;
#synthesize message;
#synthesize DBContents;
#synthesize workoutArray;
#synthesize managedObjectContext;
//#synthesize saveWorkout;
//#synthesize cancelWorkout;
-(IBAction)hideKeyboard {
}
-(IBAction)saveWorkout {
[workoutDelegate didCreateWorkout: route.text
Type: type.text
Distance: distance.text
Time: time.text
Message: message.text];
}
-(IBAction)cancelWorkout {
[self.workoutDelegate didCancelWorkout];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
-(void)viewDidLoad {
//Set images for Save & Cancel buttons.
UIImage *normalImage = [[UIImage imageNamed:#"whiteButton.png"]
stretchableImageWithLeftCapWidth:12.0
topCapHeight:0.0];
[saveWorkout setBackgroundImage:normalImage forState:UIControlStateNormal];
[cancelWorkout setBackgroundImage:normalImage forState:UIControlStateNormal];
UIImage *pressedImage = [[UIImage imageNamed:#"blueButton.png"]
stretchableImageWithLeftCapWidth:12.0
topCapHeight:0.0];
[saveWorkout setBackgroundImage:pressedImage forState:UIControlStateHighlighted];
[cancelWorkout setBackgroundImage:pressedImage forState:UIControlStateHighlighted];
//Fetch details from the database.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Workout" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error;
self.workoutArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
[request release];
//self.workoutArray = [[NSMutableArray alloc] init];
//self.DBContents.text = [self.workoutArray objectAtIndex:0];
[super viewDidLoad];
}
-(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
-(void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void) didCreateWorkout:(NSString *)thisRoute
Type:(NSString *)thisType
Distance:(NSString *)thisDistance
Time:(NSString *)thisTime
Message:(NSString *)thisMessage {
// Add the new workout.
Workout *newWorkout = [NSEntityDescription
insertNewObjectForEntityForName:#"Workout"
inManagedObjectContext:self.managedObjectContext];
newWorkout.route = thisRoute;
newWorkout.type = thisType;
newWorkout.distance = thisDistance;
newWorkout.time = thisTime;
newWorkout.message = thisMessage;
[self.workoutArray addObject:newWorkout];
//[self dismissModalViewControllerAnimated:YES];
}
-(void)didCancelWorkout {
[self dismissModalViewControllerAnimated:YES];
}
-(void)dealloc {
// [Speed release];
// [Calories release];
[route release];
[type release];
[distance release];
[time release];
[message release];
// [saveWorkout release];
// [cancelWorkout release];
[workoutArray release];
[managedObjectContext release];
[super dealloc];
}
#end
I'm trying to save details that I key on the screen (WorkoutViewController.xib) and I click the save button and get the above error.
Thanks
Stephen
The error is saying that UiView does not know (does not implement) the method -(void)didCreateWorkout .......and really if you look where you have implemented this method, you will see it is in WorkoutViewController (WorkoutViewController.m), which is not an UIView (I presume you have only one implementation of didCreateWorkout in your project). You should double check how you set the workoutDelegate property. From the code you show us it should be an instance of WorkoutViewController.
Btw., because you are having the implementaion of the -(IBAction)saveWorkout also in WorkoutViewController the quick fix for this particular problem would be to change the code of your actions to:
-(IBAction)saveWorkout {
[self didCreateWorkout: route.text
Type: type.text
Distance: distance.text
Time: time.text
Message: message.text];
}
-(IBAction)cancelWorkout {
[self didCancelWorkout];
}
However, this quick fix will not fix the problem with the design you probably intended. You should think through who should implement CreateWorkoutDelegate and then properly set the workoutDelegate property.
On a different topic I noticed two things in your code, which you might to consider to change:
use self.property=nil instead of [ivar release] in your dealloc methods
NSString properties should have attribute copy (in order to protect yourself from NSMutableString instances)
Good luck! ;)