程序为什么要使用闭包?


58

在阅读了许多解释闭包的文章之后,我仍然缺少一个关键概念:为什么要写闭包? 程序员将执行什么样的特定任务,最好通过闭包来完成?

Swift中闭包的示例是访问NSUrl并使用反向地址解析器。这是一个这样的例子。不幸的是,这些课程只是关闭。他们没有解释为什么将代码解决方案写为闭包。

一个现实世界中编程问题的示例可能会触发我的大脑说:“啊哈,我应该为此写一个闭包”,这比理论上的讨论要有用得多。该站点上不乏理论讨论。


7
“这里最近对封闭件的审查表示”-您是否缺少链接?
Dan Pichelman 2015年

2
您应该在相关答案下方的注释中添加有关异步任务的说明。这不是您原始问题的一部分,并且答复者将永远不会收到有关您所做修改的通知。
罗伯特·哈维

5
只是一个小窍门:Closures的关键点是词汇作用域(与动态作用域相对),没有词法作用域的闭合是没有用的。闭包有时也称为词汇闭包。
霍夫曼

1
闭包允许您实例化函数
user253751 2015年

1
阅读任何有关函数式编程的好书。也许开始SICP
巴西莱Starynkevitch

Answers:


33

首先,没有闭包是没有不可能的。您始终可以用实现特定接口的对象替换闭包。这只是简洁和减少耦合的问题。

其次,请记住,闭包经常不恰当地使用,在这种情况下,简单的函数引用或其他构造会更清晰。您不应将所有示例视为最佳实践。

在使用高阶函数时,当您实际上需要传达状态时,闭包真正超越其他构造的地方就可以了,您可以将它变成单行,如Wikipedia页面上有关闭包的 javascript示例所示:

// Return a list of all books with at least 'threshold' copies sold.
function bestSellingBooks(threshold) {
  return bookList.filter(
      function (book) { return book.sales >= threshold; }
    );
}

在这里,threshold它从定义的地方到使用的地方都非常简洁自然。精确地将其范围限制为尽可能小。 filter不必编写以允许传递客户端定义的数据(例如阈值)的可能性。我们不必仅仅为了传达这一小功能中的阈值而定义任何中间结构。它是完全独立的。

可以不加闭包地编写此代码,但是它将需要更多代码,并且很难遵循。而且,JavaScript具有相当冗长的lambda语法。例如,在Scala中,整个函数体为:

bookList filter (_.sales >= threshold)

但是,如果您可以使用ECMAScript 6,那么借助箭头功能,甚至JavaScript代码也变得更加简单,并且实际上可以放在一行中。

const bestSellingBooks = (threshold) => bookList.filter(book => book.sales >= threshold);

在您自己的代码中,寻找可以生成大量样板的地方,仅用于将临时值从一个地方传递到另一个地方。这些是考虑用闭包替代的绝佳机会。


闭合如何精确地减少耦合?
sbichenko

如果没有闭包,则必须对bestSellingBooks代码和filter代码都施加限制,例如特定的接口或用户数据自变量,以便能够传递threshold数据。这将两个功能以不太可重用的方式联系在一起。
Karl Bielefeldt 2015年

51

作为解释,我将从这个关于闭包的优秀博客文章中借用一些代码。它是JavaScript,但这是大多数谈论闭包的博客帖子使用的语言,因为闭包在JavaScript中是如此重要。

假设您想将数组呈现为HTML表。您可以这样做:

function renderArrayAsHtmlTable (array) {
  var table = "<table>";
  for (var idx in array) {
    var object = array[idx];
    table += "<tr><td>" + object + "</td></tr>";
  }
  table += "</table>";
  return table;
}

但是,关于数组中每个元素的呈现方式,您都受JavaScript左右。如果要控制渲染,可以执行以下操作:

function renderArrayAsHtmlTable (array, renderer) {
  var table = "<table>";
  for (var idx in array) {
    var object = array[idx];
    table += "<tr><td>" + renderer(object) + "</td></tr>";
  }
  table += "</table>";
  return table;
}

现在,您可以传递一个返回所需渲染的函数。

如果要在每个表行中显示运行总计怎么办?您将需要一个变量来跟踪总数,不是吗?闭包允许您编写渲染器函数,该函数关闭正在运行的total变量,并允许您编写可以跟踪正在运行的total的渲染器:

function intTableWithTotals (intArray) {
  var total = 0;
  var renderInt = function (i) {
    total += i;
    return "Int: " + i + ", running total: " + total;
  };
  return renderObjectsInTable(intArray, renderInt);
}

即使重复调用并退出这里发生的魔术renderInt 仍然保留对total变量的访问renderInt

在比JavaScript更传统的面向对象的语言中,您可以编写一个包含该总变量的类,然后将其传递而不是创建闭包。但是,关闭是一种更强大,清洁和优雅的方式。

进一步阅读


10
通常,您可以说“一流的函数==只有一个方法的对象”,“关闭==只有一个方法和状态的对象”,“对象==闭包束”。
约尔格W¯¯米塔格

看起来不错。另一件事,可能是徒劳的,是Javascript 面向对象的,您可以创建一个包含总变量的对象并将其传递。闭包仍然更加强大,干净和优雅,并且也使Javascript变得更加惯用,但是其编写方式可能暗示Javascript无法以面向对象的方式实现这一目标。
KRyan 2015年

2
其实,要真正迂腐:闭包是什么使JavaScript的面向对象的第一名!OO是关于数据抽象的,而闭包是在JavaScript中执行数据抽象的方式。
约尔格W¯¯米塔格

@JörgWMittag:就像您可以将cloures作为仅使用一种方法的对象一样,您可以将对象看作是封闭相同变量的闭包的集合:对象的成员变量只是对象的构造函数和对象的方法的局部变量是在构造函数范围内定义的闭包,可在以后调用。构造函数返回一个高阶函数(对象),该函数可以根据用于调用的方法名称在每个闭包上分派。
Giorgio 2015年

@Giorgio:的确,例如,这就是通常在Scheme中实现对象的方式,实际上也是在JavaScript中实现对象的方式(考虑到它与Scheme的密切关系,毫不奇怪,毕竟Brendan Eich最初是受雇于设计一个Scheme方言并在Netscape Navigator中实现了嵌入式Scheme解释器,直到后来才被勒令制作一种“带有看起来像C ++的对象”的语言,之后他进行了最少的更改以符合那些营销要求。
约尔格W¯¯米塔格

22

的目的closures仅仅是保持状态;因此,名称closure-它关闭状态。为了便于进一步解释,我将使用Javascript。

通常你有一个功能

function sayHello(){
    var txt="Hello";
    return txt;
}

变量范围绑定到此函数的位置。因此,执行后变量txt超出了范围。函数完成执行后,无法访问或使用它。

闭包是一种语言构造,如前所述,它允许保留变量的状态并因此扩展范围。

这在不同情况下可能很有用。一种用例是构造高阶函数

在数学和计算机科学中,高阶函数(也是函数形式,函数或函子)是至少执行以下一项功能的函数:1

  • 将一个或多个功能作为输入
  • 输出一个功能

一个简单但不可否认并非太有用的示例是:

 makeadder=function(a){
     return function(b){
         return a+b;
     }
 }

 add5=makeadder(5);
 console.log(add5(10)); 

您定义一个函数makedadder,该函数将一个参数作为输入并返回一个函数。有一个外部函数function(a){}和一个内部 函数function(b){}{}。您还(隐式)定义了另一个函数,add5作为调用高阶函数的结果makeaddermakeadder(5)返回一个匿名(内部)函数,该函数依次使用1个参数,并返回外部函数的参数与内部函数的参数之和。

是,在返回的内部函数,该函数的实际增加,外部函数的参数的(范围a)被保留。add5 记得,参数a5

或者显示至少一个有用的示例:

  makeTag=function(openTag, closeTag){
     return function(content){
         return openTag +content +closeTag;
     }
 }

 table=makeTag("<table>","</table>")
 tr=makeTag("<tr>", "</tr>");
 td=makeTag("<td>","</td>");
 console.log(table(tr(td("I am a Row"))));

另一个常见用例是所谓的IIFE =立即调用的函数表达式。在javascript中,伪造私有成员变量非常普遍。这是通过创建私有范围= 的函数来完成的closure,因为它是在调用定义后立即生成的。结构是function(){}()。注意()定义后的括号。这样就可以将其用于显示模块模式的对象创建中。诀窍是创建一个作用域并返回一个对象,该对象在执行IIFE之后可以访问该作用域。

Addi的示例如下所示:

 var myRevealingModule = (function () {

         var privateVar = "Ben Cherry",
             publicVar = "Hey there!";

         function privateFunction() {
             console.log( "Name:" + privateVar );
         }

         function publicSetName( strName ) {
             privateVar = strName;
         }

         function publicGetName() {
             privateFunction();
         }


         // Reveal public pointers to
         // private functions and properties

         return {
             setName: publicSetName,
             greeting: publicVar,
             getName: publicGetName
         };

     })();

 myRevealingModule.setName( "Paul Kinlan" );

返回的对象具有对函数(例如publicSetName)的引用,这些函数又可以访问“私有”变量privateVar

但是这些是Javascript更特殊的用例。

程序员将执行哪些特定任务,而闭包可以最好地完成该任务?

有几个原因。一个人可能对他来说很自然,因为他遵循一种功能范式。或使用Javascript:仅需依靠闭包来规避该语言的某些怪癖。


“闭包的目的只是为了保留状态;因此,闭包的名称-它关闭状态。”:确实取决于语言。闭包关闭外部名称/变量。这些可以表示状态(可以更改的内存位置),但也可以表示值(不可变)。Haskell中的闭包不保留状态:它们保留当前以及在创建闭包的上下文中已知的信息。
Giorgio 2015年

1
»它们会保存当前以及在创建闭包的上下文中已知的信息,无论是否可以更改,它都是状态 -可能是不可变的状态
Thomas Junk

16

闭包主要有两个用例:

  1. 异步性。 假设您要执行需要一段时间的任务,然后在完成后执行一些操作。您可以使代码等待完成,从而阻止进一步的执行并可能使程序无响应,也可以异步调用您的任务并说“在后台开始此长任务,并在完成时执行此关闭操作”,闭包包含完成后要执行的代码。

  2. 回调。 根据语言和平台的不同,它们也称为“代理”或“事件处理程序”。这个想法是,您有一个可自定义的对象,该对象在某些明确定义的点将执行一个event,该事件运行由设置它的代码传入的闭包。例如,在程序的UI中,您可能有一个按钮,并为其提供了一个闭包,其中包含当用户单击按钮时要执行的代码。

闭包还有其他几种用途,但这是两个主要用途。


23
因此,基本上就是回调,因为第一个示例也是回调。
罗伯特·哈维

2
@RobertHarvey:从技术上讲是正确的,但是它们是不同的思维模式。例如,(通常来说)您希望事件处理程序被多次调用,但是异步延续仅被调用一次。但是,是的,从技术上讲,您对闭包所做的任何事情都是回调。(除非您将其转换为表达式树,但这是完全不同的事情。);)
Mason Wheeler 2015年

4
@RobertHarvey:将闭包视为回调将您带入一个思路,这实际上会阻止您有效地使用它们。闭包是类固醇的回调。
gnasher729

13
@MasonWheeler这样说,听起来是错误的。回调与关闭无关。当然,您可以在闭包内回调函数或将闭包作为回调来调用;但是回调不是必需的闭包。回调只是一个简单的函数调用。事件处理程序本身并不是闭包。委托不必是闭包:它主要是C#的函数指针。当然,您可以使用它来构造一个闭包。您的解释不准确。关键是关闭状态并加以利用。
Thomas Junk 2015年

5
也许这里有特定于JS的内涵正在使我误解,但这听起来对我来说绝对是错误的。异步或回调可以使用“可调用”的任何东西。它们都不要求关闭-常规函数会很好。同时,正如Thomas所说,闭包(在函数式编程中定义或在Python中使用)很有用,因为闭包“封闭”了某种状态(即变量),并在内部范围内使函数可以对该变量的变量进行一致的访问。值,即使该函数被调用并退出多次也是如此。
乔纳森·哈特利

13

另外两个例子:

排序
大多数排序功能都是通过比较对象对来进行操作的。需要一些比较技术。将比较限制在特定的运算符上意味着比较不灵活的排序。更好的方法是接收比较函数作为排序函数的参数。有时无状态比较功能可以很好地工作(例如,对数字或名称列表进行排序),但是如果比较需要状态怎么办?

例如,考虑按到某个特定位置的距离对城市列表进行排序。一个丑陋的解决方案是将该位置的坐标存储在全局变量中。这使比较函数本身成为无状态的,但要付出全局变量的代价。

这种方法排除了多个线程同时根据它们到两个不同位置的距离对同一城市列表进行排序的情况。封闭位置的闭包可以解决此问题,并且不需要全局变量。


随机数
原始rand()没有参数。伪随机数生成器需要状态。有些人(例如,梅森·Twister)需要很多状态。甚至是简单但可怕的rand()所需状态。阅读有关新的随机数生成器的数学期刊论文,您将不可避免地看到全局变量。这对于该技术的开发人员来说很不错,而对调用者来说却不太好。将状态封装在结构中并将结构传递给随机数生成器是解决全局数据问题的一种方法。这是许多非OO语言使用的使随机数生成器可重入的方法。闭包对调用者隐藏该状态。闭包提供了简单的调用顺序rand()和封装状态的可重入性。

随机数不只是PRNG。大多数想要随机性的人都希望随机性以某种方式分布。我将从以0到1之间随机抽取的数字开始,或者简称为U(0,1)。任何会产生0到某个最大值之间的整数的PRNG都可以;只需将随机整数除以最大值(作为浮点)即可。一种方便且通用的实现方法是创建一个以闭包(PRNG)和最大值作为输入的闭包。现在,我们有了一个通用且易于使用的U(0,1)随机生成器。

除了U(0,1),还有许多其他分布。例如,具有一定平均值和标准偏差的正态分布。我遇到的每个正态分布生成器算法都使用U(0,1)生成器。创建普通生成器的一种方便且通用的方法是创建一个将U(0,1)生成器,均值和标准偏差封装为状态的闭包。至少从概念上讲,这是一个采用闭包作为参数的闭包。


7

闭包等效于实现run()方法的对象,相反,可以使用闭包来模拟对象。

  • 闭包的优点是可以在您希望使用函数的任何地方轻松使用它们:aka高阶函数,简单的回调(或策略模式)。您无需定义接口/类即可构建临时闭包。

  • 对象的优点是可以进行更复杂的交互:多种方法和/或不同的接口。

因此,使用闭包或对象主要是样式问题。这是一个使闭包变得容易但不方便使用对象实现的示例:

 (let ((seen))
    (defun register-name (name)
       (pushnew name seen :test #'string=))

    (defun all-names ()
       (copy-seq seen))

    (defun reset-name-registry ()
       (setf seen nil)))

基本上,您封装了只能通过全局闭包访问的隐藏状态:您无需引用任何对象,只需使用由三个函数定义的协议即可。


  • 我正在扩展答案,以解决supercat *的这一评论

我相信supercat对以下事实的第一句话:在某些语言中,可以精确地控制对象的生存期,而对于闭包而言,情况并非如此。但是,在使用垃圾收集语言的情况下,对象的生存期通常是不受限制的,因此可以构建一个可以在不应该调用它的动态上下文中调用的闭包(在流之后从闭包读取)例如关闭)。

但是,通过捕获将保护闭包执行的控制变量来防止此类滥用非常简单。更准确地说,这是我的想法(在Common Lisp中):

(defun guarded (function)
  (let ((active t))
    (values (lambda (&rest args)
              (when active
                (apply function args)))
            (lambda ()
              (setf active nil)))))

在这里,我们使用一个函数指定function符并返回两个闭包,它们都捕获了一个名为active

  • 第一个委托给function,仅当active为true时
  • 第二个设定actionnil,又名false

代替(when active ...),当然可以有一个(assert active)表达式,该表达式可能引发异常,以防在不应该调用闭包的情况下调用该闭包。另外,请记住,不安全的代码在使用不当时可能已经自行抛出异常,因此您很少需要这样的包装器。

这是您将如何使用它:

(use-package :metabang-bind) ;; for bind

(defun example (obj1 obj2)
  (bind (((:values f f-deactivator)(guarded (lambda () (do-stuff obj1))))
         ((:values g g-deactivator)(guarded (lambda () (do-thing obj2)))))

    ;; ensure the closure are inactive when we exit
    (unwind-protect
         ;; pass closures to other functions
         (progn
           (do-work f)
           (do-work g))

      ;; cleanup code: deactivate closures
      (funcall f-deactivator)
      (funcall g-deactivator))))

注意,停用的闭包也可以赋予其他功能;这里,局部active变量不共享fg; 另外,除了activef仅指obj1g仅指obj2

supercat提到的另一点是,闭包可能导致内存泄漏,但是不幸的是,对于垃圾回收环境中的几乎所有情况都是如此。如果它们可用,则可以通过弱指针解决(闭包本身可以保留在内存中,但不会阻止对其他资源的垃圾收集)。


1
通常实现的闭包的一个缺点是,一旦使用方法的局部变量之一的闭包暴露给外界,定义闭包的方法就可能失去对何时读写该变量的控制权。在大多数语言中,没有方便的方法来定义短暂的闭包,将其传递给方法,并保证一旦接收到它的方法返回,它就将不复存在,在多线程语言中也没有任何方法可以实现。确保关闭不会在意外的线程上下文中运行。
超级猫

@supercat:看一下Objective-C和Swift。
gnasher729

1
@supercat有状态对象不会存在相同的缺点吗?
Andres F.

@AndresF .:可以在支持失效的包装器中封装有状态对象;如果代码将私有的封装List<T>在(假设的类)中,TemporaryMutableListWrapper<T>并将其公开给外部代码,则可以确保如果使包装无效,则外部代码将不再具有处理的任何方式List<T>。可以设计闭包以使其达到预期目的后允许失效,但是这并不方便。存在闭包以使某些模式方便使用,而保护它们所需的努力将使其无效。
超级猫

1
@coredump:在C#中,如果两个闭包有任何共同的变量,则同一编译器生成的对象将同时服务于这两个对象,因为一个闭包对共享变量所做的更改必须由另一个闭包看到。要避免这种共享,就需要每个闭包都成为自己的对象,该对象拥有自己的非共享变量,并且要引用一个共享对象,该对象拥有共享变量。并非不可能,但是它将为大多数变量访问增加额外的解除引用级别,从而减慢一切。
超级猫

6

还没有说什么,但也许是一个更简单的例子。

这是一个使用超时的JavaScript示例:

// Example function that logs something to the browser's console after a given delay
function delayedLog(message, delay) {
  // this function will be called when the timer runs out
  var fire = function () {
    console.log(message); // closure magic!
  };

  // set a timeout that'll call fire() after a delay
  setTimeout(fire, delay);
}

此处发生的是,当delayedLog()被调用时,它会在设置超时后立即返回,并且该超时在后台不断计时。

但是,当超时超时并调用该fire()函数时,控制台将显示message最初传递给的delayedLog(),因为它仍然可以fire()通过闭包使用。您可以拨打任意数量的电话delayedLog(),并发送不同的消息,每次都可以延迟,这将做对事情。

但是让我们想象一下JavaScript没有闭包。

一种方法是进行setTimeout()阻塞(更像是“睡眠”功能),因此delayedLog()直到超时用完,作用域才会消失。但是阻止一切并不是很好。

另一种方法是将message变量放在其他作用域中,该delayedLog()作用域消失后将可以访问该变量。

您可以使用全局变量,或者至少使用“更广范围的”变量,但是您必须弄清楚如何跟踪超时发生的消息。但这不能只是顺序的FIFO队列,因为您可以设置所需的任何延迟。因此它可能是“先进先出”或类似的东西。因此,您需要其他方法将定时函数与所需的变量绑定在一起。

您可以实例化一个超时对象,该对象将计时器与消息“分组”。一个对象的上下文或多或少是一个可扩展的范围。然后,您将使计时器在对象的上下文中执行,以便它可以访问正确的消息。但是您必须存储该对象,因为如果没有任何引用,它就会被垃圾回收(没有闭包,也就不会有隐式引用)。并且一旦对象触发了超时,就必须将其删除,否则它将一直存在。因此,您需要某种类型的超时对象列表,并定期检查它是否要删除“用过的”对象-否则这些对象会在列表中添加和删除自身,然后...

所以...是的,这变得越来越乏味。

幸运的是,您不必使用更广泛的范围,也不必为保留某些变量而纠缠对象。由于JavaScript具有闭包,因此您已经完全具有所需的作用域。一个使您可以message在需要时访问变量的范围。因此,您可以摆脱delayedLog()上面的写作。


我有一个问题叫闭包。也许我会意外关闭它。message被包含在的功能范围内fire,因此在进一步的调用中被引用;但它确实是意外这么说。从技术上讲,它是一个关闭。无论如何+1;)
Thomas Junk 2015年

@ThomasJunk我不确定我是否会遵循。“ 偶然的关闭”会是什么样子?我在makeadder上面看到了您的示例,在我看来,它看起来几乎相同。您返回一个“ curried”函数,该函数采用一个arg而不是两个;使用相同的方法,我创建了一个接受零参数的函数。我只是不退还而是将其传递给setTimeout
Flambino 2015年

“我只是不退货”也许,那才是关键,这对我来说很重要。我不太清楚表达我的“担忧”;)从技术上来说,您是100%正确的。引用messagein fire生成闭包。当调用setTimeout它时,它利用了保留状态。
Thomas Junk

1
@ThomasJunk我知道它的气味可能有点不同。不过,我可能不会分享您的担心:)或者,无论如何,我都不会将其称为“偶然的”-可以肯定地说,我故意这样做;)
Flambino 2015年

3

PHP可用于帮助以另一种语言显示真实示例。

protected function registerRoutes($dic)
{
  $router = $dic['router'];

  $router->map(['GET','OPTIONS'],'/api/users',function($request,$response) use ($dic)
  {
    $controller = $dic['user_api_controller'];
    return $controller->findAllAction($request,$response);
  })->setName('api_users');
}

所以基本上我正在注册一个将针对/ api / users URI执行的函数。这实际上是一个中间件功能,最终被存储在堆栈中。其他功能将被包装。就像Node.js / Express.js一样。

依赖注入容器可用(通过使用条款)函数里面,当它被调用。可以创建某种路线操作类,但是事实证明,此代码更简单,更快速且更易于维护。


-1

闭合是一块任意代码,包括变量,可以作为第一类数据进行处理。

一个简单的例子是很好的旧qsort:它是对数据进行排序的函数。您必须给它一个指向比较两个对象的函数的指针。因此,您必须编写一个函数。可能需要对该函数进行参数化,这意味着您要给它提供静态变量。这意味着它不是线程安全的。您在DS中。因此,您编写了一个使用闭包而不是函数指针的替代方法。您可以立即解决参数化问题,因为参数已成为闭包的一部分。您使代码更具可读性,因为您编写了如何直接将对象与调用排序功能的代码进行比较。

在很多情况下,您想要执行一些操作,这些操作需要大量的样板代码,以及一些需要修改的微小但必不可少的代码。通过编写一个函数避免了样板代码一次,需要一个封闭的参数和做它周围的所有样板代码,然后你可以调用这个函数,并通过代码来适应一种闭合。一种非常紧凑且易读的代码编写方式。

您具有一个功能,其中需要在许多不同的情况下执行一些非平凡的代码。这通常会产生代码重复或扭曲的代码,因此非平凡的代码将只出现一次。琐碎的:您将闭包分配给变量,并在需要时以最明显的方式调用它。

多线程:iOS / MacOS X具有执行诸如“在后台线程上执行此关闭”,“ ...在主线程上”,“ ...在主线程上(从现在起10秒钟)”之类的功能。它使多线程变得微不足道

异步调用:这就是OP所看到的。任何访问互联网的电话,或任何可能花费时间的事情(例如读取GPS坐标),都是您迫不及待想要得到结果的地方。因此,您具有在后台执行操作的函数,然后传递闭包以告诉他们完成操作时该怎么做。

这是一个小开始。在产生紧凑,可读,可靠和高效的代码方面,闭包具有革命性的五种情况。


-4

闭包是编写要使用的方法的简便方法。它节省了您声明和编写单独方法的工作。当方法仅使用一次且方法定义简短时,此方法很有用。由于无需指定函数名称,返回类型或访问修饰符,因此减少了键入的好处。同样,在阅读代码时,您不必在其他地方查找方法的定义。

以上是Dan Avidar的“理解Lambda表达式”的摘要。

这为我澄清了使用闭包,因为它阐明了替代方法(闭包与方法)以及每种方法的好处。

以下代码仅在安装过程中使用一次。在viewDidLoad下就地编写它可以节省在其他地方搜索它的麻烦,并缩短了代码的大小。

myPhoton!.getVariable("Temp", completion: { (result:AnyObject!, error:NSError!) -> Void in
  if let e = error {
    self.getTempLabel.text = "Failed reading temp"
  } else {
    if let res = result as? Float {
    self.getTempLabel.text = "Temperature is \(res) degrees"
    }
  }
})

另外,它提供了一个异步过程来完成而不阻塞程序的其他部分,并且闭包将保留一个值,以便在后续函数调用中重用。

另一个关闭;这个抓住了一个价值...

let animals = ["fish", "cat", "chicken", "dog"]
let sortedStrings = animals.sorted({ (one: String, two: String) -> Bool in return one > two
}) println(sortedStrings)

4
不幸的是,这混淆了闭包和lambda。Lambda通常使用闭包,因为如果a)非常简洁地定义并且b)在给定方法上下文之内并取决于给定方法上下文(包括变量),它们通常会更有用。但是,实际的闭包与lambda的概念无关,该概念基本上是在适当位置定义一流函数并将其传递以供以后使用的功能。
内森·塔吉

当您投票否决此答案时,请阅读以下内容...我应何时投票?每当您遇到马虎,毫不费力的帖子,或者答案显然是错误的,甚至是危险的错误答案时,请使用下注。我的答案可能缺少使用闭包的某些原因,但它既不算草率也不危险。关于闭包的许多论文和讨论都集中在函数式编程和lambda表示法上。我所有的回答是,对函数式编程的这种解释帮助我理解了闭包。那和持久的价值。
Bendrix

1
答案也许并不危险,但肯定“显然[……]不正确”。对于高度互补并因此经常一起使用的概念,它使用了错误的术语-正是在其中必须清楚区分。如果程序员不加以解决的话,这很可能导致程序员阅读此设计问题。(而且,据我所知,该代码示例仅不包含任何闭包,而只是一个lambda函数,因此无助于解释为什么闭包会有所帮助。)
Nathan Tuggy

内森(Nathan),该代码示例是Swift中的闭包。您可以问别人是否怀疑我。因此,如果是关闭,您会投票赞成吗?
Bendrix 2015年

1
苹果公司相当清楚地在文档中准确地描述了其含义。“闭包是可以独立传递的功能块,可以在代码中传递和使用。Swift中的闭包类似于C和Objective-C中的块以及其他编程语言中的lambda。” 当您了解实现和术语时,可能会造成混淆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.