Core Plot scoping issue in numberForPlot function - iphone

My problem is this: I can't seem to access the variable todaysDate from the numberForPlot or numberOfRecordsForPlot functions (see below for numberForPlot), but I can from anywhere else in the file.
The NSLog in the viewDidLoad works perfectly, the date is set correctly. If I access the variable from my own class functions, then that's fine too and it works. However, when I try to access it from numberForPlot I get an error:
Program received signal: “EXC_BAD_ACCESS”.
In my header file, I have the following - note my class implements CPPlotDataSource.
#import <UIKit/UIKit.h>
#import "CorePlot-CocoaTouch.h"
#interface ResultsGraphViewController : UIViewController <CPPlotDataSource> {
NSManagedObjectContext *managedObjectContext;
CPXYGraph *graph;
NSMutableArray *eventsArray;
NSDate *todaysDate;
}
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *eventsArray;
#property (nonatomic, retain) NSDate *todaysDate;
- (void)getEvents;
- (void)configureGraph;
#end
In the implementation file, I have (relevant highlights only):
#synthesize managedObjectContext;
#synthesize eventsArray;
#synthesize todaysDate;
and
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:#"Results"];
todaysDate = [NSDate date];
NSLog(#"Set today's date to %#", todaysDate);
[self getEvents];
[self configureGraph];
}
and
-(NSNumber *)numberForPlot:(CPPlot *)plot
field:(NSUInteger)fieldEnum
recordIndex:(NSUInteger)index
{
NSLog(#"%d events in the array.", [eventsArray count]);
NSLog(#"today's date is %#.", todaysDate);
...
}
(in the last two lines, above, the number of events in the array is output successfully, but the last line causes the error).
Any ideas as to why this is a problem, and how I can get around it? I imagine it's something to do with being the CPPlotDataSource - how does this affect scoping?
Or do I just have an error in my code? All help much appreciated!

The problem is that [NSDate date] returns an autoreleased object, which you don't hold on to. It will hang around until the end of the current cycle of the run loop (why it doesn't crash immediately in your first NSLog() statement), then it will be released. When you try to access it in -numberForPlot:, it has been released and your application crashes.
To fix this, change the line in -viewDidLoad to read
self.todaysDate = [NSDate date];
You defined todaysDate to be a property with the retain attribute, so this will retain your date. Just remember to add a [todaysDate release] in your -dealloc method to prevent a leak.

Related

I'm trying to pass a string from my first ViewController to my second ViewController but it returns NULL

In my first view controller I have 3 input fields each of them take the user input into and saves it into a string such as: address, username and password as NSUserDefaults. This part works fine.
In my second view controller I'm trying to take the 3 strings from first controller (address, username and password) create a html link based on the 3 strings. I've tried many ways to access the 3 strings with no luck, the result I get is NULL.
Here is my code:
//.h file - first view controller with the 3 input fields CamSetup.h
#import <UIKit/UIKit.h>
#interface CamSetup : UIViewController <UITextFieldDelegate>
{
NSString * address;
NSString * username;
NSString * password;
IBOutlet UITextField * addressField;
IBOutlet UITextField * usernameField;
IBOutlet UITextField * passwordField;
}
-(IBAction) saveAddress: (id) sender;
-(IBAction) saveUsername: (id) sender;
-(IBAction) savePassword: (id) sender;
#property(nonatomic, retain) UITextField *addressField;
#property(nonatomic, retain) UITextField *usernameField;
#property(nonatomic, retain) UITextField *passwordField;
#property(nonatomic, retain) NSString *address;
#property(nonatomic, retain) NSString *username;
#property(nonatomic, retain) NSString *password;
#end
//.m file - first view controller CamSetup.m
#import "CamSetup.h"
#interface CamSetup ()
#end
#implementation CamSetup
#synthesize addressField, usernameField, passwordField, address, username, password;
-(IBAction) saveAddress: (id) sender
{
address = [[NSString alloc] initWithFormat:addressField.text];
[addressField setText:address];
NSUserDefaults *stringDefaultAddress = [NSUserDefaults standardUserDefaults];
[stringDefaultAddress setObject:address forKey:#"stringKey1"];
NSLog(#"String [%#]", address);
}
-(IBAction) saveUsername: (id) sender
{
username = [[NSString alloc] initWithFormat:usernameField.text];
[usernameField setText:username];
NSUserDefaults *stringDefaultUsername = [NSUserDefaults standardUserDefaults];
[stringDefaultUsername setObject:username forKey:#"stringKey2"];
NSLog(#"String [%#]", username);
}
-(IBAction) savePassword: (id) sender
{
password = [[NSString alloc] initWithFormat:passwordField.text];
[passwordField setText:password];
NSUserDefaults *stringDefaultPassword = [NSUserDefaults standardUserDefaults];
[stringDefaultPassword setObject:password forKey:#"stringKey3"];
NSLog(#"String [%#]", password);
}
- (void)viewDidLoad
{
[addressField setText:[[NSUserDefaults standardUserDefaults] objectForKey:#"stringKey1"]];
[usernameField setText:[[NSUserDefaults standardUserDefaults] objectForKey:#"stringKey2"]];
[passwordField setText:[[NSUserDefaults standardUserDefaults] objectForKey:#"stringKey3"]];
[super viewDidLoad];
}
#end
//.h second view controller LiveView.h
#import <UIKit/UIKit.h>
#import "CamSetup.h"
#interface LiveView : UIViewController
{
NSString *theAddress;
NSString *theUsername;
NSString *thePassword;
CamSetup *camsetup; //here is an instance of the first class
}
#property (nonatomic, retain) NSString *theAddress;
#property (nonatomic, retain) NSString *theUsername;
#property (nonatomic, retain) NSString *thePassword;
#end
//.m second view LiveView.m file
#import "LiveView.h"
#interface LiveView ()
#end
#implementation LiveView
#synthesize theAddress, theUsername, thePassword;
- (void)viewDidLoad
{
[super viewDidLoad];
theUsername = camsetup.username; //this is probably not right?
NSLog(#"String [%#]", theUsername); //resut here is NULL
NSLog(#"String [%#]", camsetup.username); //and here NULL as well
}
#end
There are 5 issues in this code:
If you are using ARC, all the "retain" in your #properties should be changed to "strong"
You name your iVars and properties the same thing. (common bad practice)
You are always directly accessing iVars and not properties in your code.
You don't retain your instance of CamSetup in the second object.
The direct cause of your problem: in the second object you've only created a placeholder for a CamSetup instance, you've not created one nor passed one to it! self.camSetup in your second object is empty right now.
Let's go step by step:
First, give your iVars different names from your properties. This is best practice, especially for a beginner! :)
#interface CamSetup : UIViewController <UITextFieldDelegate>
{
NSString *_address;
}
#property(nonatomic, strong) NSString *address;
#end
#implementation CamSetup
#synthesize address=_address;
...
#end
This is important, because you've setup properties, but in your code, you are not using them, you are directly accessing your iVars. Since you've named them the same thing, you might not see this.
Let's look at your first object. Every "address" in your code is going to your iVar and not property. Generally, you want to access the iVars via your properties unless you're sure otherwise. The #synthesize creates a getter and setter method for your iVar that will retain the var because you told it to in your #property statement. However, when you directly access your iVar's you're not going through those accessors and thus the stuff you wrote in your #properties doesn't matter. You could end up misunderstanding a lot of errors and bugs if you aren't clear about this. In your first object this worked anyway because the alloc/init sets a retain on the object, but I noticed you always do this, and that's going to get you into trouble.
Here's what the saveAddress: method would look like using properties:
-(IBAction) saveAddress: (id) sender
{
self.address = [[NSString alloc] initWithFormat:self.addressField.text];
[self.addressField setText:self.address];
NSUserDefaults *stringDefaultAddress = [NSUserDefaults standardUserDefaults];
[stringDefaultAddress setObject:self.address forKey:#"stringKey1"];
NSLog(#"String [%#]", self.address);
}
Next, in your second object you need to set properties for the CamSetup instance! Right now, you just have an iVar.
#import <UIKit/UIKit.h>
#import "CamSetup.h"
#interface LiveView : UIViewController
{
NSString *_theAddress;
NSString *_theUsername;
NSString *_thePassword;
CamSetup *_camSetup; //here is an instance of the first class
}
#property (nonatomic, retain) CamSetup *camSetup; // in synthesize we'll specify that this property uses the _camSetup iVar
#property (nonatomic, strong) NSString *theAddress;
#property (nonatomic, strong) NSString *theUsername;
#property (nonatomic, strong) NSString *thePassword;
#end
The implementation:
#import "LiveView.h"
#interface LiveView ()
#end
#implementation LiveView
#synthesize camSetup = _camSetup;
#synthesize theAddress = _theAddress;
#synthesize theUsername = _theUsername;
#synthesize thePassword = _thePassword;
- (void)viewDidLoad
{
[super viewDidLoad];
self.theUsername = self.camSetup.username; //this is probably not right?
// well, it's better now, but where is camSetup coming from??? it's undefined now
NSLog(#"String [%#]", self.theUsername); //resut here is NULL
NSLog(#"String [%#]", self.camSetup.username); //and here NULL as well
}
#end
We've created an iVar and property pair that will hold a pointer to a CamSetup instance and will retain that pointer (if we set it using the property). However, where is that CamSetup instance being created? Where do you alloc/init it?
There are many possible answers to this question. If CamSetup had getters for address, username, password that read them back in from your user defaults, then all you'd have to do is alloc/init a CamSetup and set it to camSetup. However, right now your first object has no functionality to retrieve the saved data so we can't do that. (still, this is the solution I'd hope you'd implement).
You might be initializing both of these in your app delegate? However, if you are using storyboard then likely it is initializing these object for you when it initializes your interface. In this case, in your appDelegate app has finished launching... method, you'll have to retrieve pointers to these instances, then the camSetup property on your second object, to point to the first. To tell you how to do this, we'd have to know detailed specifics of your app. Still, this wouldn't be doing it the best way.
Best practice would be to create an object which saves and retrieves these data from user defaults for you. This future proofs your implementation should you later want to change the way you store these data. You'd just change the way they are stored/retrieved within their class.
Your problem is here:
CamSetup *camsetup; //here is an instance of the first class
You aren't doing anything to make camsetup refer to the same instance of the CamSetup class that is taking input from the user. The camsetup variable is never initialized, hence it's NULL, hence any properties you try to retrieve from it will also be NULL.
How exactly you'd fix this depends on the structure of your program. One possibility is to have whatever code is creating the LiveView controller set the camsetup value. For example:
LiveView *liveViewController = [LiveView alloc] initWithNibName:#"LiveView" bundle:nil]];
liveViewController.camsetup = camSetupController;
(you'd need to make camsetup a property to do this).
BUT, from a design standpoint, having the one view controller have a reference to the other is probably the wrong way to go about solving this problem -- it introduces unnecessary dependencies between the two controllers. For example, say you later decide to make it possible to go directly to the LiveView controller upon program launch, using a saved name/password; you can't do that if LiveView depends on getting its input from a CamSetup object.
So, a better approach might be to have LiveView take its input from NSUserDefaults, and/or by having the code that's calling LiveView set the username/password properties.
-- edit --
For example, retrieve the data from the user defaults like:
self.address = [[NSUserDefaults standardUserDefaults] objectForKey:#"stringKey1"];
Is the camsetup initialized? If not -- initialize it first.
There are many ways to do this, most of which depend on how the two controllers are related to each other (like how are you going from the first to the second).
However, in your case you don't need to pass the string at all, since you are saving them in user defaults. Just read the values of those defaults in your second controller.
Hi i got your problem just do one thing.
When you are setting value in NSUserDefaults it need synchronization so just synchronize it.
[stringDefaultUsername synchronize];
And retrieve data using
NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:#"stringKey2"];
Follow apple NSUserDefaults Class Reference for more info

NSMutableArray count keeps changing

I have too much code to know which i need to quote here, but in my app delegate I have an NSMutableArray. Then in another class, it creates a new entry to the NSMutableArray but upon passing back to another class which should use that to display something on screen, it doesn't display anything. Putting an NSLog for the NSMutableArray count at the end of the class creating it displays the number 1, and then putting the same NSLog code at the start of the class which is meant to use that returns 0.
Any ideas why this is?
EDIT: Ok, i'll try and include all related code..
app delegate.h:
#interface palettesAppDelegate : NSObject <UIApplicationDelegate> {
NSMutableArray *colourPalettesContainer;
}
#property (assign, readwrite) NSMutableArray *colourPalettesContainer;
#end
app delegate.m:
#import "palettesAppDelegate.h"
#implementation palettesAppDelegate
#synthesize colourPalettesContainer;
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
Homeview.h:
#import <UIKit/UIKit.h>
#import "HandlingPalettes.h"
#interface HomeView : UIViewController {
HandlingPalettes *handlingPalettes;
}
#end
Homeview.m:
#import "HomeView.h"
#import <QuartzCore/QuartzCore.h>
#implementation HomeView
- (void)viewDidLoad {
[super viewDidLoad];
handlingPalettes = [[HandlingPalettes alloc] init];
[handlingPalettes newPalette];
}
-(void)viewWillAppear:(BOOL)animated {
NSLog(#"view will appear: %i", [dataCenter.colourPalettesContainer count]);
int numberOfExisting = [dataCenter.colourPalettesContainer count];
}
- (void)dealloc {
[handlingPalettes release];
[super dealloc];
}
#end
HandlingPalettes.h:
#import <UIKit/UIKit.h>
#interface HandlingPalettes : UIViewController {
}
-(void)newPalette;
#end
HandlingPalettes.m:
#import "HandlingPalettes.h"
#import "HomeView.h"
#import "palettesAppDelegate.h"
#implementation HandlingPalettes
-(void)newPalette {
palettesAppDelegate *dataCenter = (palettesAppDelegate *)[[UIApplication sharedApplication] delegate];
//If this is the first palette
if (dataCenter.colourPalettesContainer == nil) {
dataCenter.colourPalettesContainer = [[NSMutableArray alloc] init];
}
//Add a new palette
[dataCenter.colourPalettesContainer addObject:#"Test1", #"Test2", nil];
NSLog(#"Handling: %i", [dataCenter.colourPalettesContainer count]);
}- (void)dealloc {
[super dealloc];
}
#end
Your main mutablearray is in your app delegate. So, see what happens if in EVERY METHOD that you want to access the array you have the line to set up the app delegate relationship
palettesAppDelegate *dataCenter = (palettesAppDelegate *)[[UIApplication sharedApplication] delegate];
Now, when you call the dataCenter object you will be referencing the App Delegate and your program will find the array.
You may also find that you will need to have an #import "palettesAppDelegate.h" in each object that is going to reference the App Delegate.
Note, just adding the app delegate code is not necessarily the proper way to deal with this issue from an architectural standpoint. But if it works you at least know the answer to your original question.
I suspect the problem is ultimately related to confused memory management of the colourPalettesContainer member. You release it in the app delegate's dealloc method, but that class never retains it! It would be much cleaner if you'd follow Apple's memory management guidelines: your classes should only release objects that they own (i.e., that they themselves retained earlier). For example, you can do this by declaring the array's property retain:
#property (retain) NSMutableArray *colourPalettesContainer;
(To prevent leaking the array, you'll also need to release or autorelease it in the newPalette method. Retain and release should always come in close pairs.)
But even better, why not simply create the array in the app delegate's init method, or in its accessor (if for some reason you want to continue creating it only on its first use)? Unless you want to replace all palettes at once, there is no reason to let the array be assigned to from outside the app delegate.
#interface PalettesAppDelegate : NSObject <UIApplicationDelegate> {
#private
NSMutableArray *colourPalettesContainer;
}
#property (readonly) NSMutableArray *colourPalettesContainer;
#end
#implementation PalettesAppDelegate
- (NSMutableArray *)colourPalettesContainer {
if (colourPalettesContainer == nil) {
colourPalettesContainer = [[NSMutableArray alloc] init];
return colourPalettesContainer;
}
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
To make the design even cleaner, change the type of the colourPalettesContainer property to NSArray * and add an -addPalette: method to the app delegate. (It is rarely a good idea to publicly expose a mutable array inside a class.) You can then simply get rid of -newPalette in HandlingPalettes. (If you want to have all your palette-handling methods in HandlingPalettes, then simply move the array there. If you need to access the palettes from random places in your app, then you can simply put a retained reference to your HandlingPalettes object in the app delegate.)
Once you clean up the object ownership mess, the count mismatch will either resolve itself "by magic" or the cause will likely become much more obvious. In the latter case, check that the HomeView's dataCenter is actually the same object as the one in HandlingPalettes. (You omitted how HomeView gets its reference — are you sure you aren't creating another instance of the app delegate by accident?)
(By the way, you probably meant to use -addObjects:, not -addObject: in newPalette. Note also that all class names should be capitalized, with no exceptions: i.e., always use PalettesAppDelegate, never palettesAppDelegate. If for some reason Xcode's project template created it like that, simply rename the class. Lowercase class names are much too easy to confuse with variable names. Also, try to find better names in general: e.g., instead of HandlingPalettes, I'd use PalettesViewController (to reflect the fact that it is a subclass of UIViewController); and instead of dataCenter, I'd rather just choose appDelegate.)
I would be inclined to get rid of the newPalette method, and instead create a getter method for colourPalettesContainer in your app delegate.
ie:
appdelegate.h
#interface PalettesAppDelegate : NSObject <UIApplicationDelegate> {
NSMutableArray *colourPalettesContainer;
}
#property (non-atomic, retain) NSMutableArray *colourPalettesContainer;
#end
#implementation palettesAppDelegate
appdelegate.m
#import "appdelegate.h"
#synthesize colourPalettesContainer;
- (NSMutableArray *) colourPalettesContainer{
if(colourPalettesContainer==nil){
colourPalettesContainer=[[NSMutableArray alloc] init];
}
return colourPalettesContainer;
}
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
you should then be able to add items by calling
[appDelegate.colourPalettesContainer addObject:object];

Using NSDate as a property gives me EXC_BAD_ACCESS, why?

I am adding NSDate as a pointer with a property, and every time I unload my view, it crashes with EXC_BAD_ACCESS. I am doing (not posting full code):
.h
NSDate *scheduledDate;
#property (nonatomic, retain) NSDate *scheduledDate;
.m
#synthesize scheduledDate;
- (void)dealloc {
[super dealloc];
[asset release];
[passedDate release];
[eventDate release];
[eventName release];
}
I have not done anything else with the pointer, but I still get EXC_BAD_ACCESS. Why is this happening? Is there a different way to set the property for NSDate?
SORRY:
I fixed an error in my question code, it was only a copy and paste issue, not a fix to my problem, it still exists.
You're calling [super dealloc] before the release in your dealloc implementation. That means the [scheduledDate release] is release some non-free memory (which is no longer nil).
Specifically, change the order so [super dealloc] is last:
- (void)dealloc {
[asset release];
[passedDate release];
[eventDate release];
[eventName release];
[super dealloc];
}
Your code looks inconsistent. The #property declaration should be for "NSDate scheduledDate", not "NSString ...".
If you are not using #property (nonatomic, retain) NSDate *scheduledDate; then there is no need to synthesize scheduledDate.
Also, how are you determining the value of scheduledDate? post the code you are using for it.
Also, post the crash log that your app produces.
Your #property (nonatomic, retain) is for "passedDate", not "scheduledDate". Since "scheduledDate" is not getting instantiated and retained by #property (nonatomic, retain), then attempting to release it is attempting to release something that hasn't been created yet (it isn't being synthesized as a property).

Core Data Strings

Im new to using core data and having really basic problems. Im trying to have the user enter a string and then be able to save that string and allow it to be returned to them at some point. But i cannot seem to get it to save. In fact the program quits when I attempt to run the following method. I can post the rest of my project, but i thought maybe that would be annoying so let me know if seeing it in greater detail would help. Thanks so much.
James
.h: file
#import <UIKit/UIKit.h>
#import "People.h"
#class rootViewController;
#interface data : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UITextField *firstName;
UITextField *lastName;
UITextField *phone;
UIButton *saveButton;
NSMutableDictionary *savedData;
//Used for Core Data.
NSManagedObjectContext *managedObjectContext;
NSMutableArray *peopleArray;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *eventArray;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (void)setUpTextFields;
- (void)saveAndReturn:(id)sender;
- (void)fetchRecords;
#end
.m file:
-(void)saveAndReturn:(id)sender{
People *userEnteredName = (People *)[NSEntityDescription insertNewObjectForEntityForName:#"People" inManagedObjectContext:managedObjectContext];
[userEnteredName setName:firstName.text];
//NSError *error;
//if (![managedObjectContext save:&error]) {
// This is a serious error saying the record could not be saved.
// Advise the user to restart the application
//}
[peopleArray insertObject:userEnteredName atIndex:0];
}
From the error you gave you must have named the People object differently - in the model are you using "People" for both class and entity name (those can be the same)?
Edit:
After reviewing your code, you had multiple problems:
1) In the app delegate you did "[data alloc]" but no init. That was where you set the managed object context, but it was never used... not just because of the lack of an init but because...
2) The place where the data controller was really built and used from was the rootViewController. That's the one that is actually doing all the work, the one in the app delegate is just discarded.
3) So where to get the context then? Honestly the best spot is in the data controller, one fix I know worked was putting this line before every time the context was accessed:
#import "UserProfileAppDelegate.h"
// Then in the method before the use of context........
self.managedObjectContext = [((UserProfileAppDelegate *)[[UIApplication sharedApplication] delegate]) managedObjectContext];
When that was in place, the project ran. I think though you should put that into something like a viewDidLoad on the data controller (if it has a view that is ever used).

iPhone SDK - instance variable out of scope issue

I am getting crazy over this error. Compiler is saying out of scope for an instance NSSString variable. Never had this thing before and used thousands of NSString instance variables!
Here is my class .h file
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#import "Snapshot.h"
#interface RecordAudioViewController : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate> {
NSString *filename;
}
#property (nonatomic, retain) NSString *filename;
- (IBAction) recordAudio;
- (IBAction) playAudio;
#end
Variable is synthesized properly. I initalize filename variable in viewDidLoad method. I want to use it in IBAction method recordAudio, but compiler always says out of scope? Why is that, is this a bug or something?
Here is .m code. viewDidLoad method where I set the filename instance variable:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *tmpDir = NSTemporaryDirectory();
filename = [NSString stringWithFormat: #"%.0f.%#", [NSDate timeIntervalSinceReferenceDate] * 1000.0, #"caf"];
NSLog(filename);
}
And the IBAction method
- (IBAction) recordAudio
{
NSLog(filename); // here I get out of scope message when moving over with mouse cursor and when steping over this line EXC_BAD_ACCESS
}
The entire .m file can be seen here: http://pastie.org/1021993
Actually, if you set filename = [NSString stringWithFormat...], the autoreleased result is NOT retained.
However, if you use self.filename = [NSString stringWithFormat...] it WILL retain the string. Kinda looks like the string is getting released out from under you because you're not retaining it.
You mentioned that you initialize the variable filename in the viewDidLoad method. if you mean nsstring alloc and init methods by initializing, i don't think that you are going the right way. It is not necessary to initialize a synthesized string, or more generically any strings. I'm not sure whether you meant this by initializing, but i gave my opinion based on the idea that i got from your Ques.
Is viewDidLoad actually happening? If it doesn't get called, that would perfectly explain the crash in recordAudio as it hasn't been initialised.