tags:

views:

811

answers:

6

thing.h

@interface Thing : NSObject 
{
     float stuff[30];
}

@property float stuff;
@end

thing.m

@implementation Thing
@synthesize stuff;
@end

I get error: type of property 'stuff' does not match type of ivar 'stuff'

I don't want to use an NSArray because I'd have to make the floats into NSNumbers (right?) and that's a pain to do math with.

Update: I've noticed similar answers had guesses and trial answers. While I appreciate the attempts by non-Objective-C folks, I'm hoping for a definitive answer whether it's possible or not.

A: 

I'm not well-versed in Objective-C 2.0, but I'm guessing that the issue might be caused by the fact that a C array is essentially just a pointer to the first element of the array, meaning that the type of float stuff[30] is actually float *, not merely a float.

mipadi
No, C arrays are not pointers, they are arrays. It just so happens that arrays implicitly decay into pointers to their first elements, but there is still a difference between arrays and pointers.
Adam Rosenfield
+3  A: 

The type of the property must match the type of the instance variable it will be stored in, so you could do something like

  @interface Thing : NSObject 
  {
       float stuff[30];
  }

  @property float[30] stuff;
  @end

and it should work. I wouldn't recommend it though. I'm guessing you're looking for something like indexed properties from Delphi. The closest you'll get is something like the following.

  @interface Thing : NSObject 
  {
       float stuff[30];
  }

  - (void) setStuff:(float)value atIndex:(int)index;
  - (float) getStuffAtIndex:(int)index;
  @end
Daniel
You could simply make the property readonly and then read and set values at specific indices with the normal array operator. But I like yours better because it's more encapsulated.
Chuck
That failed to compile with a syntax error. Does it work for you?
willc2
See the accepted answer for code that actually compiles.
Daniel
+1  A: 

Even if you could get that to compile, it wouldn't behave well. 'stuff' would return a float*, and the client would have no idea how long the array way; 'setStuff:' would just change the pointer, and you'd either be pointing to stack-allocated data that would vanish out from under you or heap-allocated data that would leak because it wouldn't know to free it.

smorgan
+2  A: 

OK, I have compiled up the following code at it works as expected.

FloatHolder.h

@interface FloatHolder : NSObject {
    int _count;
    float* _values;
}

- (id) initWithCount:(int)count;

// possibly look into this for making access shorter
// http://vgable.com/blog/2009/05/15/concise-nsdictionary-and-nsarray-lookup/
- (float)getValueAtIndex:(int)index;
- (void)setValue:(float)value atIndex:(int)index;

@property(readonly) int count;
@property(readonly) float* values; // allows direct unsafe access to the values

@end

FloatHolder.m

#import "FloatHolder.h"


@implementation FloatHolder

@synthesize count = _count;
@synthesize values = _values;

- (id) initWithCount:(int)count {
    self = [super init];
    if (self != nil) {
     _count = count;
     _values = malloc(sizeof(float));
    }
    return self;
}

- (void) dealloc
{
    free(_values);

    [super dealloc];
}

- (float)getValueAtIndex:(int)index {
    if(index<0 || index>=_count) {
     @throw [NSException exceptionWithName: @"Exception" reason: @"Index out of bounds" userInfo: nil];
    }

    return _values[index];
}

- (void)setValue:(float)value atIndex:(int)index {
    if(index<0 || index>=_count) {
     @throw [NSException exceptionWithName: @"Exception" reason: @"Index out of bounds" userInfo: nil];
    }

    _values[index] = value;
}

@end

then in your other application code you can do something like the following:

** FloatTestCode.h **

#import <Cocoa/Cocoa.h>
#import "FloatHolder.h"

@interface FloatTestCode : NSObject {
    FloatHolder* holder;
}

- (void) doIt:(id)sender;

@end

** FloatTestCode.m **

#import "FloatTestCode.h"


@implementation FloatTestCode

- (id) init
{
    self = [super init];
    if (self != nil) {
     holder = [[[FloatHolder alloc] initWithCount: 10] retain];
    }
    return self;
}

- (void) dealloc
{
    [holder release];

    [super dealloc];
}

- (void) doIt:(id)sender {
    holder.values[1] = 10;
}
Daniel
+1  A: 

You can't do it the way you want to do it. You can jump through some hoops and get something similar, e.g. using Daniel's solution, but it's not quite the same thing. The reason you can't do it is that arrays are not lvalues in C. An lvalue is something that can appear on the left-hand side of an assignment. The following code is invalid C:

float stuff1[30], stuff2[30];
stuff1 = stuff2;  // ERROR: arrays are not lvalues

As a consequence, you can't declare properties whose types are not lvalues.

Adam Rosenfield
A: 

Daniel's FloatHolder answer has a major bug. It only allocates memory for one float and not for the whole array.

The line:

_values = malloc(sizeof(float));

Should be:

_values = malloc(sizeof(float) * count);

Otherwise it seems to be a good answer. (Sorry couldn't work out how to reply directly)

Qubei