How can i get the property of NSObject using NSString [duplicate] - iphone

This question already has an answer here:
Objective-C get a class property from string
(1 answer)
Closed 9 years ago.
I'm developing for the iPhone. Is there any way to get a property using an NSString holding the property name? something like:
#interface MyLovelyClass : NSObject
#property (nonatomic) double test;
-(double) returnDoubleProperty:(NSString *) propertyName;
and then to call it
MyLovelyClass *mlc=[[MyLovelyClass alloc] init];
double value=[mlc returnDoubleProperty:#"test"];
I understand that if i have a limited number of properties, i can manually write if else statements that would return values for each input string. However, is there any way to achieve this programmaticaly?

You want to use KVC (key value coding) which does exactly what you describe, but you don't need to implement any methods for it to work. It does work with objects though so your double would be wrapped inside an NSNumber:
#property (nonatomic, strong) NSNumber *test;
NSNumber *value = [mlc valueForKey:#"test"];

Since properties are methods, you can call them through performSelector:. To make a selector from string use NSSelectorFromString. The double will be wrapped in an id object of type NSNumber, so you need to pull it back by calling doubleValue:
SEL toCall = NSSelectorFromString(#"test");
double value=[[mlc performSelector:toCall] doubleValue];

Related

Objective C: Which is changed, property or ivar?

Worrying about duplicates but can not seem to find and answer I can understand in any of the other posts, I just have to ask:
When I have in my .h:
#interface SecondViewController : UIViewController{
NSString *changeName;
}
#property (readwrite, retain) NSString *changeName;
then in my .m
#synthesize changeName;
-(IBAction)changeButton:(id)sender{
changeName = #"changed";
}
Is it the synthesized property or the instance variable that get changed when I press "changeButton" ?
You (and it seems some of the others that answered) are confusing properties with actual variables.
The way properties work is, they create METHODS (called setter and getter) that set or get/return ivars. And the do notation (self.string) actually INVOKES these methods. So a property can't be CHANGED, only the declared iVar is.
When you declare a property like so:
#property (nonatomic, retain) NSString *string;
And #synthesize it the following happens:
An iVar called string (of type NString*) is created
(if you do
#synthesize string = whateverYouWant
the iVar created
is called whateverYouWant - a convention is to name the iVars
the same as the property with preceding underscore (_string))
an accessor method is created like this
-(NSString*) string;
a setter is created like this
-(void) setString: (NSString*) newString;
Now what self.xxxx does is, it actually sends the message xxxx to self
(like [self xxxx]).
It works with ANY method, not just properties, though it should only
Be used with properties.
So when you do self.string = #"hello" it actually comes down to
[self setString: #"hello"];
(Note that the compiler actually knows you are trying to set and so the
setString message is sent instead of just string. If you accessed self.string
it would send [self string])
Thus you don't SET a property, you invoke the (synthesized) setter method that in
itself sets the iVar.
Accessing your iVar directly is ok, if you know what your doing.
Just calling
string = #"something else";
Will produce leaking code, since no memory management is done.
The synthesized accessors and setters actually do this for you, depending
on how you defined th property (retain,copy,assign).
Because the setter (for a retained property) doesn't just do
IVar = newValue
If you declared a retained property it actually looks something like this:
-(void) setString: (NSString*) newString {
if (string) [string release];
string = [newString retain];
}
So the property synthesize takes a bit of work off your hands.
EDIT
Since it still doesn't seem clear, the property that is declared is not to be thought
of like a variable. In the above example, when using
#synthesize string = _string;
there IS NO variable called "string". It's just the way you access the method structures
that set the iVar _string through the setter methods. Since string is no variable/object pointer, you cannot send messages to it ([string doSomething] won't work).
When you just synthesize the property using #synthesize string; the generated iVar gets
the same name as the property.
Calling [string doSomething] will then work, but it has nothing to do with the property. The "string" refers to the iVar. Hence th convention to name the iVars underscored, so
you don't accidentally access the iVar when you meant to use the getter/setter.
Both. Property uses instance variable as its storage. In your code you change the instance variable, but if you access the property (via self.changeName) you'd get the same value as instance variable.
Usually to distinguish between ivars and properties people use _ prefix for ivars. And then synthesizes properties like this:
#synthesize myProperty=_myProperty;
well, the var
it's always the var
in your case the property methods aren't used at all.
now, consider this case:
self.changeName = #"changed";
this way you are using the property, but that just means that you are using the methods "magically" created for you by the compiler, the setter and getter methods, where you, again, change the var (property doesn't exist, in reality, it's just a way to create the setter and getter methods for you)

iPhone Core Data crash with ARC

This is my first time trying to use both ARC and Core Data. I can't seem to figure out why my code is crashing.
in the .h I have:
#interface Foo : NSObject {
}
#property (nonatomic,strong) NSString *name;
#property (nonatomic,strong) NSString *email;
#property (nonatomic) BOOL myBool;
#property (nonatomic) float myFloat;
in the .m
#implementation User
#dynamic name;
#dynamic email;
#dynamic myBool;
#dynamic myFloat;
User *user = (User *)[NSEntityDescription insertNewObjectForEntityForName:#"User" inManagedObjectContext:[appDelegate managedObjectContext]];
[user setName:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"name"]];
[user setEmail:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"email"]];
[user setAuthorisation_token:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"authentication_token"]];
[user setMyFloat:5]; <------ crash when set to anything other than 0 (eg,setting to FALSE will crash it).
[user setMyBool:FALSE]; <---- crash when set this to anything other than 0.
Basically whenever I try to use a type other than a string I am getting EXEC crash on that particular line. When I use strings for everything it is fine. In my.xcdatamodeld file I have myFloat set to FLOAT and myBool set to BOOLEAN
kill
error while killing target (killing anyway): warning: error on line 2184 of "/SourceCache/gdb/gdb-1708/src/gdb/macosx/macosx-nat-inferior.c" in function "void macosx_kill_inferior_safe()": (os/kern) failure (0x5x)
quit
Program ended with exit code: 0
You can't use #dynamic for primitives (like float and BOOL) because Core Data won't create implementations for them.
So the reason why your code is crashing is because when you use #dynamic you are telling the compiler "I promise that an implementation for these getters and setters will be available at runtime". But since Core Data doesn't create them then your code tries to call methods that doesn't exist.
Instead there are two things you could do: Use an NSNumber for both the BOOL and the float or implement your own getters and setters.
Using NSNumber:
Core Data only uses objects and not primitives but you can specify boolean or float in the Model. When you call [user myFloat] you will actually get an NSNumber back with the float value inside it. To access the primitive you then call float f = [[user myFloat] floatValue];. The same thing goes for the boolean, it also gets stored in an NSNumber. So when you try to access it you will get back an NSNumber that you need to call BOOL b = [[user isMyBool] boolValue]; to get the primitive back.
The same thing goes the other way around, when setting myFloat and myBool, you need to store them inside an NSNumber, e.g. [user setMyFloat:[NSNumber numberWithFloat:f]]; and [user setMyBool:[NSNumber numberWithBool:b]];.
To use this approach you would have to change your last two properties to
#property (nonatomic, strong) NSNumber *myBool;
#property (nonatomic, strong) NSNubmer *myFloat;
but you can keep the #dynamic for both of them.
Implementing you own getters and setters:
For your convenience, you may want your "user" object to get and set the primitive types, float and BOOL, directly. In that case you should keep the properties as float and bool and in your implementation file (.m) remove the #dynamic lines for myFloat and myBool.
To implement the getter and setter you need to know a little about KVC/KVO and Core Data. In short: you need to tell the system when you are about to access or change a property and when yo u are done accessing or changing it, since Core Data won't do it for you. Between the "will access/change" and "did access/change" you are free to retrieve or modify the properties. One more caveat is that Core Data still cannot save the BOOL and float directly, so they need to be packaged into and unpackaged from NSNumbers when getting and setting.
Further, you can't call [self setValue:ForKey:]; or [self valueForKey:#""]; because that would cause the method you are in to call itself and throw you into an infinite loop. Core Data solves this use-case by allowing you to get and set the value without hitting your own implementation by calling [self setPrimitiveValue:ForKey:] and [self primiveValueForKey:]. Note: primiteValueForKey has nothing to do with primitive types (int, float, BOOL) but is just the name of the methods you use to get and set values in Core Data directly.
The implementation for your float and BOOL would look something like this:
- (float)myFloat
{
[self willAccessValueForKey:#"myFloat"];
float f = [[self primitiveValueForKey:#"myFloat"] floatValue];
[self didAccessValueForKey:#"myFloat"];
return f;
}
- (void)setMyFloat:(float)f
{
[self willChangeValueForKey:#"myFloat"];
[[self setPrimitiveValue:[NSNumber numberWithFloat:f] forKey:#"myFloat"];
[self didChangeValueForKey:#"myFloat"];
}
- (BOOL)isMyBool
{
[self willAccessValueForKey:#"myBool"];
BOOL b = [[self primitiveValueForKey:#"myBool"] boolValue];
[self didAccessValueForKey:#"myBool"];
return b;
}
- (void)setMyBool:(BOOL)b
{
[self willChangeValueForKey:#"myBool"];
[[self setPrimitiveValue:[NSNumber numberWithBool:b] forKey:#"myBool"];
[self didChangeValueForKey:#"myBool"];
}

Class not setting as expected

Icon is set as #property (nonatomic, retain) AHGridIcon *icon;
Usually i just do:
-(void)setIcon:(AHGridIcon *)iconLocal {
icon = iconLocal;
}
But i read a guide to getters setters and properties online which has lead me to believe that instead, this is right:
-(void)setIcon:(AHGridIcon *)iconLocal {
if (iconLocal != self.icon)
{
NSLog(#"local: %#", iconLocal);
NSLog(#"self.icon 1: %#", self.icon);
[iconLocal retain];
[icon release];
icon = iconLocal;
NSLog(#"self.icon 2: %#", self.icon);
}
}
The problem is, the original icon is staying put, it's not being replaced with the new icon. What am i doing wrong? Should i just revert to the usual way i do it?
You should use '#synthesize' unless you really need custom setter behavior.
like I posted in my comment:
the best way is to use #synthesize which will create a getter and a setter to with respect to the properties you wrote in your property (nonatomic, retain) => not threadsafe but fast getter and setter and a retaining (and also releasing) setter. If you dont need sophisticating stuff to do in your setter then you should not override the setter.
.h:
#property (nonatomic, retain) AHGridIcon *icon;
.m:
#implementation Something
#synthesize icon;
...
#end
The code you posted in your setter is nearly the same as the compiler would produce when only using synthesize.
Your usual way is not really nice because in your header is defined (in your property) that the setter is retaining but in your implementation you are overriding that correct setter which doesn't retain. It is nearly the same as the compiler would produce with an (nonatomic, assign) property.
But if you want to override your setter then it should look like the same as you wrote. For me it is working fine.
first retaining the new object
then releasing the old one
then assigning the local pointer to your new object
you can even omit your if but then it is really important that you first retain the new and then release the old objects (like you did - just want to mention that).
For solving your problem with an overriten setter: Your setter looks ok in my eyes. Have you also overriten the getter? If yes then post it here (you use it by calling self.icon in your log-call).
I've done a small test-program
#synthesize str;
- (void)setStr:(NSString *)localStr
{
if(str != localStr)
{
NSLog(#"old : %#", self.str);
NSLog(#"new1: %#", localStr);
[localStr retain];
[str release];
str = localStr;
NSLog(#"new2: %#", self.str);
}
}
and the output is fine:
old : (null)
new1: Hello
new2: Hello
old : Hello
new1: World
new2: World

Why aren't my variables persisting between methods? - iPhone

Header file:
NSString *righta;
#property(nonatomic, retain) NSString *righta;
(I don't usually make #property declarations for my variables but thought this might help the variable persist through the class, though it hasn't)
Implementation file:
#synthesize righta;
- (void) function1 {
righta = [NSString stringWithFormat:#"A"];
}
- (IBAction)function2:(id)sender {
NSLog(#"righta is still %#", righta);
}
On trying to access the value of the string in the second function, I receive an "EXC_BAD_ACCESS" and the app crashes.
Any help would be GREATLY appreciated.
Thanks!
stringWithFormat returns an autoreleased object. You must retain it. Note that you are accessing the ivar directy, not the property so the string is not getting retained. Use the property instead:
self.righta = [NSString stringWithFormat:#"A"];
Some programmers prefer to synthesize their properties with a different ivar name to avoid accessing the ivar directly by mistake.
#synthesize righta = righta_;
You must use
self.righta = [NSString stringWithFormat:#"A"];
To assign the variable otherwise the accessor is not used and the value is not retained.
Change function1 to:
self.righta = [NSString stringWithFormat:#"A"];
You were assigning directly to the righta ivar without retaining the string.

NSString not working

In my code I declared NSString object in .h and synthesized in .m , I m assigning it with string object in array . but its not working .
when I print it on Log it display sometime CAlayer Class , some time shows NSCFString class object .some time shows UIDevicewhitedevicetype class how to solve this ? help...
How are you assigning the string to the NSString object in your code?
Are you doing something like:
NSString* someString = #"My string";
self.myStringProperty = someString;
Where myStringProperty is the NSString declared as a property.
I think your memory is ruined. You maybe release something that should not be released.
The reference is definitely bad. You said you are assigning the string to a property. Does that imply that in your header you have something like:
#property (assign) NSString* myString;
If so, that explicitly states that you are not going to hold onto the string reference, allowing it to be dealloced even if you still hold a pointer (not a reference) to it. You should make it say either:
#property (copy) NSString* myString;
#property (retain) NSString* myString;
If you are pointing your string at a value in an array, as soon as that array is released all of its contents are released. If you aren't holding a retained reference to the string it will be deallocated. Once that has happened the pointer you have stored points to undefined memory that is being reused to hold the object types you listed.