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

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

Related

Objective c — Update parameter in block

I was doing some tinkering with tree traversals (which I have solved in a much more straightforward way) but I have come across an issue in the following piece of Objective C logic:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString **) = ^NSString *(int a, NSString **adder){
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
*adder = [*adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", *adder);
return [*adder stringByAppendingString:appendBlock(a-1, adder)];
};
appendBlock(5, &result);
return result;
}
Basically, I want to create a block of code that concatenates numbers into the given string (adder). The result should be: "-5--4--3--2--1-".
I get a segmentation fault with the above code but with some other code that I wrote for the tree traversal, the adder string was essentially not getting updated. Any pointers to what I am doing wrong here? (Is it possible that the variable that is being updated by the inner block (inside recursion) is disallowed as it is already being occupied by the outer block OR is it just that NSString is non-mutable data type?)
In any case, I want to keep the design of the function the same; how would I solve this problem (using c/objective)?
After some searching and experimenting I found a way to fix this.
There is no reason to be using a double-pointer for your adder parameter in the block. Just use a regular pointer and update your code accordingly.
The error is coming from the fact that inside of the block, appendBlock is NULL and you end up dereferencing the NULL pointer trying to call it.
Here's an updated version that works:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString *);
__block __weak NSString *(^weakBlock)(int, NSString *);
weakBlock = appendBlock = ^NSString *(int a, NSString *adder){
NSString *(^innerBlock)(int, NSString *) = weakBlock;
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
adder = [adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", adder);
// Split this update to make it easier to debug.
NSString *update = innerBlock(a-1, adder);
return [adder stringByAppendingString:update];
};
appendBlock(5, result);
return result;
}
Output: "-5--4--3--2--1-"
This update is rewritten for point #1 (which really has nothing to do with your original issue.
To solve point #2 this update creates the original appendBlock variable as well as a new __block __weak weakBlock reference to the same block. And then inside the block, a new (strong) block pointer is created to reference the weak block pointer. Without the use of the weak pointer, the code works but causes a warning.

Function calls with pointers in Objective C

I'm a newbie in Objective C, used to write C. Anyway, I have a class called DataProcessing:
DataProcessing.m
...
- (BOOL)MyStringTweaker:(NSString *)strIn : (NSString *)strOut {
if(some_thing) {
strOut = [NSString stringWithFormat:#"I_am_tweaked_%#", strIn];
return true;
}
else
return false;
}
...
From the AppDelegate (OSX Application)
AppDelegate.m
...
NSString *tweaked;
DataProcessing *data_proc = [[DataProcessing alloc] init];
if([data_proc MyStringTweaker:#"tweak_me":tweaked])
NSLog([NSString stringWithFormat:#"Tweaked: %#", tweaked]);
else
NSLog(#"Tweaking failed...");
...
This doesn't work, *tweaked is NIL after the call to MyStringTweaker...
What am I missing?
Objective-C, like C, is pass-by-value only. You need to change your method signature to be:
- (BOOL)MyStringTweaker:(NSString *)strIn : (NSString **)strOut
and use:
*strOut = [NSString stringWithFormat:#"I_am_tweaked_%#", strIn];
to do the assignment.
Then, where you call it, you need to pass the address of the pointer you want to fill in:
[data_proc MyStringTweaker:#"tweak_me" :&tweaked]
A good explanation is in the comp.lang.c FAQ.
Editorial aside: Why not label the second argument? It looks weird to have it naked like that.

'Assigning to 'id' from incompatible type'

I'm implementing a objective C wrapper for Box2d (which is written in c++). The b2Body keeps a reference to its wrapper B2Body in its userData field. GetUserData returns a void*. I'm now implementing fast iteration for getting the B2Bodies out of the B2World.
I get an 'Assigning to 'id' from incompatible type 'B2Body *' error at the line indicated below. Why?
#import "B2Body.h"
#import "B2World.h"
#import "Box2d.h"
#implementation B2World
-(id) initWithGravity:(struct B2Vec2) g
{
if (self = [super init])
{
b2Vec2 *gPrim = (b2Vec2*)&g;
_world = new b2World(*gPrim);
}
return self;
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len;
{
if(state->state == 0)
{
state->mutationsPtr = (unsigned long *)self;
state->extra[0] = (long) ((b2World*)_world)->GetBodyList();
state->state = 1;
}
// pull the box2d body out of extra[0]
b2Body *b = (b2Body*)state->extra[0];
// if it's nil then we're done enumerating, return 0 to end
if(b == nil)
{
return nil;
}
// otherwise, point itemsPtr at the node's value
state->itemsPtr = ((B2Body*)b->GetUserData()); // ERROR
state->extra[0] = (long)b->GetNext();
// we're returning exactly one item
return 1;
}
`
B2Body.h looks like this:
#import
#interface B2Body : NSObject
{
int f;
}
-(id) init;
#end
NSFastEnumerationState is a C structure, and the itemsPtr field is:
id __unsafe_unretained *itemsPtr;
In earlier versions, the __unsafe_unretained specifier was obviously missing.
Note, that the field itemsPtr is a pointer-to-id. Since id is essentially a pointer, itemsPtr is a pointer to an object pointer. Actually, this field is what holds the array of objects that allows the fast enumeration. Basically, it trolls through this array of object pointers.
Since I know nothing about Box2d, that's about all I can say. Assuming b->GetUserData() returns a pointer to an array of objects, you should be able to do this:
state->itemsPtr = (__unsafe_unretained id *)b->GetUserData();
While a bit dated, Mike Ash's article is still a great source for implementing fast enumeration.
EDIT
Just noticed that you are returning a single object. So, I assume GetUserData just returns a single object pointer. Since you need to return a pointer to object pointers, you would need to do something like this:
id object = (__bridge id)b->GetUserData();
state->itemsPtr = &object;
However, that stack object will be gone once you return from this method, which is why you are passed a stack buffer you can use. Thus, you should probably stuff that single pointer into the provided stack buffer:
*buffer = (__bridge id)b->GetUserData()
state->itemsPtr = buffer;

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];

pointer before declaration of object objective-c

can you tell me something : is it a mistake or can we write "result" without the " * " here :
#implementation Person (Sorting)
- (NSComparisonResult)compareByName:(Person *)person2 {
>>//here :
>>NSComparisonResult result = [self.lastName caseInsensitiveCompare:person2.lastName];
if (result == NSOrderedSame) {
return [self.firstName caseInsensitiveCompare:person2.firstName];
}
return result;
}
#end
Thanks
caseInsensitiveCompare method returns NSComparisonResult so not using * is absolutely correct.
In objective-c you must use pointers to obj-c objects, but NSComparisonResult is just an enum (i.e. plain integer) so you may freely use it without pointer.