4

What's the best syntax for passing a c-style array containing NSString* to an objective-c method? Here's what I'm currently using:

- (void) f:(NSString **) a {

}

- (void) g {
    NSString* a[2] = {@"something", @"else"};
    [self f:a];
}
SundayMonday
  • 19,147
  • 29
  • 100
  • 154
  • 1
    Don't. Use a Cocoa collection within Cocoa or a non-Objective-C object for interop with "native" C/C++. – Barry Wark May 10 '12 at 19:31
  • 1
    Dave's answer is the right one; other answers may be technically correct, but that doesn't make it the right fix. Now, of course, there may be some esoteric reason why you think you need a C array, but it is likely the less desirable pattern to use... (There are reasons, but they mostly boil down to "we have a crappy library that we can't control and *have* to use it somehow") – bbum May 10 '12 at 19:39
  • Since Objective-C doesn't provide any "normal" way to declare array of constants (see discussion here: http://stackoverflow.com/questions/2436463/how-do-i-declare-an-array-as-a-constant-in-objective-c), I consider this is valid question and a correct approach when you need to pass array of constants. – Mike Keskinov May 21 '16 at 14:39

3 Answers3

6

Your only other option is the following:

- (void) f:(NSString* []) a {

}

It's identical when compiled. I don't know about "best", but I prefer this version's readability. It's easier to infer that the pointer you're passing is intended to be used as an array. Pointers to pointers have different uses elsewhere (see the various NSError** parameters used in the iOS SDK), so the distinction is helpful.

Matt Wilding
  • 20,115
  • 3
  • 67
  • 95
2

Slightly better is:

- (void) f:(NSString *[]) a

as it makes it clear it is expecting an array (passed by reference) of references to NString, rather than a reference to a reference to an NString.

However this will in all probability not change the compiler's type checking, you could pass an NSString ** without issue. The same goes if you add a bounds:

- (void) f:(NSString *[2]) a

Here the compiler will in all probability just ignore the 2 - both when calling and inside the body, you're just adding a "comment". (If you are passing multi-dimensional arrays you do need to specify all but the last index.)

Addendum

Prompted by Mike Keskinov (see comments)

Update for ARC

Under ARC the declaration in the question:

NSString* a[2] = {@"something", @"else"};

which has no explicit ownership qualifier is treated as:

NSString* __strong a[2] = {@"something", @"else"};

that is an array of strong references to strings. When a pointer to a variable itself is passed, in this case a pointer to an array, the compiler needs to know the ownership qualification of the pointed-at variable. At the time of writing in the case of pointer to an array the compiler has no default so the declaration in this answer above:

- (void) f:(NSString *[]) a

will produce the error, as reported by Mike Keskinov in the comments below, and you must insert an explicit ownership qualifier:

- (void) f:(NSString * __strong []) a

With that information the compiler knows how to automatically handle the string references in the array – in the body of f the compiler will automatically as required retain and release NSString references.

For more details on ARC and pointers to variables/pointers see Handling Pointer-to-Pointer Ownership Issues in ARC and NSError and __autoreleasing.

Use For Constant Arrays

Though not part of the original question is it clear that many are using C arrays as a means of having an array of string constants in Objective-C. For this particular use observe:

  1. If the array and parameter types are declared as arrays of constant references to strings then the array elements cannot be changed;

  2. Making the array static will also ensure it is only create and initialised once, even if declared with a function; and

  3. If your C array contains only string literals then the default __strong ownership qualifier is not required as string literals are immortal. Instead the __unsafe_unretained qualifier may be used, the references in the array will always be valid.

These produce the code fragments:

static NSString * const __unsafe_unretained a[2] = {@"something", @"else"}; 

and:

- (void) f:(NSString * const __unsafe_unretained[]) a

and you have the goal of a constant array of constant NSString literals. The compiler will not allow the array to be modified, or insert redundant memory management calls.

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
  • This gives me compiler error saying telling me that I need to "specify ownership" of the passing array. Not sure what it means, but I simple switch to the ** approach and it works just fine. – Mike Keskinov May 21 '16 at 14:40
  • @MikeKeskinov - This is due to ARC. From this comment and the one you've on the question itself it appears you are seeking to create a constant array of constant strings. See addendum to the answer. HTH – CRD May 22 '16 at 00:46
0

The best way would be to use an NSArray instead of a c-style array.

Dave Wood
  • 13,143
  • 2
  • 59
  • 67
  • 1
    Please be nice to people trying to help – Philippe Leybaert May 10 '12 at 19:08
  • @PhilippeLeybaert indeed I agree. While I appreciate the `NSArray` suggestion and I would definitely use that object in other circumstances at the moment I'm using c-style arrays. – SundayMonday May 10 '12 at 19:20
  • 3
    @SundayMonday your "at the moment" not withstanding, @Dave Wood's answer is the right one. There is *very* little reason to ever use an array of Objective-C `id`s. Doing so voids all of the standard (manual or ARC) memory management in Cocoa and will certainly bite you in the long run. If the point is C/C++ interop, there are beter ways. – Barry Wark May 10 '12 at 19:30
  • 2
    I agree with the sentiment here: This is the right way to do it, if not technically an answer to the question. – Matt Wilding May 10 '12 at 19:52