Ruby ||= 在 Objective-C 中的等价物

6

最近我一直在学习Ruby,并且非常喜欢 ||= 特性,因为它可以让我更轻松地编写Objective C中的lazy getters。

目前,我编写的getter大致如下:

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

除非我对 ||= 有所遗漏,否则我可以使用以下代码在Ruby中编写上述代码:
- (NSArray *)myArray {
  return _myArray ||= [NSArray array];
}

显然,这样更加简洁。在Objective-C语言/运行时中是否有任何功能可以让您实现这一点?

此外,以下是一个用于getter的一行三元操作符,我不确定它是否与上面发布的经典方法(第一个代码段)一样有效。请问有人能告诉我这样做是否有任何问题:

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

虽然我不懂Objective C,但你是否想在类构造函数或初始化方法中使用_myArray = [NSArray array];呢?再次强调,这只是一个不懂Objective C的人提出的潜在建议 :p - David
1
请注意,如果您关心线程安全,则上述所有内容都是不正确的。 - Greg Parker
1
多线程引入了这样一种可能性,即线程A可能调用setter方法,未通过非nil测试,然后被线程B抢占,线程B也调用setter方法并未通过非nil测试。两个线程都决定创建一个新的——独立的——NSArray并将其存储在_myArray中;于是就会发生一些有趣的事情。 - jscs
2个回答

12

最后一个片段与您发布的第一个片段具有相同的效果。

作为一种改进,虽然Objective-C中没有像||=这样的运算符,但是您可以省略三元if运算符的第二个参数并执行以下操作:

return _myArray = _myArray ?: [NSArray array];

这恰好等同于

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

这是一种语言扩展,在现代版本的gccclang中都受支持。

奖励:如果你想节省更多的击键,可以这样做

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

顺便提一下,跳过中间操作数也可能有一些好处。

例如在这种情况下:

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

如果someMethod的评估结果为true,则它将被调用两次,而执行以下操作:

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

它只会被调用一次。


我非常喜欢这个,它提供了我的三元验证并对其进行了改进。很好的东西。非常遗憾没有 ||=。 - Peter Foti
确实如此,但请考虑Objective-C比Ruby更古老。此外,由于它只是原始C的一个薄层,我同意不引入任何“多余”运算符到语言中的设计选择。 - Gabriele Petronella

3

没有字面上的等价物,除非你想在Clang上进行黑客攻击。虽然逻辑运算符||会短路,但它不会计算其操作数。使用三元条件运算符的宏可以让你接近目标:

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

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

因为在C语言中,赋值行为类似于表达式并且会计算出所赋的值。

你实际上也可以创建一个OR_ASSIGN()宏,但我将其留给极度疯狂的读者作为练习。

或许同样疯狂的是一个函数:

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

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

但这只是变得荒谬了。

哦,你可以使用“缺失中间”?:运算符来改善第一个宏。 - Gabriele Petronella
非常感谢您的指出,@Gabriele!(我有一点点、或许是不太合理的偏好,不想省略中间操作数。) - jscs
我想这是一个品味问题,尽管我相信它是一个安全的结构,有时它可以节省一个方法调用(请参见我的答案)。 - Gabriele Petronella
@GabrielePetronella:没错,避免双重评估可能很重要。 - jscs
谢谢你的回答,Josh。讨论这些冷门话题并寻找减少按键次数的方法真是有趣。 - Peter Foti
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接