Setting and using ivars or passed parameters in setter functions - objective-c

I know this sounds like a really silly and stupid question but is there any difference in using the target ivars or the passed parameters when making extended setter functions like in:
- (void)setText:(NSString)text {
_text = text;
self.label.text = text; // VS
self.label.text = _text;
}
Is there any functionality or efficiency difference between the two assignments for self.label.text?

No, there is no difference between using the ivar and the passed parameter in your example.
However, there is a difference if you use the accessor:
// These are the same
self.label.text = text;
self.label.text = _text;
// This will call the getter
self.label.text = self.text
As an illustration of the difference, consider if we had a custom (silly) getter:
- (NSString *)text
{
return #"Hello";
}
In this case, the literal string #"Hello" would be returned regardless of the value of text.

Related

set ivars from NSDictionnary

I'm currently working on a project where the user defines some parameters in a NSDictionnary, that I'm using to setup some objects.
For example, you can ask to create a Sound object with parameters param1=xxx, param2=yyy, gain=3.5 ... Then an Enemi object with parameters speed=10, active=YES, name=zzz ...
{
active = NO;
looping = YES;
soundList = "FINAL_PSS_imoverhere_all";
speed = 100.0;
}
I then instantiate my classes, and would like to set the ivars automatically from this dictionnary.
I've actually wrote some code to check that this parameter exists, but I'm having trouble in actually setting the parameter value, especially when the parameter is non object (float or bool).
Here's what I'm doing so far :
//aKey is the name of the ivar
for (NSString *aKey in [properties allKeys]){
//create the name of the setter function from the key (parameter -> setParameter)
NSString *setterName = [aKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[aKey substringToIndex:1] uppercaseString]];
setterName = [NSString stringWithFormat:#"set%#:",setterName];
SEL setterSelector = NSSelectorFromString(setterName);
//Check if the parameter exists
if ([pge_object respondsToSelector:setterSelector]){
//TODO : automatically set the parameter
}
else{
[[PSMessagesChecker sharedInstance]logMessage:[NSString stringWithFormat:#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]] inColor:#"red"];
NSLog(#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]);
}
}
}
As you can see, I don't know what to do once I've found that the parameter exists on the object. I tried to use "performSelector... withObject..., but my problem is that some of the parameters are non-objects (float or bool).
I also tried to get the class of the parameter, by using the setter, but it didn't help.
Did anyone manage to do something like that?
Jack Lawrence's comment is spot on.
What you are looking for is called Key Value Coding, or just KVC.
This fundamental part of Cocoa lets you get and set any instance variable using its name as a String and a new value.
It will automatically handle coercing Objects to primitive values, so you can use it for int and float properties too.
There is also support for validating values and handling unknown properties.
see the docs
your code, without validation, could be written
for( id eachKey in props ) {
[anOb setValue:props[eachKey] forKey:eachKey];
}
or just
[anOb setValuesForKeysWithDictionary:props];
as Jack said.
For the non-object parameters you have to put them into an object, for example NSNumber or NSValue. You can then add these objects into your dictionary.
For Example:
float f = 0.5;
NSNumber f_obj = [NSNumber numberWithFloat:f];

Objective-C accessing float getters with variable names

Let's say I have an NSArray called myArray of NSStrings (#"a0",#"a1",#"a2")
Then in a fast enumeration I loop into my array to build properties according to that NSStrings. I've got a problem accessing that properties.
I'm trying something like that :
#property (nonatomic) float a0propertyLow;
#property (nonatomic) float a0propertyHigh;
#property (nonatomic) float a1propertyLow;
#property (nonatomic) float a1propertyHigh;
..
.. etc.
for (NSString *aPos in myArray) {
NSString *low = [NSString stringWithFormat:#"%#propertyLow",aPos];
NSString *high = [NSString stringWithFormat:#"%#propertyHigh",aPos];
SEL lowSel = NSSelectorFromString(low);
SEL highSel = NSSelectorFromString(high);
if ([self respondsToSelector:lowSel]&&[self respondsToSelector:highSel]) {
id sumPartOne = [self performSelector:lowSel];
id sumPartTwo = [self performSelector:highSel];
float bla = (float)sumPartOne + (float)sumPartTwo;
}
}
I know my code is wrong but I don't know how to make it work.
My problem is that lowSel and highSel are getters which returns float but the perform selector method returns id which is ok for an object but not for floats.
So, how can I access my float getters with variable names ? I'm sure answer must be simple but it seems that my mind is looking for something complicated (and which obviously doesn't work) so I'm asking for help :)
Thank you very much for your help
You can't use performSelector: to call a method that returns a scalar value. The documentation for performSelector: clearly says what you have to do:
For methods that return anything other than an object, use NSInvocation.
An NSInvocation is a little more complex to set up but more flexible regarding arguments and return types.
In your case, it is probably easier to use Key-Value Coding instead:
[self valueForKey:low];
takes the return type into account and will automatically wrap the float in an NSNumber.
If you really need to use these getter methods, you can change your properties to double and use objc_msgSend_fpret():
#include <objc/runtime.h>
#include <objc/message.h>
double arg0 = objc_msgSend_fpret(self, lowSel);
If you can avoid getters (I know, that's not good practice, but anyway, it works for sure with the following method), and use the instance variables directly:
void *object_getIvarPtr(id obj, const char *name)
{
if (!obj || !name)
{
return NULL;
}
Ivar ivar = object_getInstanceVariable(obj, name, NULL);
if (!ivar)
{
return NULL;
}
return ((char *)obj + ivar_getOffset(ivar));
}
float arg0 = *(float *)object_getIvarPtr(self, [lowSel UTF8String]);
Hope this helps.
One way you can do is convert your floats into objects at runtime such as:-
NSString *str=[NSSTring stringWithFormat:#"%f",yourFloatValue];
and then u can retrive it using
[str floatValue];

NSString to class instance variable

I am looking for a way to convert from NSString to a class instance variable. For sample code below, say filter is "colorFilter". I want filternameclassinstancegohere to be replaced with colorFilter.
- (void)filterSelected:(NSString *)filter
{
self.filternameclassinstancegohere = ….;
}
While there were good suggested solutions given for this question, I discovered what I needed is the NSClassFromString method. Here is a final implementation:
- (void)filterSelected:(NSString *)filter
{
//self.filternameclassinstancegohere = ….;
self.myViewController = [[NSClassFromString(filter) alloc] initWithNibName:filter bundle:nil];
}
Consider using one NSMutableDictionary instance variable with string keys rather than 40 instance variables.
You can create an arbitrary selector using NSSelectorFromString():
SEL methodName = NSSelectorFromString(filter);
[self performSelector:methodName];
This will call a method colorFilter in your example above.
Would be wise to check with respondsToSelector before calling, too.
If the filter value can only be a small, constant number of things, just use an enumeration and a switch statement:
enum Filter
{
ColorFilter,
FooFilter,
BarFilter
};
- (void)filterSelected:(Filter)filter
{
switch(filter)
{
case ColorFilter:
self.colorFilter = ...;
break;
case FooFilter:
self.fooFilter = ...;
break;
case BarFilter:
self.barFilter = ...;
break;
}
}
If the set of filter values is large and could change frequently, then you could also use Key-Value Coding. It's more complicated but more flexible.

Objective C Function Question

Hey guys, check this out. I have a function that treats for me a string. No matter what it does, i just want to knwo if is possible to this function return the result for the place that it was executed. I mean, check this:
[self priceFormat:#"1"];
priceLabel.text = price;
-(void) priceFormat:(NSString*)price {
price = #"2";
}
I just want to my function treats the string and return it to the same place that it was executed.
Thanks!
Three ways to do this
Way one, using a pointer
- (void)priceFormat:(NSString **)price {
*price = #"2";
}
Wat two, using an instance variable
What you might want instead is an ivar. In the interface (most often the h file) of your class:
NSString *price;
and in the implementation (the m or mm file):
- (void)priceFormat:(NSString *)price {
price = #"2";
}
I have created an example of this here.
If you want the price to be available to other objects as well (not just self), you might want to create a property for it and synthesize it. Then use self.price = #"2"; instead. More on this here: http://MacDeveloperTips.com/objective-c/objective-c-properties-setters-and-dot-syntax.html
Just make sure you make it a copy property (NSString in use)!
Way three using return
Note, that you can also return directly from a method:
- (NSString *)priceFormat:(NSString *)price {
return #"2";
}
priceLabel.text = [self priceFormat:#"1"];

why does my objective c method give me a "error: can not use an object as parameter to a method"

I've looked at this over and over again and I can't see the problem. Its probably obvious and I'm probably being an idiot and I apologize in advance for this.
In my interface I have:
#interface PolygonShape : NSObject
{
int numberOfSides;
int minimumNumberOfSides;
int maximumNumberOfSides;
}
#property int numberOfSides, minimumNumberOfSides, maximumNumberOfSides;
// class methods
+ (float)getAngleInDegrees:(PolygonShape *) polyshape;
+ (float)getAngleInRadians:(PolygonShape *) polyshape;
+ (NSString)getName:(PolygonShape *) polyshape;
//instance methods
- (id)init;
- (id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min
maximumNumberOfSides:(int)max;
#end
The part in the implementation that I get errors is for the getName method:
#implentation...
+ (NSString)getName:(PolygonShape *) polyshape
{
// here is where I get the "error: can not use an object as parameter to a method"
int sides = [polyshape numberOfSides];
NSString * s = [NSString init];
switch (sides) {
case 3:
s = "#Triangle";
// there's also an "assignment from incompatible pointer type" warning...but its secondary
break;
case 4:
return "#Square";
break;
default:
break;
}
}
The thing that drives me batty is that the class methods works just fine:
+ (float)getAngleInDegrees:(PolygonShape *) polyshape;
+ (float)getAngleInRadians:(PolygonShape *) polyshape;
Your getName: method should return (NSString *), not (NSString). I assume this is the error; if so, then yes, the error message could definitely have been more informative.
In fact, in Objective-C you will never see objects getting passed around without their * behind them, not as return values, not as parameters, not as local variables and not as member variables.
BTW, the warning you mention is because you have a typo, mixing up "#foo" with #"foo". The latter is an Objectice-C string literal, the former is a C string literal whose first character just happens to be #.
In addition to the other answers, you're using [NSString init] where you should be using [[NSString alloc] init]. However, that will only allocate an empty string, so you'd probably be better off initializing s to either #"" or nil.
I think the error is slightly misleading in this case. In Objective-C, it's generally not possible to pass an object by value (including return values). In this case, you declare the return value as NSString rather than NSString*. The declaration should be:
+ (NSString*)getName:(PolygonShape *) polyshape
not
+ (NSString)getName:(PolygonShape *) polyshape