I'm writing simple notes app, but when I try to assign title to note.title I got error.
-(Notez*)createNoteWithTitle:(NSString*)titleNote andText:(NSString*)textNote
{
Notez *newNote = [Notez new];
if (!_notesArray) {
_notesArray = [NSMutableArray new];
}
newNote.dateCreated = [NSDate new];
newNote.title = [NSString stringWithFormat:#"23"];
newNote.text = [NSString stringWithFormat:#"233"];
[_notesArray addObject:newNote];
return newNote;
}
Notez.h :
#import
#interface Notez : NSObject
#property NSString *text;
#property NSString *title;
#property NSDate* dateCreated;
-(Notez*)createNoteWithTitle:(NSString*)title andText:(NSString*)text;
-(void)save;
+(instancetype)sharedManager;
-(NSArray*)sortedNotes;
-(void)removeNoteAtIndex:(NSUInteger)index;
#end
note.dateCreated is OK, but note.title and note.text isn't.
They are both NSString...
- (IBAction)addNote:(id)sender {
[[Notez sharedManager] createNoteWithTitle:#"Note Title" andText:#"Note Text"];
}
Since you declared title and text as properties, but for some reason get the exception:
[Notez setTitle:]: unrecognized selector sent to instance, apparently
I can only make another guess here. Usually, when declaring a property, you get a setter and a getter method for it. This way you can omit writing these by hand if you have a lot of instance variables on a class.
Using dot notation is then equivalent to calling the setter or the getter. So in your case
newNote.title = [NSString stringWithFormat:#"23"];
is equivalent to:
[newNote setTitle:[NSString stringWithFormat:#"23"]];
Now, the exception that you suggests that the setter method: setTitle: actually does not exist on your object. I'm not sure what the reason for this might be, but you could try to explicitly synthesize your properties.
Therefor, in your .m-file add the following lines of code:
#synthesize title;
#synthesize text;
Not sure if this will actually solve your issue, but I hope the explanation of properties, getters and setters helps you to understand a little more what's going on.
Related
I have a simple PFObject subclass setup. MyPFSubclass. It looks something like this:
#interface MyPFSubclass: PFObject <PFSubclassing>
+ (NSString *)parseClassName;
#property (retain) NSString *myString;
#end
#import <Parse/PFObject+Subclass.h>
#implementation MyPFSubclass
#dynamic myString;
+ (NSString *)parseClassName {
return #”MyPFSubclass”;
}
#end
This works great, as expected, until what I discovered today.
I can set the myString value and read and write as expected, an NSLog shows the data to be what I set it to.
MyPFSubclass *obj = [MyPFSubclass new];
obj.myString = "#hello";
//prints expected value as set above
NSLog(#"%#", obj.myString);
obj[#"myString"] = "#hello";
//prints expected value as set above, again
NSLog(#"%#", obj[#"myString"]);
However, if I do the following, I do not get the changed result.
obj[#"myString"] = #"Hello";
//prints original value, not as set above
NSLog(#"%#", obj.myString);
It seems the key name setters and getters are independent to the subclass setters and getters. I don't want this!
As an example, I have a subclassed view that takes a generic PFObject and key name from which it can get and set values.
How can I resolve this? Any reason why I cannot mix usage of the subclass and keyname getters and setters?
Not an exact answer, however it is a solution.
I found that in the scenario of using a PFObject subclass, using setValue:forKeyPath is more reliable than using Parse's own bracketing syntax. i.e. myObject[#"myAttribute"].
Where this latter appears non-interchangeable with subclass properties, I found replacing it with setValue:forKeyPath works.
In a iOS app, I needed a property which is readonly for other classes but readwrite for self calls. So I followed this question and formed my code as,
In .h:
#interface TheClassName : NSObject {
NSString* str;
}
#property(nonatomic, retain, readonly) NSString* str;
#end
In .m:
#interface TheClassName()
#property(nonatomic, retain, readwrite) NSString* str;
#end
#implementation TheClassName
-(id)init {
if(self = [super init]) {
str = [[NSString alloc] initWithString:#"hello"];
}
return self;
}
#end
This procedure is valid also according to Apple Documentation. But I tried experimenting on my code and the problem started.
All I did is ignored the #interface part in the .m file with respect to Seva Alekseyev's answer here, but compiler showed no warning or error when I used it as str = [[NSString alloc] initWithString:#"hello"] in the .m file !!
I mean, the property is set to be readonly, so the statement should have produced an error, but it didn't. WHY??
NOTE: I am using Xcode 5.1.1
This code:
str = [[NSString alloc] initWithString:#"hello"];
doesn't call the property setter, it references the instance variable you defined directly:
#interface TheClassName : NSObject {
NSString* str;
}
and, by default, the backing instance variable will be called _str, so you should remove that instance variable definition and refer to _str directly (if you don't want to create a readwrite version of the property). As it currently stands the property str won't refer to the instance variable str without explicitly using a #synthesize str = str; statement.
I created a Food object that I'd like to be able to set and get attributes of (such as time). For some reason I'm allowed to set attributes, but I am not able to the get attributes. I receive the following error by simply calling food.time
Property 'time' not found on object of type 'conts __strong id'
I'm not sure if the problem is with putting it in, and then retrieving it from, an array or if it is how my object class is defined. In this example I've simplified it so that you can see how I'm using it.
Some controller (with other methods not shown here)
#import "Food.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *foodArray = #[firstFood];
for (id food in foodArray) {
UILabel *foodLabel = [[UILabel alloc]
initWithFrame:CGRectMake(10, 180, self.view.frame.size.width-20, 50)];
foodLabel.backgroundColor = [UIColor clearColor];
foodLabel.text = food.time; // This line causes error
foodLabel.textColor = [UIColor blackColor];
[foodLabel setFont:[UIFont fontWithName:#"Courier" size:14]];
[self.view addSubview:foodLabel];
}
}
Food.h
#import <Foundation/Foundation.h>
#interface Food : NSObject
#property (strong, nonatomic) NSString *time;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *place;
#end
Food.m
#import "Food.h"
#implementation Food
#end
As far as the compiler knows food is just a generic NSObject pointer. You either need to cast it to a Food object, or just change your definition within the for loop.
for (Food *food in foodArray) {
//...etc
}
That is assuming firstFood is actually a Food object, since you do not show it's definition in your snippet.
If you don't want to change the type, you can send any message to id, and let it figure out at run-time whether it is valid:
foodLabel.text = [food time];
would also be valid, but you are unable to use the dot syntax on an object of type id, either cast it or use the standard bracket syntax (which will fail at run-time if that object does not respond to that message).
This is my first time using this site and I am quite new to Objective-c. I'm sure this is a simple question but for some reason I am having a lot of issues. The app is designed to have the user enter a string via textfield, then it will pick the rest of the sentence and display it. The issue appears to be that my *name will be retained after the keyboard method and work once in the changelabel method. Then if i press the button again, invoking the changelabel method, the name appears to have been released and crashes the app.
#import
#import "Array.h"
#interface RandomBoredViewController : UIViewController {
UILabel *label;
UIButton *button;
UITextField *textField;
Array *array;
NSString *name;
NSString *description;
NSMutableString *whole;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) IBOutlet UIButton *button;
#property (nonatomic, retain) IBOutlet UITextField *textField;
#property (nonatomic, retain) Array *array;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSMutableString *whole;
-(IBAction) keyBoard;
-(IBAction) changeLabel;
#end
and my .m
#import "RandomBoredViewController.h"
#implementation RandomBoredViewController
#synthesize label;
#synthesize checker;
#synthesize button;
#synthesize textField;
#synthesize array;
#synthesize name;
#synthesize description;
#synthesize whole;
-(IBAction) changeLabel {
NSLog(#"Button being pushed");
description = [array getString];
NSLog(#"%#",description);
NSLog(#"%#",name);
name = [NSString stringWithString:name];
whole = [NSMutableString stringWithString:name];
NSLog(#"%#",name);
NSLog(#"%#",whole);
[whole appendString:description];
NSLog(#"%#",name);
NSLog(#"%#",whole);
label.text = whole;
NSLog(#"%#",name);
}
-(IBAction) keyBoard {
name = [NSString stringWithString:textField.text];
NSLog(#"%#",name);
label.text = [NSString stringWithString: name];
[textField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
array = [[Array alloc]init];
[array createArray];
NSLog(#"%i",[array arrayCount]);
whole = [[NSMutableString alloc]init];
name = [[NSString alloc]init];
}
- (void)dealloc {
[super dealloc];
[label release];
[button release];
[textField release];
[array release];
//[name release];
[description release];
}
#end
You are setting name to an autoreleased instance of NSString, this is probably what's causing your app to crash.
Use
self.name = [NSString stringWithString:textField.text];
Your synthesized mutator will retain the NSString and prevent it from being released.
Taking one thing in microcosm, the code you've posted creates two things named name — an instance variable and a property.
Instance variables are directly accessed storage. They have no behaviour.
Properties are named attributes accessed via getters and setters. So they may have arbitrary behaviour. They may report the values of instance variables or values calculated from instance variables or values calculated or obtained by any other means. Relevantly, the setters may retain, assign or act in any other way.
Instance variables may be accessed only by the instance of a class they belong to. Properties are usually intended to be accessed by anyone.
Since retaining is a behaviour and you've ascribed it to your name property, setting something to it would result in a retain. Instance variables can't have behaviour, so setting a value to it doesn't result in a retain or anything else.
As a result, this line:
name = [NSString stringWithString:name];
Creates a new string and returns a non-owning reference. Which means it'll definitely last for the duration of this autorelease pool (ie, you explicitly may pass it as an argument or return it safely, assuming you haven't taken manual control of your autorelease pools).
You store that reference to your instance variable. Instance variables don't have behaviour so the reference is stored but you still don't own that object. It's still only safe to use for the duration of that autorelease pool.
So when you access it in that method it's safe. When you access it later it's unsafe.
If instead you'd gone with:
self.name = [NSString stringWithString:name];
Then you'd have set that string to be the new value of the property. Because your property has the retain behaviour, you'd subsequently have completely safe access to the string object, until you say otherwise.
Because you've got a property with exactly the same name as an instance variable, you could subsequently access it either as just name or as self.name. Similarly you could have stored directly to the instance variable rather than via the property if you'd ensured you had an owning reference manually.
As suggested above, use of ARC is a way to get the compiler to figure all this stuff out for you.
That issue is what causes your code to crash — you end up trying to access a reference that has ceased to be valid. If you'd taken ownership of it then it would have continued to exist at least as long as you kept ownership.
try using self.name
sometimes this stuff confuses me as well and for that you might want to consider using arc in which case most of this stuff can be avoided.
when using properties you should always use self.propertyName vs propertyName (only), it uses the accessors (get propertyName, set propertyName) as opposed to directly accessing that pointers value.
take in mind there are 2 exceptions to the rule, init and dealloc which should NOT use self.
self.name = [NSString stringWithString:name];
you technically should also have an init method
to initialize your variables, and i believe you should call [super dealloc] last not first in your dealloc method, but thats not your problem and might not matter (just what I do when I dont use arc)
When you change your instance variable in changeLabel, you should release the previous value and retain the new one. You may use the accessors to perform the memory management stuff for you. Also, I think you should invoke [super dealloc] after releasing the instance variables in your implementation of dealloc.
If you're not familiar with Cocoa memory management (and even if you are), the best is to enable ARC (Automatic Reference Counting) and let the compiler deal with it.
My app runs well until I stop it and restart - whereupon the archive file - highScores.archive is present. Then, the app balks at encoding - I get a EXC_BAD_ACCESS at the first line (for a long time, it didn't happen until I got to the date object I was encoding.
My guess is that I need to put in a retain in a couple of places, but I don't know where.
The code:
FlipHighScores.h
...
#interface FlipHighScores : NSObject <NSCoding> {
//NSString *themeChosen;
NSInteger newHighScore;
NSInteger newScoreStartLevel;
NSInteger newScoreFinishLevel;
NSDate *scoreDateCreated;}
#property (copy, nonatomic) NSString *themeChosen;
#property (nonatomic) NSInteger highScore;
#property (nonatomic) NSInteger scoreStartLevel;
#property (nonatomic) NSInteger scoreFinishLevel;
#property (nonatomic, readonly, strong) NSDate *scoreDateCreated;
...
FlipHighScores.m
...
#synthesize themeChosen = _themeChosen;
#synthesize highScore = _highScore;
#synthesize scoreStartLevel = _scoreStartLevel;
#synthesize scoreFinishLevel = _scoreFinishLevel;
#synthesize scoreDateCreated = _scoreDateCreated;
...
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_themeChosen forKey:#"_themeChosen"];
NSLog(#"Theme Chosen is %#", _themeChosen);
[aCoder encodeInt:_highScore forKey:#"_highScore"];
[aCoder encodeInt:_scoreStartLevel forKey:#"_scoreStartLevel"];
[aCoder encodeInt:_scoreFinishLevel forKey:#"_scoreFinishLevel"];
NSLog(#"Date Created in encodeWithCoder is %#", _scoreDateCreated);
[aCoder encodeObject:_scoreDateCreated forKey:#"_scoreDateCreated"];}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self) {
_themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"];
_highScore = [aDecoder decodeIntForKey:#"_highScore"];
_scoreStartLevel = [aDecoder decodeIntForKey:#"_scoreStartLevel"];
_scoreFinishLevel = [aDecoder decodeIntForKey:#"_scoreFinishLevel"];
_scoreDateCreated = [aDecoder decodeObjectForKey:#"_scoreDateCreated"];
}
return self;}
-(NSString *)description {
NSDate *date = _scoreDateCreated;
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
//NSLog(#"dateString from description is %#", dateString);
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%d %# S:%d F:%d D:%#", _highScore, _themeChosen, _scoreStartLevel, _scoreFinishLevel, dateString];
return descriptionString:}
What I find confusing is that if I delete the save file - highScores.archive and run the app, it runs without problem. I stop and kill the app and then start it again - the first time encoding is called it crashes.
At the line where I encode the themeChosen object. I have read a few posts about decoding issues being fixed with a "retain" or changing to . format (why that would help, I don't really understand). But this is encoding. Decoding will probably be the next question...
I am not using ARC on this project. Maybe when I rebuild the whole thing from scratch...
Oh, I forgot to mention that everything was running smoothly as far as I had tested until I added in tracking the Theme variable. Then things went a smidge awry as mentioned here.
I think your problem is in your -initWithCoder. You're taking the result of -decodeObjectForKey: and directly assigning it to the synthesized ivar. Methods that don't have the word "copy" in their names are generally assumed to return autoreleased objects.
If you directly assign an autoreleased object to a variable, that object will be released in the next run loop, will dealloc itself, and now your variable points to junk memory. When you try to access it, you'll get an exec_bad_access.
What you should be doing is taking advantage of the accessor methods that #synthesize creates for you. Instead of
_themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"];
you should write
[self setThemeChosen:[aDecoder decodeObjectForKey:#"_themeChosen"]];
or, if you positively must use an equal sign, you could use the syntactic sugar of "dot notation":
self.themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"]
Which will ultimately be translated into close to the same thing.
The key is: the synthesized setter does more than simply assign the object to the ivar. It also retains the object (actually, in this case, it copies the object because you specified copy in your #property declaration). This is only one of the many, many, many reasons you should never access ivars directly and always use accessors -- especially now that they're essentially written for you automagically by #property/#synthesize.
UPDATE:
You're going to find you have trouble with scoreDateCreated seeing as you declared it as being readonly in its #property declaration. Why's it read only? It doesn't appear to be a derived value, so you're clearly going to have to assign something to it.
If you want it read/write in your object, but only want to expose a read only interface, you can redeclare the #property as read/write in an anonymous category at the top of FlipHighScores.m. So it looks read only to anything that includes the header, but is actually read/write inside your object's implementation.