property for ivar that points to two-dimensional array of pointers to NSStrings

412 views Asked by At

I want to create a class that contains a dynamic, two-dimensional c-array of pointers to NSStrings. I know I can simulate a two-dimensional array using an NSArray containing multiple NSArrays, but if possible I'd like to do this using a traditional two-dimensional c-array. ARC won't allow a simple assignment of a pointer to an NSString to an element of a c-array unless you use "__unsafe_unretained":

@interface NumberStringsArray : NSObject
{
  @public
  NSString * __unsafe_unretained **_array;
}

To avoid memory leaks and to give an object in the class ownership of each NSString assigned to the c-array, I add a pointer to each NSString object to an NSMutableArray. In -(void)dealloc I free the memory acquired to create the two-dimensional c-array.

Here's my question: How do I declare a property based on the _array ivar so that I can refer to the i,j element of the array as "foobar.array[i][j]" rather than "foobar->array[i][j]"?

Later amplification: I did it in a very similar manner to the answerer except for the __bridge stuff. I don't know if that makes a difference. I allocate the two-dimensional array here:

self->_array = (NSString * __unsafe_unretained **)calloc(_columnCount, sizeof(void *));
if (!self->_array)
  return nil;
for (UINT16 i = 0; i < _columnCount; i++)
{
  self->_array[i] = (NSString * __unsafe_unretained *)calloc(_rowCount, sizeof(void *));
  if (!self->_array[i])
  {
    for (UINT16 a = 0; a < _columnCount; a++)
      if (self->_array[a])
        free(self->_array[a]);
    if (self->_array)
      free(self->_array);
    return nil;
  }
}

I put pointers to the NSString objects into the array using substrings generated from a file of comma-separated values:

NSArray *numbers = [line componentsSeparatedByString: @","];
for (UINT16 i = 0; i < _columnCount; i++)
{
  NSString *number = @"";
  if (i < [numbers count])
    number = [numbers objectAtIndex: i];
  //
  // save it in owners
  //
  [self.owners addObject: number];
  self->_array[i][j] = number;
}

In -(void)dealloc I free all the memory:

-(void)dealloc
{
  for (UINT16 i = 0; i < self.columnCount; i++)
    if (self->_array[i])
      free(self->_array[i]);
  if (self->_array)
    free(self->_array);
}
3

There are 3 answers

0
Ramy Al Zuhouri On BEST ANSWER

Declare this property:

@property (nonatomic) NSString * __unsafe_unretained **_array;

Then you can allocate the pointers to objects:

_array= (NSString * __unsafe_unretained **) malloc(M*sizeof(CFTypeRef) );
for(NSUInteger i=0; i<M;i++)
{
    _array[i]= ((NSString * __unsafe_unretained *) malloc(N*sizeof(CFTypeRef) );
    for(NSUInteger j=0; j<N;j++)
    {
        _array[i][j]= (__bridge NSString*) (__bridge_retained CFTypeRef) [[NSString alloc]initWithCString: "Hello" encoding: NSASCIIStringEncoding];
        // I see that you got habit with C so you'll probably like this method
    }
}

Then when you don't need it anymore, free the array:

for(NSUInteger i=0; i<M; i++)
{
    for(NSUInteger j=0; j<N;j++)
    {
        CFTypeRef string=(__bridge_transfer CFTypeRef) _array[i][j];
    }
    free(_array[i]);
}
free(_array);
0
Hot Licks On

(Note: This addresses the creation/use of the property to access the data, not the way the data should be managed by conventional Objective-C storage management or by ARC. Thinking about that makes my head hurt.)

If you want a read-only C array to "look" like an Objective-C property, declare the property such as @property (readonly, nonatomic) char* myProp; and then, rather than using @synthesize, implement a getter for it along the lines of:

-(char**)myProp {
    return myPropPointer;
    // Or, if the array is allocated as a part of the instance --
    return &myPropArray[0];
}
6
Jack On

You can't because you can't declare a concrete object for an Objective-C class. So

NumberStringsArray object;

is not allowed.

You are forced to declare it as

NumberStringsArray *object = [[NumberStringsArray alloc] init.. ];

so you have to access to the ivar through the correct -> operator applied to pointers. Mind that the object.something in Objective-C is just a shorthand for [object something] while in standard C you would use . to access to fields of a concrete struct.