Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby ||= equivalent in Objective-C

I've been learning ruby as of late and am basically in love with the ||= feature as it would make writing lazy getters much easier in Objective C.

Currently I write getters such as:

- (NSArray *)myArray {
  if (!_myArray) {
    _myArray = [NSArray array];
  }
  return _myArray
}

Unless im missing something with the ||= I would be able to write the previous code in Ruby using:

- (NSArray *)myArray {
  return _myArray ||= [NSArray array];
}

Thats obviously much cleaner. Is there anything at all in the Objective-C language/runtime that would enable you to do this?

Also, the following is a one line ternary for getters, im not sure if its as effective as the tried and true method posted above (first snippet). Can someone tell me if theres anything wrong with doing:

- (NSArray *)myArray {
  return _myArray = _myArray ? _myArray : [NSArray array];
}
like image 488
Peter Foti Avatar asked Oct 26 '13 03:10

Peter Foti


2 Answers

The last snippet has the same effect as the first one you posted.

As an improvement, while there's no operator like ||= in Objective-C, you can omit the second parameter of a ternary if operator and do

return _myArray = _myArray ?: [NSArray array];

which is exactly equivalent to

return _myArray = _myArray ? _myArray : [NSArray array];

This is a language extension supported by modern versions of both gcc and clang.

Bonus: if you want to save some more keystrokes, you can do

- (NSArray *)myArray {
    return _myArray = _myArray ?: @[];
}

As a side note, skipping the middle operand can also have some benefit.

For instance in this case

id x = [self someMethod] ? [self someMethod] : [self anotherMethod];

if someMethod evaluates to true it will be called twice, whereas doing

id x = [self someMethod] ?: [self anotherMethod];

it will only be called once.

like image 74
Gabriele Petronella Avatar answered Sep 26 '22 22:09

Gabriele Petronella


There's no literal equivalent, unless you want to hack on Clang. The || logical operator, while it short-circuits, doesn't evaluate to its operands. A macro, using the ternary conditional, will get you close:

#define NON_NIL(o, p) ((o) ? (o) : (p))

- (NSMutableArray *)myArray
{
    return _myArray = NON_NIL(_myArray, [NSMutableArray array]);
}

because assignment in C sort of acts like an expression and evaluates to the assigned value.

You could actually make an OR_ASSIGN() macro, too, but I'm going to leave that as an exercise for the utterly deranged reader.

Perhaps equally deranged would be a function:

id lazySet(id *obj; id(^valBlock)(void))
{
    if( !(*obj) ){
        *obj = valBlock();
    }
    return *obj;
}

- (NSMutableArray *)myArray
{
    return lazySet(&_myArray, ^{return [NSMutableArray array]});
}

but that's just getting ridiculous.

like image 26
jscs Avatar answered Sep 25 '22 22:09

jscs