0

I'm trying to create a C array of objective C NSStrings using malloc. I'm not doing it right, but I don't think I'm far off. Maybe someone can point me in the right direction. Let's say we want 5 strings in our array for the sake of argument.

Interface:

@interface someObject : NSObject {
    NSString **ourArray;
}
@property () NSString **ourArray;
@end

Implementation:

@implementation someObject
@synthesize ourArray;

-(id)init {
    if((self = [super init])) {
        self->ourArray = malloc(5 * sizeof(NSString *));
    }
    return self;
}

-(NSString *)getStringAtPos3 {
    if(self.ourArray[3] == nil) {
        self.ourArray[3] = @"a string";
    }
    return self.ourArray[3];
}
@end

When I set a breakpoint in getStringAtPos3 it doesn't see the array element as nil so it never goes into the if statement.

Nick
  • 3,958
  • 4
  • 32
  • 47
  • 3
    There is no need to **ever** use `self->`. It'll give old hat ObjC developers hives if you do. – bbum Dec 05 '10 at 01:21
  • 2
    Note also that a C array of pointers to Objective-C objects is an exceedingly atypical pattern. Just use an `NSArray` or `NSPointerArray` instead. – bbum Dec 05 '10 at 01:23
  • What's bad about using `self->`? Also I'm developing for iOS which doesn't have `NSPointerArray`, but I want an array of a dynamic size which can have null entries. Presumably C arrays are faster too, although it wouldn't make much difference in this case. – Nick Dec 05 '10 at 11:54

3 Answers3

2

mallocing an array of pointers is done as follows:

self->ourArray = malloc(5 * sizeof(NSString *));
if (self->ourArray == NULL)
    /* handle error */
for (int i=0; i<5; i++)
    self->ourArray[i] = nil;

malloc doesn't make guarantees about the contents of the returned buffer, so set everything to nil explicitly. calloc won't help you here, as a zero pattern and nil/NULL aren't the same thing.

Edit: even though zero and null may be the same on i386 and arm, they are not the same conceptually, just like NULL and nil are strictly not the same. Preferably, define something like

void *allocStringPtrs(size_t n)
{
    void *p = malloc(sizeof(NSString *));
    if (p == NULL)
        // throw an exception
    for (size_t i=0; i<n; i++)
        p[i] = nil;
    return p;
}
Community
  • 1
  • 1
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Is it best to use malloc or calloc then? calloc does return all nil objects though. – Nick Dec 05 '10 at 11:32
  • `calloc` may return all `nil`-objects *on your platform*, but does not guarantee this; it's a coincidence. It is good style not to rely on such low-level platform features. It's even better to use a native Objective C solution like `NSArray` and stay clear of C interfaces such as `malloc`. If you must use these interfaces, use `malloc` for pointer arrays and set the elements explicitly. – Fred Foo Dec 05 '10 at 12:47
1

I figured out the problem - I should have been using calloc, not malloc. While malloc simply allocates the memory, calloc

contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero.

This means you get an array of nil objects essentially as in objective c 0x0 is the nil object.

Nick
  • 3,958
  • 4
  • 32
  • 47
  • No, that's not what it means. Confusingly perhaps, a null pointer is not guaranteed to be all-zeroes, which is what `calloc` gives you. (It is on i386, but never rely on it.) Explicitly set all elements to null and add the `sizeof` as @chrisaycock suggests. – Fred Foo Dec 05 '10 at 01:17
  • 1
    Mm, I don't think so, larsmans. C99 section 6.3.2.3, "An integer constant expression with the value 0, or such an expression cast to type void \*, is called a null pointer constant." and "The macro NULL is defined in (and other headers) as a null pointer constant; see 7.17.". 7.17 specifies NULL as a macro "which expands to an implementation-defined null pointer constant". So, NULL could be 0 or (void\*)0 in different implementations, but not 1. – Ken Dec 05 '10 at 06:36
  • 1
    @Ken Both of those are constant expressions, though. @larsmans is correct that there is no guarantee that `NULL` will be represented internally with all zero bits. That said, it is on iOS/OS X, and rather safe to depend upon for those systems. – Justin Spahr-Summers Dec 05 '10 at 11:36
  • 0x0 is the nil object though, so surely if calloc allocates the pointer as a set of zeros, then it will be the nil object? Using calloc works on i386 and arm anyway. – Nick Dec 05 '10 at 11:46
  • +1 for Justin: this rule is only true in constant expressions of pointer type, while `calloc` writes zeroes of byte type. The 0 in that case is translated by the compiler to whatever is the proper null pointer for the current platform. Relying on null being all-zeroes is non-portable and may break the application if it moves to a different platform. – Fred Foo Dec 05 '10 at 12:45
  • However, this code is not designed to be portable - it's an array of ObjC objects. So surely iterating through the whole array and setting nil just wastes time? – Nick Dec 05 '10 at 13:16
  • @Nick: Why on earth would you want to `calloc` this array and handle it in an intentionally non-portable way, when Objective-C gives you easier-to-use, portable tools to begin with? AFAICT, you're abusing C by discarding it's portability features and abusing ObjC by unnecessarily (?) dropping down to the level of C. Sorry if this sounds harsh, I just can't figure out why you'd want to do this in the first place. – Fred Foo Dec 05 '10 at 14:55
  • From my comment on the original question: I'm developing for iOS which doesn't have NSPointerArray, but I want an array of a dynamic size which can have null entries. Presumably C arrays are faster too, although it wouldn't make much difference in this case. If nothing else, it's increasing my understanding on this stuff. – Nick Dec 05 '10 at 16:28
  • Ah, that seems a valid reason. I don't think C arrays are any faster though, although I have no experience in ObjC. I can tell you that in C++, the "native" solution is often as fast as C. – Fred Foo Dec 06 '10 at 10:47
  • I think they would be faster as using NSArrays involve objective c message sends which are quite slow. – Nick Dec 31 '10 at 00:07
1

One issue is this:

self->ourArray = malloc(5 * sizeof(NSString *));  // notice the sizeof()
chrisaycock
  • 36,470
  • 14
  • 88
  • 125