1

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 Answers3

0

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.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • He is having a triple pointer, so _array[i][j] wouldn't be a concrete class, but a pointer and that's allowed. – Ramy Al Zuhouri Dec 06 '12 at 00:51
  • @RamyAlZuhouri So how would you declare the property that corresponds to _array? – StephenAshley.developer Dec 06 '12 at 01:36
  • The property would just be @property (nonatomic, readwrite) NSString * __unsafe_unretained ** _array; The problem is about how retaining the strings. – Ramy Al Zuhouri Dec 06 '12 at 01:51
  • My big doubt is just how to retain an object pointed by a void pointer, I don't know how to do this.But he could declare the property if then he's able to retain the created objects. – Ramy Al Zuhouri Dec 06 '12 at 02:57
  • @RamyAlZuhouri You got it exactly right. To retain an object pointed to by an __unsafe_unretained pointer, you add a pointer to the object to an NSMutableArray, which has no function other than to "own" the __unsafe_unretained objects. When the larger object goes out of scope, the runtime calls the larger object's -(void) dealloc method (where you free the memory that holds all the pointers) and releases the NSMutableArray, thus releasing ownership of the contained objects. At least that's how I think it works. – StephenAshley.developer Dec 07 '12 at 18:05
  • I'll tell you how to force ARC to retain these objects. – Ramy Al Zuhouri Dec 07 '12 at 19:25
0

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);
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
0

(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];
}
Hot Licks
  • 47,103
  • 17
  • 93
  • 151