I am trying to set the property of a child view controller (DateViewController) from the parent and getting a bad access error the second time I do so. Here is the code. This is the DateViewController.h. The problem lies with the selectedDate property:
#import <UIKit/UIKit.h>
#protocol DateViewDelegate <NSObject>
-(void) dateViewControllerDismissed:(NSDate *)selectedDate;
#end
#interface DateViewController : UIViewController {
IBOutlet UIDatePicker *dateReceipt;
id myDelegate;
}
-(IBAction)btnDone;
#property(nonatomic,assign)NSDate *selectedDate;
#property(nonatomic,assign)id<DateViewDelegate> myDelegate;
#end
Inside DateViewController.m, I do synthesize selectedDate. Now in the parent view controller (ComdataIOSViewController.m) I set the selectedDate property of the DateViewController to the variable receiptDate which is declared as an NSDate * in the #interface section of ComdataIOSViewController.h. This is a snippet of ComdataIOSViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
receiptDate = [NSDate date];
}
-(IBAction)btnSetDate {
dlgDate=[[DateViewController alloc] initWithNibName:nil bundle:nil];
dlgDate.selectedDate = receiptDate;
dlgDate.myDelegate = self;
[self presentModalViewController:dlgDate animated:true];
[dlgDate release];
}
-(void) dateViewControllerDismissed:(NSDate *)selectedDate
{
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
[dateFormat setDateStyle:NSDateFormatterShortStyle];
receiptDate = selectedDate;
dateString = [dateFormat stringFromDate:receiptDate];
lblDate.text = dateString;
}
So the first time I click the set date button on the parent controller, the DateViewController appears, I pick the date from the datepicker control, and the controller is dismissed. In the parent view controller, dateViewControllerDismissed gets called and I set the receiptDate to the selectedDate parameter. The next time I click the date button, I get a bad access error where I set the DateViewController's selectedDate property to the receiptDate. I'm assuming this is some sort of memory issue that I'm not handling correctly. IOS programming is still new to me.
I have found several problems in your code which could lead your application to crash. Actually they are memory management problem.
Assigning autoreleased object to receiptDate:
receiptDate = [NSDate date];
when you will try to use this value later it will cause app crash because memory where receiptDate point could be already released. You could fix it by retaining the value:
receiptDate = [[NSDate date] retain];
and releasing in dealloc or anywhere you are changing it (I dont know how it is declared. It should be retain property).
You are assigning NSDate without retaining it:
receiptDate = selectedDate;
you could fix it by retaining:
receiptDate = [selectedDate retain];
I am sorry because I could not write all aspects of memory management in objective-C. It is better to use ARC if you don't know iOS memory managent well.
You could find a lot of useful information in this two guides from Apple: Advanced Memory Management Programming Guide and Memory Management Programming Guide for Core Foundation
Your property is never retained. What I would suggest to do would be to change the assign to retain in your property declaration. That'll solve your problem and you won't have to call retain everywhere you set selectedDate. The property will do that for you.
If you're not using ARC, don't forget to set the property to nil in your dealloc method, like so:
self.selectedDate = nil;
Note that I use self.selectedDate. It's important so that selectedDate is accessed as a property, not a variable.
Related
I do not know what to say but this bug has been haunting me for the last 2 days and I cannot get it to work.
I want to add a UIDatePicker to my view. But for some reason it is not scrolling. I just dragged and dropped the control but without any luck. I checked my view is user-interaction enabled. I even created a dummy view controller which only has a picked in it and made it the initial view controller, still no scrolling happening.
All other interactions are fine.
I created a new project from scratch and simply dropped a picker in its view and tested. It scrolled!!! Not sure why the one in my app is not.
I know you will ask for code, but there is no code here. Jut adding the picker in an initial vc and no action is happening.
Any idea what could be wrong? where i should look?
Cheers
Try to use following code
#import "DatePickerViewController.h"
#interface DatePickerViewController ()
#end
#implementation DatePickerViewController
-(void)getSelection:(id)sender
{
NSLocale *usLocale = [[NSLocale alloc]
initWithLocaleIdentifier:#"en_US"];
NSDate *pickerDate = [_datePicker date];
NSString *selectionString = [[NSString alloc]
initWithFormat:#"%#",
[pickerDate descriptionWithLocale:usLocale]];
_dateLabel.text = selectionString;
}
.
.
.
#end
okay below is a standard example of creating a datepicker
- (void)viewDidLoad {
CGRect pickerFrame = CGRectMake(0,250,100,100);
UIDatePicker *myPicker = [[UIDatePicker alloc] initWithFrame:pickerFrame];
[myPicker addTarget:self action:#selector(pickerChanged:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:myPicker];
[myPicker release];
}
- (void)pickerChanged:(id)sender
{
NSLog(#"value: %#",[sender date]);
}
this is all good and well. I'm a little used to creating elements in IB so when I create an object programatically I'm not sure how to access the data.
What I mean is.. should I assign myPicker to a class property and then access it as _myPicker?
Or lets say I want to access the date inside of the pickerChanged method without calling another method. Should I assign an NSDate property and re-assign it every time the picker is changed?
I ran into some memory issues when I was trying to do it that way. I had another method grabbing _theDate, and it probably tried to access it at the same time pickerChanged was modifying it?
Anyway, what I'm getting at is "whats the proper workflow when creating things like action sheets, and pickers programmatically". When these things are changed, how should the resulting data be saved so the rest of the class can access it?
Bonus question:
Is there a difference between this?
for(UILabel *myLabel in view.subviews){
NSLog(myLabel.text);
}
and this? Do I need to check the class all the time if i know my view only contains a certain kind of object?
for((id) myLabel in view.subviews){
if([myLabel isKindOfClass:[UILabel class]){
UILabel *theLabel = myLabel;
NSLog(myLabel.text);
}
}
Generally, you will just define properties if you'll need to access them more than once. You can do this in the .m file's interface:
#interface MyObject()
#property (weak, nonatomic) UIDatePicker *myPicker;
#end
You will then be able to access it by either _myPicker or self.myPicker.
You shouldn't need another NSDate property in your class because you can access the set date at any time:
_myPicker.date
For your last question: the latter of the two is merely extra sanity checks. While you're writing your own code, and you should know what subviews you're adding in, it can't hurt to double check the type of the subviews incase anything should go wrong and you try to access selectors that don't exist. This is a larger programming question though and not necessarily objective-c or iOS specific.
The documented approach is to intercept the UIControlEventValueChanged event, as per your example.
You would then typically copy the [sender date] value to a property in your pickerChanged: method.
If the user hits a save button, then the object that presented the view containing the picker should be able to retrieve the selected date via the property.
It's not considered good practice to use isKindOfClass:. You should structure your code such that you always know what class you're dealing with.
Also, you should really switch to ARC so you don't need to worry about calling release
You need to declare a UIDatePicker property to hold one instance of your child controller
This is what you need to add in your .h file:
#property (strong, nonatomic) UIDatePicker *myPicker;
And then in your .m file you need to add a data source method for this date picker. something like what rdelmar has instructed above:
self.myPicker = [[UIDatePicker alloc] init];
Hi a very simple app it takes in 2 arguments via 2 text boxes, and then totals them and displays them in a label called result. The idea is to have it handled via an object called brain, for which in the later part i have given the code. problem is foo is zero and when you click the button the result goes to nothing.
The plan is to use this to build a better model view architecture for a bigger app i have completed.
#import "calbrain.h"
#import "ImmyViewController.h"
#interface ImmyViewController ()
#property (nonatomic, strong) calbrain *brain;
#end
#implementation ImmyViewController
#synthesize brain;
#synthesize num1;
#synthesize num2;
#synthesize result;
-(calbrain *) setBrain
{
if (!brain) {
brain = [[calbrain alloc] init];
}
return brain;
}
- (IBAction)kickit:(UIButton *)sender {
NSString *number1 = self.num1.text;
NSString *number2 = self.num2.text;
NSString *foo;
foo = [brain calculating:number1 anddouble:number2];
self.result.text = foo;
// self.result.text = [brain calculating:self.num1.text anddouble:self.num2.text];
}
#end
#implementation calbrain
-(NSString *) calculating:(NSString *)number1 anddouble:(NSString *)number2
{
double numb1 = [number1 doubleValue];
double numb2 = [number2 doubleValue];
double newresult = (numb1 + numb2);
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber n numberWithFloat:newresult]];
return numberAsString;}
Check your brain using NSLog in the (IBAction)kickit:(UIButton *)sender function. I guess you didn't initialise brain. If this is not the case, you need to provide more code.
i did just that, i came to the conclusion the setter for brain isnt working properly
i put the alloc init line of code before i needed to alloc init the brain, and it works fine, i stubbed out the setter,
i will go back and see why it wasnt overriding the setter made by properties, but interesting stuff none the less. it means i can change my actual larger app to have a cleaner more organised architecture.
thanks for your time.
Try initializing your brain object in viewDidLoad() using your setter method. You have to call setter method to get your brain object initialized.
Something like this
viewDidLoad()
{
brain = [self setBrain];
//You can also do this
brain = [[calbrain alloc] init];
}
and use that brain object in your (IBAction)kickit: method.
Hope this helps.
Let me begin to tell you that I am new to Objective C. I have just finished Big Nerd Ranch's book and i want to create a real simple and basic app to learn more.
My idea was to create an app that will calculate the weeks between 2 dates. I have created a class for that and tested it. That works.
As you can see below, I have created to views (programmatically), One with the dates and the other will become visible when you click on start or end date.
If you select a date and click on the button 'calculate weeks', you will go back to the first view.
No my big question is, how do I get this selected value back to my main screen? I have tried several possibilities and search the web for information, but I couldn't get is to work.
I know this should be real easy, but for me at this moment it isn't. :-)
I have created a NSMutableArray that contains the values "Start date" and "End date". My idea was to add the value of the UILabel from the SelectDateView to this array.
I have created a property In the inputview #property (readwrite, retain) NSMutableArray *datesArray; for that.
in the selectDateViewController i have created another property #property (nonatomic, assign) BITInputViewController *ivc; so I (in my opinion) can add a value to datesArray.
When I select a date this method is called, it works for the UILabel on SelectDateView, but doesn't do anything with the datesArray.
- (void)LabelChange:(id)sender{
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.dateStyle = NSDateFormatterMediumStyle;
dateLabel.text = [NSString stringWithFormat:#"%#",
[df stringFromDate:datePicker.date]];
[ivc.datesArray addObject:dateLabel.text];
if (ivc.datesArray) {
for (NSString *d in ivc.datesArray) {
NSLog(#"This is in datesArray %#",d);
}
}else NSLog(#"!ivc.datesArray");
}
When I test the app and select a date, I always see "!ivc.datesArray" in the log file.
I also do this check of ivc.datesArray in -(void)viewWillAppear:(BOOL)animated
and here I see the current date, as I set this date in - (void)viewDidLoad
with this dateLabel.text = [NSString stringWithFormat:#"%#",[df stringFromDate:[NSDate date]]];
(When I print out the array in the inputview, it does show start date and end date, but not the selected date. )
Hopefully someone can give me a few pointers on this.
declare a NSString* date above the #interface in your view1.m file and the create a method in tht first class it should be something like
-(void)passDate:(NSString *)dateString
{
date = [NSString stringWithFormat:#"%#",dateString];
}
and in your second class create object of first class like
View1 * vc = [[View1 alloc]init];
[vc passDate:df];
Hope it works. You have to declare NSString before the #interface and no property and synthesize.
I am fairly new with Objective-C memory management and although I thought I understood it, I have a problem that I cannot manage to solve.
I have this property:
#property (nonatomic, retain) NSDate *dateDisplayed;
that I assign in my viewDidLoad with a custom method:
self.dateDisplayed = [self dbDateFormatToNsDate:#"15/11/2011"];
My dbDateFormatToNsDate method looks like this:
- (NSDate *) dbDateFormatToNsDate:(NSString *) date {
NSDateFormatter *d = [[NSDateFormatter alloc] init];
[d setDateFormat:#"dd/MM/yyyy"];
NSDate *toReturn = [d dateFromString:date];
[d release];
return toReturn;
}
So it returns an autoreleased object (if NSDate follows the convention). But when I get out from viewDidLoadin another function trying to read dateDisplayed:
[dateDisplayed isEqualToDate:[self dbDateFormatToNsDate:#"15/11/2011"]]
I get an NSZombie exception. Thanks for any help!
When assigning using self.property the property is retained because the setter methos is called but when just assigning without using self. it isnt. Assuming of course that you have retain in the propery definition of the .h file.
You could [d autorelease]; instead. I might be totaly off on this, but the toReturn NSDate might need to keep the formatter around even after youve released it, causing the bad access:
Try:
- (NSDate *) dbDateFormatToNsDate:(NSString *) date {
NSDateFormatter *d = [[NSDateFormatter alloc] init];
[d setDateFormat:#"dd/MM/yyyy"];
NSDate *toReturn = [d dateFromString:date];
[d autorelease];
return toReturn;
}
Since you are returning an object created by a method that does not start with alloc, copy, mutableCopy, convention says you should autorelease it.
Autorelease means that it will be release in the future. If the caller of the method needs it to stick around, then they will retain it.
Read the memory management guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
Here's the rules:
1 & 3 apply to that method. 2 applies to a calling class that may need to hold it.
1 - You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
2 - You can take ownership of an object using retain
A received object is normally guaranteed to remain valid within the method it was received in, and that method may also safely return the object to its invoker. You use retain in two situations: (1) In the implementation of an accessor method or an init method, to take ownership of an object you want to store as a property value; and (2) To prevent an object from being invalidated as a side-effect of some other operation (as explained in “Avoid Causing Deallocation of Objects You’re Using”).
3 - When you no longer need it, you must relinquish ownership of an object you own
You relinquish ownership of an object by sending it a release message or an autorelease message. In Cocoa terminology, relinquishing ownership of an object is therefore typically referred to as “releasing” an object.
4 - You must not relinquish ownership of an object you do not own
This is just corollary of the previous policy rules, stated explicitly.