【问题标题】:In an Objective-C block, can you make the outer method return? a la Ruby proc?在 Objective-C 块中,你可以让外部方法返回吗?一个 Ruby 过程?
【发布时间】:2013-04-27 11:19:21
【问题描述】:

在 Ruby 中,在块甚至嵌套块中调用 return 将转移控制并从最里面的包装方法返回,我说的是 proc。在 Ruby lambda 中,return 从块本身返回,调用方法继续,这也是 Objective-C 块的工作方式。

有没有办法让 Ruby 的 proc 语义 w.r.t 在 Objective-C 中返回?我想要一个块内的 return 来返回外部方法。

【问题讨论】:

  • 如果块在它定义的方法的范围之外运行,那么会返回做什么?
  • @newacct: 从运行它的范围返回,比如 Ruby...最坏的情况main

标签: objective-c ruby closures


【解决方案1】:

我不熟悉 Ruby,但 ObjC 块对其封闭方法施加控制的唯一方法是让该方法测试其返回值。

这可能很简单:

- (id)bloviate:(id (^)(void))bombast 
{
    // The method returns the results of the Block call;
    // thus, strictly speaking, the method returns when
    // the Block does.
    return bombast();
}

或者你可以检查返回值并从方法有条件地返回:

- (id)elucidate:(BOOL (^)(id))explanation
{
    id obj = /* Get some object */;
    if( explanation(obj) ) {
        return obj;
    }
    // Else continue
}

【讨论】:

    【解决方案2】:

    简短的回答是否定的,块内的 return 语句只会从块中返回。如下所示,您当然可以使用异常、@try 语句和宏的组合来构建一些不优雅的东西,但最后我认为这会比其他任何东西都更令人困惑。

    @interface MPReturnFromNestedBlockException : NSException
    + (void) returnExceptionWithResult:(id)result;
    @end;
    
    @implementation MPReturnFromNestedBlockException
    + (void) returnExceptionWithResult:(id)result
    {
        MPReturnFromNestedBlockException *exception = (id)
            [self exceptionWithName:@"MPReturnFromMethodException"
                             reason:nil
                           userInfo:@{@"result":result}];
        [exception raise];
    }
    @end
    
    #define abruptReturn(value)\
        ({ [MPReturnFromNestedBlockException returnExceptionWithResult:value]; })
    
    #define blockMayReturnAbruptly(block)\
    ({\
        id result = nil;\
        @try { block(); }\
        @catch(MPReturnFromNestedBlockException *exception) {\
            result = exception.userInfo[@"result"];\
        }\
        result;\
    })
    
    
    int main (int argc, const char * argv[])
    {
        @autoreleasepool {
    
            NSArray *numbers = @[@1, @2, @3];
    
            id value = blockMayReturnAbruptly(^{
                [numbers enumerateObjectsUsingBlock:^(id numA, NSUInteger index, BOOL *stop) {
                    double a = [numA doubleValue];
                    [numbers enumerateObjectsUsingBlock:^(id numB, NSUInteger index, BOOL *stop) {
                        double b = [numB doubleValue];
                        NSLog(@"%f x %f = %f", a, b, a*b);
                        if (a * b > 3)
                            abruptReturn(@(a*b));
                    }];
                }];
            });
    
            NSLog(@"Result = %@", value);
        }
        return 0;
    }
    

    输出如下:

    1.000000 x 1.000000 = 1.000000
    1.000000 x 2.000000 = 2.000000
    1.000000 x 3.000000 = 3.000000
    2.000000 x 1.000000 = 2.000000
    2.000000 x 2.000000 = 4.000000
    Result = 4
    

    【讨论】:

    • 这是个好主意,但异常不适用于 ObjC 中的控制流。 Cocoa is not exception-safe.
    • 您还可以补充一点,它可能不适用于经常使用块的多线程代码。
    • 我已经检查过了,当从另一个线程使用返回宏时,它肯定不起作用。这是有道理的,因为异常捕获机制尚未安装在正在运行的线程的调用堆栈上。
    猜你喜欢
    • 2011-09-27
    • 2023-04-03
    • 2017-02-15
    • 2012-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-11-20
    • 2018-09-18
    相关资源
    最近更新 更多