Skip to the content.

Out parameter in Objective-C with ARC

When you use an out parameter in Objective-C with ARC, you should know what’s going on behind the scene. Imagine you write a method like this:

- (void)test:(NSString **)value {
  *value = @"Test";
}

And call this method like:

NSString *s = nil;
[obj test:&s];

It works fine, but you should know that a compiler generates a temporary variable before calling the method. This is because the actual type of the out parameter is NSString *__autoreleasing* but the actual variable type is NSString *__strong. A compiler generates code like this:

- (void)test:(NSString *__autoreleasing *)value {
  *value = @"Test";
}

{
  ...
  NSString *__strong s = nil;
  NSString *__autoreleasing temp = s;
  [obj test:&temp];
  s = temp;
}

Now, you’re writing a method that returns a block in which an out parameter is set. For example:

typedef void (^Block)(NSString *);
- (Block)test:(NSString **)value {
  return ^(NSString *s) {
    *value = s;
  }
}

and call this like:

NSString *s = nil;
Block block = [o test:&s];
block(@"Test");

Although you may think s becomes @”Test”, but it doesn’t. A code compiler generates is something like:

NSString *__strong s = nil;
NSString *__autoreleasing temp = s;
Block block = [o test:&temp];
s = temp;
block(@"Test");

and it’s clear that calling block won’t change a value of s. To make it work, you should make the parameter __strong.

typedef void (^Block)(NSString *);
- (Block)test:(NSString *__strong *)value {
  return ^(NSString *s) {
    *value = s;
  }
}

In this case, a compiler won’t generate a temporary variable and s will be set as you expect.