objective-cpropertiesautomatic-ref-countingunsafe-unretained

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


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);
}

Solution

  • 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);