如何从Objective-C中的异步内部函数内部返回外部函数

Posted

技术标签:

【中文标题】如何从Objective-C中的异步内部函数内部返回外部函数【英文标题】:How to return outer function from inside async inner function in Objective-C 【发布时间】:2018-09-30 17:04:04 【问题描述】:

我想从内部异步内部函数中返回外部函数。 很快,我会为此目的使用完成处理程序,它会从函数中逃脱。但在 Objective-C 中,完成处理程序实际上不会从函数返回:

我的函数如下所示:

-(void)chosenFrom:(NSDictionary<NSString *,id> *)info

    [self asyncCode:info withCompletion:^(NSData *imageData) 
        if(imageData) 
             // I want to return from  chosenFrom function ***inside here.***
        
    ];

// This is to illustrate  completion handler don't escape
[self checkCompletionEscaping:^(NSString * lad) 
    NSLog(@"Check me %@", lad);// It would print all 3 lines below.
];


-(void) checkCompletionEscaping:(void(^)(NSString * lad)) completion 
completion(@"Hello"); // completion handler should've escaped from func.
completion(@"Shivam");
completion(@"How are you");

如果我使用 Swift,我可以使用完成处理程序从内部函数内部轻松地从外部函数返回:

private func getHistoryKeys(searchterm: String, completion: @escaping () -> ()) 
  let url = PubmedAPI.createEsearchURL(searchString: searchterm)
  let task = session.dataTask(with: url)  (data, response, error) in
   if let error = error 
      completion() // This would work as return
    else 
      completion() // Same as return
   
  
 task.resume()

PS:转义意味着从函数返回,就像return语句一样。

【问题讨论】:

目前尚不清楚您在这里尝试做什么,特别是您使用术语 escaping 来表示什么。你暗示你知道如何在 Swift 中做你想做的事,如果你编辑你的问题(不要在 cmets 中这样做)并包含你的 Swift 版本的checkCompletionEscaping 并描述它的行为与 Objectove 有何不同,这将有所帮助- C 版。这将帮助人们帮助你。 @CRD 更新问题 您没有重现与 Objective-C 中相同的示例,不幸的是,您的 Swift 代码中的 @escaping 并不意味着“像 return 语句一样从函数返回”,而是完全不同的东西。您似乎在异步上下文中寻找类似 non-local goto 的东西,但我说 seem 因为您似乎在说您可以在 Swift 中做您想做的事情它缺乏这样的结构。所以要么我错过了明显的东西,要么你需要更清楚。搜索 SO 寻找关于等待异步函数结果的问题,这是一个常见的问题,你可能会找到你想要的。 这基本上是您几个小时前发布的previous question 的副本。请删除这两个问题之一并根据需要编辑另一个。 【参考方案1】:

更简单的方法是调用另一个告诉它已完成的函数。

-(void)chosenFrom:(NSDictionary<NSString *,id> *)info

    [self asyncCode:info withCompletion:^(NSData *imageData) 
        if(imageData) 
             [self completedAsync:imageData];
        
    ];



-(void)completedAsync:(NSData*) imageData 
  // do your thang.

【讨论】:

【参考方案2】:

您似乎以不同的方式两次问了同一个问题,而人们却一直在帮助您。这不是一个答案,因为我真的不知道这个问题,但希望这可以帮助您找到答案或以不同的方式提出问题,以便人们可以帮助您。

让我们从你的陈述开始:

转义意味着从函数返回,就像 return 语句一样

你提到过在 Swift 中使用 @escaping。虽然术语 escaping 可能在某些语言中用于指代您所说的内容,但它在 Swift 中根本不是这个意思。

混淆它在 Swift 中的含义是合理的,因为它本质上使可能/应该是隐藏的编译器优化对语言中的程序员可见。 Swift 中的定义是:

闭包被称为转义当闭包作为参数传递给函数时,但在函数返回后被调用。当您声明一个将闭包作为其参数之一的函数时,您可以在参数类型前写上@escaping,表示允许闭包转义。

闭包可以逃逸的一种方法是将其存储在函数外部定义的变量中。例如,许多启动异步操作的函数将闭包参数作为完成处理程序。函数在开始操作后返回,但在操作完成之前不会调用闭包——闭包需要转义,稍后再调用。

Swift 编程语言 (Swift 4.2)

这告诉你@escaping 影响闭包的存储和使用方式,而不是闭包本身在调用时的实际作用。调用闭包时,无论是否标记为@escaping,都会执行相同的操作。

继续,用于说明 @escaping 的示例与您的情况相关 - 您显然希望有一个方法,例如 A,启动异步操作,例如 *B **,给它传递一个闭包,比如 C,稍后调用它会导致 A 返回。这是不可能的,因为当 C 被入侵时,不会调用 A 来返回。再看例子,重点补充:

闭包可以逃逸的一种方法是将其存储在函数外部定义的变量中。例如,许多启动异步操作的函数将闭包参数作为完成处理程序。 函数开始操作后返回,但直到操作完成才调用闭包

A 启动 B 之后,它返回,当 C 被调用时,A 启动 B返回。你显然是在要求不可能的事!

那么你可能想做什么?

您的意图可能是使 A 和它启动的异步操作 B 显示为单个 同步 单元并为 A B 完成工作之前不要返回。

很少在需要将异步包装为同步操作的情况下,还有更多的情况是人们试图这样做以使异步更易于处理,但最终破坏了异步的所有好处过程。做这样的包装通常看起来很简单;但除非你很好地掌握了GCD 块模型信号量线程阻塞死锁它为粗心的人设置了陷阱。

如果您的意图过于尝试包装异步性,那么更好的方法是接受它,在您的情况下,弄清楚如何让您的闭包 C 在异步调用更长的时间时执行所需的操作在您的方法 A 的相应调用终止并且不再存在之后。 30 年前,异步是日常编程的深奥结局,而今天它是其核心,您需要理解和使用它。

如果在尝试采用异步性之后,您决定遇到需要隐藏在同步包装器内的罕见情况之一,请在 SO 上搜索 异步信号量 等等,你应该找到解决方案。

如果您遇到异步性,或者实际上是在问一些完全不同的问题,请提出一个新问题,展示您所做/尝试过的/等等,毫无疑问会有人帮助您下一步。

HTH

【讨论】:

“这不是这样的答案”,但已作为答案发布。 :D @GeneCode - 它超出了评论限制,只是一点点;-)

以上是关于如何从Objective-C中的异步内部函数内部返回外部函数的主要内容,如果未能解决你的问题,请参考以下文章

如何从具有隐藏变量名称(参数前的_)或内部+外部(例如 foo(with bar:))的 Objective-C 调用 Swift 函数

如何从Objective-C中的字符串数组创建内部链接?

node js - 当函数依赖于内部的异步函数时,如何从函数返回值

如何在同一框架内访问 Objective-C 中的内部 Swift 类?

在异步函数内部,从回调函数返回值返回 Promise(undefined) [重复]

在异步函数内部,从回调函数返回值返回 Promise(undefined) [重复]