加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift4.2语言规范(二十五) 自动引用计数(ARC)

发布时间:2020-12-14 05:11:28 所属栏目:百科 来源:网络整理
导读:Swift使用 自动引用计数 (ARC: Automatic Reference Counting)来跟踪和管理应用程序的内存使用情况。 在大多数情况下,这意味着内存管理在Swift中“正常工作”,您不需要自己考虑内存管理。 当不再需要这些实例时,ARC会自动释放类实例使用的内存。 但是,

Swift使用自动引用计数(ARC: Automatic Reference Counting)来跟踪和管理应用程序的内存使用情况。在大多数情况下,这意味着内存管理在Swift中“正常工作”,您不需要自己考虑内存管理。当不再需要这些实例时,ARC会自动释放类实例使用的内存。

但是,在少数情况下,ARC需要有关代码部分之间关系的更多信息,以便为您管理内存。本章介绍了这些情况,并说明了如何启用ARC来管理所有应用程序的内存。在Swift中使用ARC非常类似于将ARC与Objective-C一起使用转换为ARC发行说明中所述的方法

引用计数仅适用于类的实例。结构和枚举是值类型,而不是引用类型,并且不通过引用存储和传递。

ARC是如何工作的

每次创建类的新实例时,ARC都会分配一块内存来存储有关该实例的信息。此内存保存有关实例类型的信息,以及与该实例关联的任何存储属性的值。

此外,当不再需要实例时,ARC释放该实例使用的内存,以便可以将内存用于其他目的。这可确保类实例在不再需要时不会占用内存空间。

但是,如果ARC要释放仍在使用的实例,则将无法再访问该实例的属性,或调用该实例的方法。实际上,如果您尝试访问该实例,您的应用很可能会崩溃。

为了确保实例在仍然需要时不会消失,ARC会跟踪当前引用每个类实例的属性,常量和变量的数量。只要至少有一个对该实例的活动引用仍然存在,ARC就不会释放实例。

为了实现这一点,无论何时将类实例分配给属性,常量或变量,该属性,常量或变量都会对实例进行强引用该引用被称为“强”引用,因为它保持对该实例的坚定持有,并且只要该强引用仍然存在就不允许它被释放。

ARC在行动

以下是自动引用计数如何工作的示例。此示例以一个名为的简单类开头,该类Person定义了一个名为的存储常量属性name

 1 class Person {
 2     let name: String
 3     init(name: String) {
 4         self.name = name
 5         print("(name) is being initialized")
 6     }
 7     deinit {
 8         print("(name) is being deinitialized")
 9     }
10 }

Person类有设置实例的一个初始化name属性并显示一条消息,指示初始化正在进行中。所述Person类还具有当类的实例被释放,打印的消息的deinitializer。

下一个代码片段定义了三个类型的变量Person?,这些变量用于Person在后续代码片段中设置对新实例的多个引用由于这些变量属于可选类型(Person?而不是Person),因此它们会自动使用值初始化nil,并且当前不会引用Person实例。

1 var reference1: Person?
2 var reference2: Person?
3 var reference3: Person?

您现在可以创建一个新Person实例并将其分配给以下三个变量之一:

1 reference1 = Person(name: "John Appleseed")
2 // Prints "John Appleseed is being initialized"

请注意,消息是在您调用类的初始化程序时打印的。这证实已经进行了初始化。"John?Appleseed?is?being?initialized"Person

因为新Person实例已分配给reference1变量,所以现在有一个强引用reference1到新Person实例。因为至少有一个强引用,ARC确保将其Person保留在内存中并且不会被释放。

如果将同一Person实例分配给另外两个变量,则会建立另外两个对该实例的强引用:

1 reference2 = reference1
2 reference3 = reference1

现在有三个对这个单一Person实例的强引用

如果通过分配nil两个变量来中断其中两个强引用(包括原始引用),Person则会保留单个强引用,并且不会释放实例:

1 reference1 = nil
2 reference2 = nil

ARC不会释放Person实例,直到第三个也是最后一个强引用被破坏,此时很明显您不再使用该Person实例:

1 reference3 = nil
2 // Prints "John Appleseed is being deinitialized"

类实例之间的强引用循环

在上面的示例中,ARC能够跟踪Person您创建的新实例的引用数,并在Person不再需要实例时取消分配该实例。

但是,可以编写一个代码,其中类的实例永远不会达到零强引用的程度。如果两个类实例彼此具有强引用,则会发生这种情况,这样每个实例都会使另一个实例保持活动状态。这被称为强引用周期

您可以通过将类之间的某些关系定义为弱引用或无引用而不是强引用来解决强引用循环。解决类实例之间的强引用循环中描述了此过程但是,在您学习如何解决强引用循环之前,了解如何引起这样的循环是有用的。

这是一个如何通过意外创建强引用循环的示例。这个例子定义了两个叫做Person和的Apartment,它们模拟了一套公寓及其居民:

 1 class Person {
 2     let name: String
 3     init(name: String) { self.name = name }
 4     var apartment: Apartment?
 5     deinit { print("(name) is being deinitialized") }
 6 }
 7 
 8 class Apartment {
 9     let unit: String
10     init(unit: String) { self.unit = unit }
11     var tenant: Person?
12     deinit { print("Apartment (unit) is being deinitialized") }
13 }

每个Person实例都有一个nametype属性String和一个apartment最初的可选属性nilapartment物业是可选的,因为一个人可能并不总是有公寓。

类似地,每个Apartment实例都具有unit类型属性,String并且具有tenant最初的可选属性nil租户属性是可选的,因为公寓可能并不总是有租户。

这两个类还定义了一个deinitializer,它打印出该类的一个实例被取消初始化的事实。这使您可以查看是否按预期释放实例PersonApartment

下一个代码片段定义了两个名为john的可选类型变量unit4A,它们将被设置为下面的特定ApartmentPerson实例。nil由于是可选这两个变量都具有初始值

1 var john: Person?
2 var unit4A: Apartment?

您现在可以创建特定的Person实例和Apartment实例,并将这些新实例分配给johnunit4A变量:

1 john = Person(name: "John Appleseed")
2 unit4A = Apartment(unit: "4A")

以下是创建和分配这两个实例后强引用的外观。john变量现在具有对新Person实例的强引用,并且该unit4A变量具有对新Apartment实例的强引用

您现在可以将两个实例链接在一起,以便此人拥有公寓,并且公寓有租户。注意,感叹号(!)用于解开和访问存储在内部的情况下,johnunit4A任选的变量,以便这些实例的属性可设置为:

1 john!.apartment = unit4A
2 unit4A!.tenant = john

以下是将两个实例链接在一起后强引用的外观:

不幸的是,链接这两个实例会在它们之间产生强大的引用周期。Person实例现在具有实例的强引用Apartment,并且Apartment实例具有实例的强引用Person因此,当您断开johnunit4A变量所持有的强引用时,引用计数不会降为零,并且ARC不会释放实例:

1 john = nil
2 unit4A = nil

请注意,将这两个变量设置为时,都不会调用deinitializer?nil强引用循环可防止PersonApartment实例被释放,从而导致应用程序内存泄漏。

以下是在将johnunit4A变量设置为以下后强引用的外观nil

Person实例和Apartment实例之间的强引用仍然存在且无法被破坏。

解决类实例之间的强引用循环

当您使用类类型的属性时,Swift提供了两种解决强引用循环的方法:弱引用和无引用引用。

弱引用和无引用引用使引用周期中的一个实例能够引用另一个实例而不保持强大的保持。然后,实例可以相互引用而不会创建强大的引用周期。

当另一个实例的生命周期更短时,即在可以首先释放另一个实例时,使用弱引用。Apartment上面示例中,公寓在其生命周期中的某个点上没有租户是合适的,因此弱引用是在这种情况下打破引用周期的适当方式。相反,当另一个实例具有相同的生命周期或更长的生命周期时,请使用无主引用。

弱引用

一个弱引用是不保留对实例的强抱它指的是,所以不从引用的实例处置停止ARC的引用。此行为可防止引用成为强引用循环的一部分。通过weak在属性或变量声明之前放置关键字来指示弱引用

因为弱引用不会对它引用的实例保持强大的保持,所以在弱引用仍然引用它的情况下可以释放该实例。因此,ARC会自动设置一个弱引用,以nil指示它所引用的实例何时被释放。并且,因为弱引用需要允许它们的值nil在运行时更改为它们,所以它们总是被声明为可选类型的变量而不是常量。

您可以检查弱引用中是否存在值,就像任何其他可选值一样,并且您永远不会最终得到对不再存在的无效实例的引用。

注意

当ARC设置弱引用时,不会调用属性观察者nil

下面的例子是相同的Person,并Apartment例如从上方,有一个重要区别。这一次,Apartment类型的tenant属性被声明为弱引用:

 1 class Person {
 2     let name: String
 3     init(name: String) { self.name = name }
 4     var apartment: Apartment?
 5     deinit { print("(name) is being deinitialized") }
 6 }
 7 
 8 class Apartment {
 9     let unit: String
10     init(unit: String) { self.unit = unit }
11     weak var tenant: Person?
12     deinit { print("Apartment (unit) is being deinitialized") }
13 }

来自两个变量(johnunit4A的强引用以及两个实例之间的链接如前所述:

1 var john: Person?
2 var unit4A: Apartment?
3 
4 john = Person(name: "John Appleseed")
5 unit4A = Apartment(unit: "4A")
6 
7 john!.apartment = unit4A
8 unit4A!.tenant = john

现在,您将这两个实例链接在一起的引用文件如下:

Person实例仍然具有对该实例的强引用Apartment,但该Apartment实例现在具有对该实例的引用Person这意味着当您通过将john变量设置为断开变量所持有的强引用时nil,不再有对该Person实例的强引用

1 john = nil
2 // Prints "John Appleseed is being deinitialized"

因为没有对Person实例的更强引用,所以它被释放并且tenant属性设置为nil

对该Apartment实例唯一剩下的强引用来自unit4A变量。如果您打破强引用,则不再有对该Apartment实例的强引用

1 unit4A = nil
2 // Prints "Apartment 4A is being deinitialized"

因为没有对该Apartment实例的更强引用,所以它也被取消分配:

注意

在使用垃圾收集的系统中,弱指针有时用于实现简单的缓存机制,因为只有当内存压力触发垃圾收集时才会释放没有强引用的对象。但是,使用ARC时,一旦删除了最后一个强引用,就会释放值,使得弱引用不适用于此类目的。

无主引用

就像一个弱引用一样,无主引用并不会对它所引用的实例保持强势。但是,与弱引用不同,当另一个实例具有相同的生命周期或更长的生命周期时,将使用无主引用。通过unowned在属性或变量声明之前放置关键字来指示无主引用

预期无主引用值始终具有值。因此,ARC永远不会将无主引用的值设置为nil,这意味着使用非可选类型定义无主引用。

重要

仅当您确定引用始终引用尚未释放的实例时,才使用无主引用

如果在取消分配该实例后尝试访问无主引用的值,则会出现运行时错误。

下面的示例定义两个类,Customer并且CreditCard,该模型中的银行客户和可能的信用卡客户。这两个类每个都将另一个类的实例存储为属性。这种关系有可能创建一个强大的引用周期。

之间的关系CustomerCreditCard距离之间的关系略有不同Apartment,并Person在上面的弱引用例所示。在此数据模型中,客户可能拥有或不拥有信用卡,但信用卡将始终与客户相关联。一个CreditCard实例永远不会超过Customer它所指那个。为了表示这一点,Customer该类具有可选card属性,但CreditCard该类具有无主(和非可选)customer属性。

此外,只能通过将值和实例传递给自定义初始值设定项来创建CreditCard实例这可确保实例在创建实例时始终具有与之关联的实例。numbercustomerCreditCardCreditCardcustomerCreditCard

由于信用卡将始终拥有客户,因此您将其customer属性定义为无主引用,以避免强大的引用周期:

 1 class Customer {
 2     let name: String
 3     var card: CreditCard?
 4     init(name: String) {
 5         self.name = name
 6     }
 7     deinit { print("(name) is being deinitialized") }
 8 }
 9 
10 class CreditCard {
11     let number: UInt64
12     unowned let customer: Customer
13     init(number: UInt64,customer: Customer) {
14         self.number = number
15         self.customer = customer
16     }
17     deinit { print("Card #(number) is being deinitialized") }
18 }

注意

该类number属性CreditCard定义为一种类型UInt64而不是Int,以确保该number属性的容量足以在32位和64位系统上存储16位卡号。

下一个代码片段定义了一个Customer名为的可选变量john,该变量将用于存储对特定客户的引用。由于是可选的,该变量的初始值为nil:

1 var john: Customer?

您现在可以创建一个Customer实例,并使用它来初始化并将新CreditCard实例分配为该客户的card属性:

1 john = Customer(name: "John Appleseed")
2 john!.card = CreditCard(number: 1234_5678_9012_3456,customer: john!)

现在您已经链接了两个实例,这是引用的外观:

Customer实例现在具有对该实例的强引用CreditCard,并且该CreditCard实例具有对该实例的无主引用Customer

由于无主customer引用,当您破坏john变量持有的强引用时,不再有对该Customer实例的强引用

因为没有更多对该Customer实例的强引用,所以它已被释放。发生这种情况后,没有更多强大的CreditCard实例引用,它也被解除分配:

1 john = nil
2 // Prints "John Appleseed is being deinitialized"
3 // Prints "Card #1234567890123456 is being deinitialized"

上面的最后一个代码片段显示Customer实例和CreditCard实例的deinitializers在john变量设置为之后都打印了它们的“deinitialized”消息nil

注意

上面的示例显示了如何使用安全的无主引用。对于需要禁用运行时安全检查的情况,Swift还提供不安全的无主引用 - 例如,出于性能原因。与所有不安全的操作一样,您负责检查该代码的安全性。

您通过书面表示不安全的无主引用unowned(unsafe)如果在取消分配引用的实例后尝试访问不安全的无主引用,则程序将尝试访问实例所在的内存位置,这是一种不安全的操作。

无主引用和隐式解包的可选属性

上面的弱和无主引用的例子涵盖了两个更常见的场景,其中必须打破强大的引用周期。

PersonApartment实施例显示的情况下两个属性,这两者都允许为nil,具有引起强烈的基准周期的潜力。使用弱引用可以最好地解决此方案。

CustomerCreditCard实施例显示的情况下被允许是一种性质nil不能为与另一个属性nil有可能造成很强的引用周期。使用无主引用可以最好地解决此方案。

但是,还有第三种情况,其中两个属性应始终具有值,并且nil一旦初始化完成,这两个属性都不应该是。在这种情况下,将一个类上的无主属性与另一个类上隐式解包的可选属性组合起来很有用。

这使得一旦初始化完成就可以直接访问这两个属性(没有可选的解包),同时仍然避免了引用周期。本节介绍如何设置此类关系。

下面的示例定义了两个类,Country并且City每个都将另一个类的实例存储为属性。在这个数据模型中,每个国家都必须始终拥有一个首都,每个城市必须始终属于一个国家。为了表示这一点,Country该类有一个capitalCity属性,City该类有一个country属性:

 1 class Country {
 2     let name: String
 3     var capitalCity: City!
 4     init(name: String,capitalName: String) {
 5         self.name = name
 6         self.capitalCity = City(name: capitalName,country: self)
 7     }
 8 }
 9 
10 class City {
11     let name: String
12     unowned let country: Country
13     init(name: String,country: Country) {
14         self.name = name
15         self.country = country
16     }
17 }

要设置两个类之间的相互依赖性,初始化程序City将获取Country实例,并将此实例存储在其country属性中。

初始化器City初始化器中调用Country但是,初始化Country程序无法传递selfCity初始化程序,直到新Country实例完全初始化为止,如两阶段初始化中所述

要处理此要求,请将capitalCity属性声明Country为隐式展开的可选属性,由其类型注释(City!末尾的感叹号指示这意味着该capitalCity属性的默认值nil与任何其他可选项一样,但可以在不需要打开其值的情况下访问,如隐式解包选项中所述

因为capitalCity具有默认nil值,所以Country只要Country实例name在其初始化程序中设置其属性,就会认为实例已完全初始化。这意味着Country初始化程序可以在设置self属性后立即开始引用并传递隐含属性nameCountry因此初始化程序可以初始化程序设置其自己的属性时作为初始化程序self的参数之一传递CityCountrycapitalCity

所有这些意味着您可以在单个语句中创建CountryCity实例,而无需创建强引用循环,并且capitalCity可以直接访问属性,而无需使用感叹号来解包其可选值:

1 var country = Country(name: "Canada",capitalName: "Ottawa")
2 print("(country.name)‘s capital city is called (country.capitalCity.name)")
3 // Prints "Canada‘s capital city is called Ottawa"

?

在上面的示例中,使用隐式展开的可选项意味着满足所有两阶段类初始化程序要求。capitalCity初始化完成后,可以像非可选值一样使用和访问属性,同时仍然避免强引用周期。

闭包的强引用周期

您在上面看到了当两个类实例属性相互之间具有强引用时,如何创建强引用循环。您还了解了如何使用弱和无主引用来打破这些强大的引用周期。

如果将闭包分配给类实例的属性,并且该闭包的主体捕获实例,则也会发生强引用循环。这种捕获可能是因为闭包的主体访问实例的属性,例如self.someProperty,或者因为闭包调用实例上的方法,例如self.someMethod()在任何一种情况下,这些访问都会导致闭包“捕获”?self,从而创建一个强大的引用周期。

这种强引用循环的发生是因为闭包(如类)是引用类型为属性分配闭包时,您将分配对该闭包引用实质上,它与上面的问题相同 - 两个强引用相互保持活着。但是,这次是一个类实例和一个闭包,而不是两个类实例。

Swift为这个问题提供了一个优雅的解决方案,称为闭包捕获列表但是,在学习如何使用闭包捕获列表打破强引用循环之前,了解如何引起这样的循环是有用的。

下面的示例显示了在使用引用的闭包时如何创建强引用循环self此示例定义了一个名为的类HTMLElement,它为HTML文档中的单个元素提供了一个简单模型:

 1 class HTMLElement {
 2 
 3     let name: String
 4     let text: String?
 5 
 6     lazy var asHTML: () -> String = {
 7         if let text = self.text {
 8             return "<(self.name)>(text)</(self.name)>"
 9         } else {
10             return "<(self.name) />"
11         }
12     }
13 
14     init(name: String,text: String? = nil) {
15         self.name = name
16         self.text = text
17     }
18 
19     deinit {
20         print("(name) is being deinitialized")
21     }
22 
23 }

HTMLElement类定义了一个name属性,该属性指示该元素的名称,诸如"h1"用于一个标题元素,"p"为段落元件,或"br"用于换行元件。HTMLElement还定义了一个可选text属性,您可以将其设置为表示要在该HTML元素中呈现的文本的字符串。

除了这两个简单属性之外,HTMLElement该类还定义了一个名为的惰性属性asHTML此属性引用,结合了封闭nametext成HTML字符串片段。所述asHTML属性的类型的,或“不带参数,并返回一个函数值”。()?->?StringString

默认情况下,为asHTML属性分配一个闭包,该闭包返回HTML标记的字符串表示形式。此标记包含可选text值(如果存在),或者不包含文本内容(如果text不存在)。对于段落元素,闭包将返回取决于属性是否等于"<p>some?text</p>""<p?/>"text"some?text"nil

asHTML属性的命名和使用方式有点像实例方法。但是,因为asHTML是闭包属性而不是实例方法,所以asHTML如果要更改特定HTML元素的HTML呈现,则可以使用自定义闭包替换属性的默认值

例如,asHTML属性可以设置为封闭默认为一些文字,如果text属性nil,以防止返回一个空的HTML标签表示:

1 let heading = HTMLElement(name: "h1")
2 let defaultText = "some default text"
3 heading.asHTML = {
4     return "<(heading.name)>(heading.text ?? defaultText)</(heading.name)>"
5 }
6 print(heading.asHTML())
7 // Prints "<h1>some default text</h1>"

注意

asHTML属性被声明为一个惰性属性,因为只有在元素实际需要呈现为某个HTML输出目标的字符串值时才需要它。asHTML作为惰性属性的事实意味着您可以self在默认闭包内引用,因为在初始化完成并且self已知存在之后才会访问惰性属性

HTMLElement类提供了一个单一的初始值设定,这需要一个name参数,并(如果需要的话)一个text参数来初始化新的元件。该类还定义了一个deinitializer,它打印一条消息以显示HTMLElement实例何时被释放。

以下是使用HTMLElement该类创建和打印新实例的方法:

1 var paragraph: HTMLElement? = HTMLElement(name: "p",text: "hello,world")
2 print(paragraph!.asHTML())
3 // Prints "<p>hello,world</p>"

注意

paragraph上面变量被定义为可选?HTMLElement,因此可以将其设置为nil下面以证明存在强引用循环。

不幸的是,HTMLElement如上所述,该类在HTMLElement实例和用于其默认asHTML的闭包之间创建了一个强引用循环这是循环的样子:

实例的asHTML属性强烈引用它的闭包。然而,因为封闭件是指self它的主体内(作为一种方式来引用self.nameself.text),封闭捕获自,这意味着它保持很强的引用回HTMLElement实例。在两者之间创建了强大的引用周期。(有关在闭包中捕获值的更多信息,请参阅捕获值。)

注意

即使闭包self多次引用,它也只捕获对该HTMLElement实例的一个强引用

如果将paragraph变量设置nil并断开其对HTMLElement实例的强引用,则实例HTMLElement及其闭包都不会被释放,因为强引用周期:

paragraph = nil

请注意,HTMLElement不会打印deinitializer?中的消息,这表示HTMLElement实例未取消分配。

解决闭包的强引用周期

通过将捕获列表定义为闭包定义的一部分,可以解决闭包和类实例之间的强引用循环捕获列表定义在闭包体内捕获一个或多个引用类型时要使用的规则。与两个类实例之间的强引用循环一样,您将每个捕获的引用声明为弱引用或无主引用,而不是强引用。弱或无主的适当选择取决于代码的不同部分之间的关??系。

注意

每当你引用一个闭包中的成员时,Swift都要求你写self.somePropertyself.someMethod()(而不仅仅是somePropertysomeMethod()self这有助于您记住,可能会self意外捕获

定义捕获列表

捕获列表中的每个项目都是weakor?unowned关键字与类实例(例如self的引用或使用某个值(例如初始化的变量的配对这些配对写在一对方括号内,用逗号分隔。delegate?=?self.delegate!

将捕获列表放在闭包的参数列表之前,如果提供了它们,则返回类型:

1 lazy var someClosure: (Int,String) -> String = {
2     [unowned self,weak delegate = self.delegate!] (index: Int,stringToProcess: String) -> String in
3     // closure body goes here
4 }

如果闭包没有指定参数列表或返回类型,因为它们可以从上下文中推断出来,请将捕获列表放在闭包的最开头,然后是in关键字:

1 lazy var someClosure: () -> String = {
2     [unowned self,weak delegate = self.delegate!] in
3     // closure body goes here
4 }

弱和无主引用

当闭包和它捕获的实例将始终相互引用时,将闭包中的捕获定义为无主引用,并且将始终同时取消分配。

相反,当捕获的引用可能nil在将来的某个时刻出现时,将捕获定义为弱引用弱引用始终是可选类型,并且nil在它们引用的实例被释放时自动变为这使您可以检查它们在闭包体内是否存在。

注意

如果捕获的引用永远不会变为nil,则应始终将其捕获为无主引用,而不是弱引用。

无主引用是用于解决上面的闭包的强引用周期的HTMLElement示例中的强引用周期的适当捕获方法以下是编写类以避免循环的方法:HTMLElement

 1 class HTMLElement {
 2 
 3     let name: String
 4     let text: String?
 5 
 6     lazy var asHTML: () -> String = {
 7         [unowned self] in
 8         if let text = self.text {
 9             return "<(self.name)>(text)</(self.name)>"
10         } else {
11             return "<(self.name) />"
12         }
13     }
14 
15     init(name: String,text: String? = nil) {
16         self.name = name
17         self.text = text
18     }
19 
20     deinit {
21         print("(name) is being deinitialized")
22     }
23 
24 }

HTMLElement除了在asHTML闭包内添加捕获列表之外,此实现与先前的实现相同在这种情况下,捕获列表是,这意味着“将自己捕获为无主引用而非强引用”。[unowned?self]

您可以HTMLElement像以前一样创建和打印实例:

1 var paragraph: HTMLElement? = HTMLElement(name: "p",world</p>"

以下是引用文件在捕获列表中的显示方式:

这一次,self闭包的捕获是一个无主的引用,并没有HTMLElement对它捕获实例保持强有力的控制如果您将paragraph变量的强引用设置nil,则HTMLElement实例将被释放,从下面示例中打印的deinitializer消息可以看出:

1 paragraph = nil
2 // Prints "p is being deinitialized"

有关捕获列表的详细信息,请参阅捕获列表

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读