21

I'm trying to pass an NSString by reference but it doesn't work.

This is the function:

+(void)fileName:(NSString *) file
{
    file = @"folder_b";
}

and this is the call:

NSString *file;

[function fileName:file];

nslog(@"%@",file);    // and there is nothing in the string....

What I must do to pass my string by reference?

andyvn22
  • 14,696
  • 1
  • 52
  • 74
alex
  • 869
  • 4
  • 13
  • 28

5 Answers5

63

If you want to return a value, then return a value. Pass by reference in Cocoa/iOS is largely limited to NSError**.

Given:

+(void)fileName:(NSString *) file

Then do:

+(NSString *) fileName;

And be done with it.

If you need to return more than one value at a time, that begs for a structure or, more often, a class.

In Objective-C, pass by reference smells like you are doing it wrong.


Pass by reference in Objective-C is reserved largely for returning NSError* information about a recoverable failure, where the return value of the method itself indicates whether or not the requested task succeeded or failed (you can pass NULL as the NSError** argument to allow the method to optimize away creating said error metadata).

Pass by references is also used to retrieve interior state of objects where the return value is effectively a multi-value. I.e. methods from AppKit like the following. In these cases, the pass-by-reference arguments are typically either optional or are acting as secondary return values.

They are used quite sparingly across the API. There is certainly use for pass by reference, but -- as said above -- doing so should be quite rare and rarer still in application code. In many cases -- and in some of the cases below, potentially -- a better pattern would be to create a class that can encapsulate the state and then return an instance of said class instead of pass by reference.

NSWorkspace.h:- (BOOL)getInfoForFile:(NSString *)fullPath application:(NSString **)appName type:(NSString **)type;
NSTextView.h:- (void)smartInsertForString:(NSString *)pasteString replacingRange:(NSRange)charRangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString;
NSAttributedString.h:- (BOOL)readFromURL:(NSURL *)url options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict;
NSNib.h:- (BOOL)instantiateWithOwner:(id)owner topLevelObjects:(NSArray **)topLevelObjects NS_AVAILABLE_MAC(10_8);
NSSpellChecker.h:- (NSRange)checkGrammarOfString:(NSString *)stringToCheck startingAt:(NSInteger)startingOffset language:(NSString *)language wrap:(BOOL)wrapFlag inSpellDocumentWithTag:(NSInteger)tag details:(NSArray **)details NS_AVAILABLE_MAC(10_5);
bbum
  • 162,346
  • 23
  • 271
  • 359
  • 2
    "In Objective-C, pass by reference smells like you are doing it wrong." Could you explain that? What's the down side of doing it? – Philip007 Sep 27 '12 at 12:34
  • 2
    @gal did you read the actual answer? If you need two parameters, use a structure or a class. Very very rarely is pass by ref the right pattern to use. – bbum Jun 25 '13 at 13:39
  • 1
    I also disagree. Passing a pointer to a pointer is something that should only be used when absolutely necessary, as it can lead to weird errors if you're not sure about what you're doing, but that doesn't mean it's wrong to do it, as your answer implies. – Vitor M. Barbosa May 20 '14 at 14:22
32

I believe you're looking for:

+ (void)fileName:(NSString **)file
{
  *file = @"folder_b";
}

What's really done here is we're working with a pointer to a pointer to an object. Check C (yup, just plain C) guides for "pointer dereference" for further info.

(...But as has been pointed out repeatedly, in this particular example, there's no reason to pass by reference at all: just return a value.)

andyvn22
  • 14,696
  • 1
  • 52
  • 74
3

Try this

+(void)filename:(NSString **)file {
     *file=@"folder_b";
}

and send the file as &file like:

NSString *file;
[function fileName:&file];
nslog(@"%@",file);

hope this will work.

Wh1T3h4Ck5
  • 8,399
  • 9
  • 59
  • 79
Sikander
  • 447
  • 4
  • 26
3

Passing a pointer to your object is the Objective C (and C) way of passing by reference.

I agree with 'bbum' that a perceived need to pass by reference is a signal to think about what you are doing; however, it is by no means the case that there are not legitimate reasons to pass by reference.

You should not create classes willy-nilly every time you have a function or method that needs to return more than one value. Consider why you are returning more than one value and if it makes sense to create a class for that then do so. Otherwise, just pass in pointers.

-Just my 2 cents

bhause
  • 123
  • 1
  • 4
-2

I suspect this is because NSString is immutable. Have you tried NSMutableString?

Jason
  • 86,222
  • 15
  • 131
  • 146
  • Using a mutable string cannot 'set' the variable to a new object, so strictly will not work in the example given. But depending on the circumstances it is likely the best way to go (+1) (instead of double object ptr's) – Akusete Jul 26 '10 at 01:58
  • One option would be to pass an `NSMutableString` and then use `setString:`, but that wouldn't be "by reference". – andyvn22 Jul 26 '10 at 02:01
  • @andyvn: You are right, my point was that (as often is the case) the technically write answer to the question, and the best solution a the problem are not the same thing. – Akusete Jul 26 '10 at 02:08
  • 2
    There's no reason to be passing by reference to this kind of method at all. –  Jul 26 '10 at 03:52