I'm new to this objective-c property attribute thing. Here's the code:
student.h
#import <Foundation/NSObject.h>
#interface Student: NSObject
#property int age(assign, readwrite); //I don't know what attribute it should have
#property NSString *name(retain, readwrite); //this one either
-(Student*) initWithName: (NSString *) n andAge:(int) a;
-(void) speakName;
+(void) smoke:(NSString*) thing;
#end
student.m
#import "student.h"
#implementation Student
-(Student*) initWithName:(NSString*) n andAge:(int) a {
self = [super init];
if(self) {
[self setName: n];
[self setAge: a];
}
return self;
}
-(void) speakName {
NSLog(#"MyName is %#", _name);
}
+(void) smoke:(NSString*) thing {
NSLog(#"Smoking %#", thing);
}
#end
main.m
#import <Foundation/Foundation.h>
#import "student.h"
int main(void) {
Student *student = [[Student alloc] initWithName: #"Markson" andAge: 29];
[student speakName];
[Student smoke:#"weed"];
[student release];
return 0;
}
for the the line
#property int age(assign, readwrite); //I don't know what attribute it should have
#property NSString *name(retain, readwrite); //this one either
when I don't give nothing to them, the compiler give me 4 warnings:
In file included from student.m:1:
./student.h:5:1: warning: no 'assign', 'retain', or 'copy' attribute is
specified - 'assign' is assumed [-Wobjc-property-no-attribute]
#property NSString *name;
^
./student.h:5:1: warning: default property attribute 'assign' not appropriate
for non-GC object [-Wobjc-property-no-attribute]
2 warnings generated.
In file included from main.m:2:
./student.h:5:1: warning: no 'assign', 'retain', or 'copy' attribute is
specified - 'assign' is assumed [-Wobjc-property-no-attribute]
#property NSString *name;
^
./student.h:5:1: warning: default property attribute 'assign' not appropriate
for non-GC object [-Wobjc-property-no-attribute]
2 warnings generated.
Please anyone can tell what attributes should be used for age and name properties?
#property int age(assign, readwrite); //I don't know what attribute it should have
#property NSString *name(retain, readwrite); //this one either
Short Note:
If you are creating property for primitive types like int, float, BOOL etc then use assign
If you want to retain the value then use strong or retain.
In all other situations you can go with weak.
And always go with atomic, and the syntax is like this
#property (<assign/retain/weak>, <atomic/nonatomic>, <read/readwrite>) <dataType> <propertyName>;
Change those lines to this:
#property (assign, nonatomic) int age;
#property (retain, nonatomic) NSString *name;
readwrite is the default one, so you don't need to set it.
Check this documentation:
Encapsulating data in Objective-C
Related
I have a resource which is fetched from a JSON API.
The JSON is parsed into a NSDictionary which, in this case is called game.
I'm creating a new instance of my Game class based on the attributes from the JSON.
Game class has a property called userRegistered which is defined as follows:
// in Game.h
#interface
#property (nonatomic, assign) BOOL userRegistered;
// elsewhere in my code I have
Game *newGame = [[Game alloc] init];
newGame.userRegistered = ([game objectForKey:#"user_registered"] > 0);
The "user_registered" key in the dictionary will always be either 1 or 0.
Xcode warns me the I have -
warning: Semantic Issue: Incompatible integer to pointer conversion passing 'int' to parameter of type 'BOOL *' (aka 'signed char *')
Can someone please explain the issue and how I might resolve it?
Update
My full game class is defined as follows:
#import <Foundation/Foundation.h>
#interface Game : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *photoURL;
#property (nonatomic, copy) NSString *gameURL;
#property (nonatomic, assign) BOOL *userRegistered;
#end
// Game.m
#import "Game.h"
#implementation Game
#synthesize name = _name;
#synthesize partnerName = _partnerName;
#synthesize photoURL = _photoURL;
#synthesize gameURL = _gameURL;
#synthesize userRegistered = _userRegistered;
#end
I'm getting the error in one of my ViewControllers in this method
// api_response.body has just been set to an __NSCFArray containing
// NSDictionaries by AFNetworking
NSDictionary *game;
Game *newGame;
for (game in api_response.body){
newGame = [[Game alloc] init];
NSLog(#"Creating a new game");
// set attributes for new game instance
newGame.name = [game objectForKey:#"name"];
newGame.photoURL = [game objectForKey:#"photoURL"];
// user registered is either 0 (false) or 1 (true)
newGame.userRegistered = [[game objectForKey:#"user_registered"] intValue];
// add the game instance to the appropriate array
[self addGameToGamesArray:newGame];
newGame = nil;
}
The warning shows over newGame.userRegistered = [[game objectForKey:#"user_registered"] intValue];
[game objectForKey:#"user_registered"] is likely giving you an NSNumber object. You probably mean instead to compare the integer value inside that NSNumber object.
([[game objectForKey:#"user_registered"] intValue] > 0)
UPDATE in response to your update:
Your problem is with how you're declaring your BOOL property - you have a * that you need to remove.
#property (nonatomic, assign) BOOL *userRegistered;
should be
#property (nonatomic, assign) BOOL userRegistered;
I was able to solve this issue by simply using boolValue
game.userRegistered = [[json objectForKey:#"user_registered"] boolValue];
Thanks all for the help
objectForKey function will return an objective-c instance.
([[game objectForKey:#"user_registered"] boolValue] > 0)
([game boolForKey:#"user_registered"]==YES)
Why isn't a setter synthesized for myString in the example below? The basic assignment below results in myString being nil. Trying to use the setter [self setMyString:s]; results in an unrecognized selector exception.
// in .h file
#interface MyClass
#property (nonatomic, readonly) NSString *myString;
#end
// in .m file
#interface MyClass (MyCategory)
#property (nonatomic, copy) NSString *myString;
#end
#implementation MyClass
#synthensize myString;
- (void) someMethod:(NSString *) s {
myString = [s copy];
// why is myString nil here?
}
#end
Edit: the problem was with gdb. po myString printed Can't print description of a NIL object.. However NSLog(#"myString: %#", myString); printed the expected value.
The other two answers are correct, but I think they miss your intention. It's common to declare a property as read-only in the .h file, so that code outside the class implementation can't write it. Inside the .m file, you want it to be readwrite. This kind of redefinition is explicitly supported. However, you need to put the redeclaration as readwrite in a class-extension:
// In your .h file:
#interface MyClass : NSObject
#property (nonatomic, copy, readonly) NSString *myString;
#end
// In your .m file:
#interface MyClass () // Note the empty parentheses
#property (nonatomic, copy, readwrite) NSString *myString;
#end
You do still need to use self.myString = aString or [self setMyString:aString], instead of writing to the ivar directly as you're doing right now.
It looks like you're trying to declare a publicly readonly, privately writable property. You should do that in a class extension rather than a category. Syntactically, a class extension looks like a category with no name:
#interface MyClass ()
#property (nonatomic, copy) NSString *myString;
#end
Declaring a property with the same name in both your MyClass interface and your MyCategory category seems like a bad idea. Remove the declaration in the category and I expect all will be well.
I have seen a lot of code like this
header.h
#interface Foo : NSObject
{
NSString *str;
}
#property(nonatomic, retain) NSString *str;
#end
and then in implementation
#implementation Foo
#synthesize str = _str;
#end
I can't understand what is the benefit of using such assignment ?
#synthesize str = _str;
It is just a common naming convention.
It is so that in your implementation, you can distinguish accessing a variable directly against accessing via the property accessor.
If you try and access str in your code, like [str length], the code won't compile. You either need to do [self.str length] or [_str length].
... and as it's an NSString immutable property, use copy, not retain.
#synthesize str = _str; will mean that the instance variable that is synthesised for the str property is called _str. In your code you therefore have a mismatch between it and the declared instance variable. So you'll actually end up with 2 instance variables, one called str and one called _str.
You want to do this:
#interface Foo : NSObject
#property(nonatomic, retain) NSString *str;
#end
#implementation Foo
#synthesize str = _str;
#end
Or this:
#interface Foo : NSObject {
NSString *str;
}
#property(nonatomic, retain) NSString *str;
#end
#implementation Foo
#synthesize str;
#end
Or obviously rename the declared instance variable, _str.
There's lots of questions on SO already about whether or not to prefix with _ such as - Prefixing property names with an underscore in Objective C .
Here's a quick example of how it can be useful to use the name change:
#interface MyClass : NSObject {
NSString *myString;
}
#property (nonatomic, retain) NSString *myString;
- (NSString *)stringTreatment:(NSString *)str;
#end
#implementation MyClass
#synthesize str = _str;
- (id)init {
self = [super init];
if (self) {
self.str = [NSString string];
}
return self;
}
- (NSString *)stringTreatment:(NSString *)str {
return [str uppercaseString];
}
#end
If you wouldn't have synthesized str as _str, you would get a warning in that stringTreatment method saying that the local declaration of str hides the instance variable.
Also, in your code, you could be assigning a value to _str and have an external class call [MyClass str] and it would return it.
So, to make a long story short, "str" remains the name of your property and "_str" is the internal reference to that property. For example, you won't be able to use [MyClass _str], that won't work. Makes sense?
Hope this helps.
The code is probably the best way to see what I am trying to do:
AcInfo.h:
#interface AcInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString *registrationNumber;
#end
AcInfo.m:
#implementation AcInfo
#dynamic registrationNumber;
#end
AnotherClass.h:
#interface AnotherClass : NSObject {
}
#property (nonatomic, retain) AcInfo *detailItem;
#property (nonatomic, retain) IBOutlet UITextField *registrationNumberTextField;
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget;
#end
AnotherClass.m:
#import "AcInfo.h"
#implementation AnotherClass
#synthesize detailItem, registrationNumberTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
registrationNumberTextField.text = #"Test";
// I expect this to set detailItem.registrationNumber to the value of
// registrationNumberTextField.text (Test) but it doesn't change anything!
setDetailItemValueFromUIElement:registrationNumberTextField forAcInfoTarget:detailItem.registrationNumber;
}
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget
{
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
// This doesn't do anything when it returns!
(NSString *)acInfoTarget = (UITextField *)uiElement.text
return;
}
}
#end
In short, I want acInfoTarget to call the getter [detailObject registrationNumber] and the setter [detailObject setRegistrationNumber] in the setDetailItemValueFromUIElement: function...
You can set or read properties by name using
// setter
NSString *propertyName = #"myProperty";
[object setValue:newValue forKey:propertyName];
// getter
id value = [object valueForKey:propertyName];
This is slower than using the normal dot notation, though, and it's frequently (though not always) a sign of poorly-designed code.
Also note that id is a pointer type, so you probably don't actually mean "(id*)".
Your code wants to look something like this, I think:
- (void)setDetailItemValueFromUIElement:(id)uiElement forAcInfoTarget:(NSString*)acInfoTarget {
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
NSString *newValue = ((UITextField*)uiElement).text;
[self.detailItem setValue:newValue forKey:acInfoTarget];
}
}
Properties are just syntax sugar for a couple of accessor methods. They are not, in essence, variables so you shouldn't treat them as such. If you want to affect a property, then what you wanting to do is call a method. So you should pass a id and selector parameter and not pointer to a variable type.
I keep reading that dot syntax is possible but I keep getting errors that the struct does not contain members I am referencing. Perhaps its not the dot syntax so I have included details of what I am doing in hopes of a solution:
// MobRec.h - used as the objects in the MobInfo array
#import <Foundation/Foundation.h>
#interface MobRec : NSObject {
#public NSString *mName;
#public int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
// MobDefs.h - array of MobRecords
#interface Mobdefs : NSObject {
#public NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo; // is this the right property?
-(void) initMobTable;
#end
// MobDefs.m
#import "Mobdefs.h"
#import "Mobrec.h"
#implementation Mobdefs
#synthesize mobInfo;
-(void) initMobTable
{
// if I use traditional method I get may not respond
[mobInfo objectAtIndex:0 setmName: #"doug"];
// if I use dot syntax I get struct has no member named mName
mobInfo[1].MName = #"eric";
}
// main.h
MobDefs *mobdef;
// main.m
mobdef = [[Mobdefs alloc] init];
[mobdef initMobTable];
although both methods should work I get erros on both. What am I doing wrong? My best thoughts have been that I am using the wrong #property but I think I have tried all. I am performing alloc in main. Ideally I would like to for this use dot syntax and cant see why its not allowing it.
A couple of things: (edit: original point #1 removed due to error)
Although the dot syntax is supported, the array index syntax for NSArray is not. Thus, your call to mobInfo[1] will not be the same as [mobInfo objectAtIndex:1]; Instead, mobInfo will be treated as a simple C-style array, and that call would be almost guaranteed to result in a crash.
You should not define variables in your header file as you do in main.h. The line MobDefs *mobdef; belongs somewhere in main.m.
edit: Here is how it should look:
MobRec.h
#interface MobRec : NSObject {
NSString *mName;
int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
MobRec.m
#implementation MobRec
#synthesize mName;
#synthesize mSpeed;
#end
MobDefs.h
#interface MobDefs : NSObject {
NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo;
-(void) initMobTable;
#end
MobDefs.m
#import "MobDefs.h"
#import "MobRec.h"
#implementation MobDefs
#synthesize mobInfo;
-(void) initMobTable
{
// option 1:
[(MobRec*)[mobInfo objectAtIndex:0] setMName:#"doug"];
// option 2:
(MobRec*)[mobInfo objectAtIndex:0].mName = #"eric";
// option 3:
MobRec *mobRec = [mobInfo objectAtIndex:0];
mobRec.mName = #"eric";
}
main.m
MobDef *mobdef = [[MobDefs alloc] init];
[mobdef initMobTable];
...
[mobdef release]; // don't forget!
You need to either cast the object returned by -objectAtIndex:, or use a method call on it:
[[mobInfo objectAtIndex: 0] setMName: #"doug"];
or
((Mobrec *) [mobInfo objectAtIndex: 0]).MName = #"doug";
[mobInfo objectAtIndex:0 setmName: #"doug"];
There is no objectAtIndex:setmName method, so you're going to have to explain what you think this is even supposed to do.
mobInfo[1].MName = #"eric";
Use objectAtIndex to look something up in an NSArray object.