Automatic Initializer Inheritance (构造器的自动继承)
As mentioned above, subclasses do not inherit their superclass initializers by default. However, superclass initializersareautomatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
如上所述,子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
Rule 2
If your subclass provides an implementation ofallof its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
These rules apply even if your subclass adds further convenience initializers.
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
NOTE
A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.
对于规则 2,子类可以将父类的指定构造器实现为便利构造器。
Designated and Convenience Initializers in Action (指定构造器和便利构造器实践)
The following example shows designated initializers, convenience initializers, and automatic initializer inheritance in action. This example defines a hierarchy of three classes calledFood,RecipeIngredient, andShoppingListItem, and demonstrates how their initializers interact.
接下来的例子将在实践中展示指定构造器、便利构造器以及构造器的自动继承。这个例子定义了包含三个类Food、RecipeIngredient以及ShoppingListItem的类层次结构,并将演示它们的构造器是如何相互作用的。
The base class in the hierarchy is calledFood, which is a simple class to encapsulate the name of a foodstuff. TheFoodclass introduces a singleStringproperty callednameand provides two initializers for creatingFoodinstances:
类层次中的基类是Food,它是一个简单的用来封装食物名字的类。Food类引入了一个叫做name的String类型的属性,并且提供了两个构造器来创建Food实例:
class Food{
var name:String
init(name:String) {
self.name=name
}
convenience init() {
self.init(name:"[Unnamed]")
}
}
The figure below shows the initializer chain for theFoodclass:
下图中展示了Food的构造器链:
Classes do not have a default memberwise initializer, and so theFoodclass provides a designated initializer that takes a single argument calledname. This initializer can be used to create a newFoodinstance with a specific name:
类类型没有默认的逐一成员构造器,所以Food类提供了一个接受单一参数name的指定构造器。这个构造器可以使用一个特定的名字来创建新的Food实例:
let namedMeat=Food(name:"Bacon")
// namedMeat's name is "Bacon"
Theinit(name: String)initializer from theFoodclass is provided as adesignatedinitializer, because it ensures that all stored properties of a newFoodinstance are fully initialized. TheFoodclass does not have a superclass, and so theinit(name: String)initializer does not need to callsuper.init()to complete its initialization.
Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能确保Food实例的所有存储型属性都被初始化。Food类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造过程。
TheFoodclass also provides aconvenienceinitializer,init(), with no arguments. Theinit()initializer provides a default placeholder name for a new food by delegating across to theFoodclass’sinit(name: String)with anamevalue of[Unnamed]:
Food类同样提供了一个没有参数的便利构造器init()。这个init()构造器为新食物提供了一个默认的占位名字,通过横向代理到指定构造器init(name: String)并给参数name传值[Unnamed]来实现:
let mysteryMeat=Food()
// mysteryMeat's name is "[Unnamed]"
The second class in the hierarchy is a subclass ofFoodcalledRecipeIngredient. TheRecipeIngredientclass models an ingredient in a cooking recipe. It introduces anIntproperty calledquantity(in addition to thenameproperty it inherits fromFood) and defines two initializers for creatingRecipeIngredientinstances:
类层级中的第二个类是Food的子类RecipeIngredient。RecipeIngredient类用来表示食谱中的一项原料。它引入了Int类型的属性quantity(以及从Food继承过来的name属性),并且定义了两个构造器来创建RecipeIngredient实例:
class RecipeIngredient:Food{
var quantity:Int
init (name:String,quantity:Int) {
self.quantity=quantity
super.init(name:name)
}
override convenience init(name:String) {
self.init(name:name,quantity:1)
}
}
The figure below shows the initializer chain for theRecipeIngredientclass:
下图中展示了RecipeIngredient类的构造器链:
TheRecipeIngredientclass has a single designated initializer,init(name: String, quantity: Int), which can be used to populate all of the properties of a newRecipeIngredientinstance. This initializer starts by assigning the passedquantityargument to thequantityproperty, which is the only new property introduced byRecipeIngredient. After doing so, the initializer delegates up to theinit(name: String)initializer of theFoodclass. This process satisfies safety check 1 fromTwo-Phase Initializationabove.
RecipeIngredient类拥有一个指定构造器init(name: String, quantity: Int),它可以用来填充RecipeIngredient实例的所有属性值。这个构造器一开始先将传入的quantity参数赋值给quantity属性,这个属性也是唯一在RecipeIngredient中新引入的属性。随后,构造器向上代理到父类Food的init(name: String)。这个过程满足两段式构造过程中的安全检查 1。
RecipeIngredientalso defines a convenience initializer,init(name: String), which is used to create aRecipeIngredientinstance by name alone. This convenience initializer assumes a quantity of1for anyRecipeIngredientinstance that is created without an explicit quantity. The definition of this convenience initializer makesRecipeIngredientinstances quicker and more convenient to create, and avoids code duplication when creating several single-quantityRecipeIngredientinstances. This convenience initializer simply delegates across to the class’s designated initializer, passing in aquantityvalue of1.
RecipeIngredient还定义了一个便利构造器init(name: String),它只通过name来创建RecipeIngredient的实例。这个便利构造器假设任意RecipeIngredient实例的quantity为1,所以不需要显式指明数量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个quantity为1的RecipeIngredient实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为quantity参数传递1。
Theinit(name: String)convenience initializer provided byRecipeIngredienttakes the same parameters as theinit(name: String)designatedinitializer fromFood. Because this convenience initializer overrides a designated initializer from its superclass, it must be marked with theoverridemodifier (as described inInitializer Inheritance and Overriding).
注意,RecipeIngredient的便利构造器init(name: String)使用了跟Food中指定构造器init(name: String)相同的参数。由于这个便利构造器重写了父类的指定构造器init(name: String),因此必须在前面使用override修饰符(参见构造器的继承和重写)。
Even thoughRecipeIngredientprovides theinit(name: String)initializer as a convenience initializer,RecipeIngredienthas nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore,RecipeIngredientautomatically inherits all of its superclass’s convenience initializers too.
尽管RecipeIngredient将父类的指定构造器重写为了便利构造器,它依然提供了父类的所有指定构造器的实现。因此,RecipeIngredient会自动继承父类的所有便利构造器。
In this example, the superclass forRecipeIngredientisFood, which has a single convenience initializer calledinit(). This initializer is therefore inherited byRecipeIngredient. The inherited version ofinit()functions in exactly the same way as theFoodversion, except that it delegates to theRecipeIngredientversion ofinit(name: String)rather than theFoodversion.
在这个例子中,RecipeIngredient的父类是Food,它有一个便利构造器init()。这个便利构造器会被RecipeIngredient继承。这个继承版本的init()在功能上跟Food提供的版本是一样的,只是它会代理到RecipeIngredient版本的init(name: String)而不是Food提供的版本。
All three of these initializers can be used to create newRecipeIngredientinstances:
所有的这三种构造器都可以用来创建新的RecipeIngredient实例:
let oneMysteryItem=RecipeIngredient()
let oneBacon=RecipeIngredient(name:"Bacon")
let sixEggs=RecipeIngredient(name:"Eggs",quantity:6)
The third and final class in the hierarchy is a subclass ofRecipeIngredientcalledShoppingListItem. TheShoppingListItemclass models a recipe ingredient as it appears in a shopping list.
类层级中第三个也是最后一个类是RecipeIngredient的子类,叫做ShoppingListItem。这个类构建了购物单中出现的某一种食谱原料。
Every item in the shopping list starts out as “unpurchased”. To represent this fact,ShoppingListItemintroduces a Boolean property calledpurchased, with a default value offalse.ShoppingListItemalso adds a computeddescriptionproperty, which provides a textual description of aShoppingListIteminstance:
购物单中的每一项总是从未购买状态开始的。为了呈现这一事实,ShoppingListItem引入了一个布尔类型的属性purchased,它的默认值是false。ShoppingListItem还添加了一个计算型属性description,它提供了关于ShoppingListItem实例的一些文字描述:
class ShoppingListItem:RecipeIngredient{
var purchased=false
var description:String{
var output="\(quantity)x\(name)"
output+=purchased?" ✔":" ✘"
return output
}
}
NOTE
ShoppingListItemdoes not define an initializer to provide an initial value forpurchased, because items in a shopping list (as modeled here) always start out unpurchased.
ShoppingListItem没有定义构造器来为purchased提供初始值,因为添加到购物单的物品的初始状态总是未购买。
Because it provides a default value for all of the properties it introduces and does not define any initializers itself,ShoppingListItemautomatically inheritsallof the designated and convenience initializers from its superclass.
由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,ShoppingListItem将自动继承所有父类中的指定构造器和便利构造器。
The figure below shows the overall initializer chain for all three classes:
下图展示了这三个类的构造器链:
You can use all three of the inherited initializers to create a newShoppingListIteminstance:
你可以使用全部三个继承来的构造器来创建ShoppingListItem的新实例:
var breakfastList= [
ShoppingListItem(),
ShoppingListItem(name:"Bacon"),
ShoppingListItem(name:"Eggs",quantity:6),
]
breakfastList[0].name="Orange juice"
breakfastList[0].purchased=true
for item in breakfastList{
print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
Here, a new array calledbreakfastListis created from an array literal containing three newShoppingListIteminstances. The type of the array is inferred to be[ShoppingListItem]. After the array is created, the name of theShoppingListItemat the start of the array is changed from"[Unnamed]"to"Orange juice"and it is marked as having been purchased. Printing the description of each item in the array shows that their default states have been set as expected.
如上所述,例子中通过字面量方式创建了一个数组breakfastList,它包含了三个ShoppingListItem实例,因此数组的类型也能被自动推导为[ShoppingListItem]。在数组创建完之后,数组中第一个ShoppingListItem实例的名字从[Unnamed]更改为Orange juice,并标记为已购买。打印数组中每个元素的描述显示了它们都已按照预期被赋值。
Failable Initializers (可失败构造器)
It is sometimes useful to define a class, structure, or enumeration for which initialization can fail. This failure might be triggered by invalid initialization parameter values, the absence of a required external resource, or some other condition that prevents initialization from succeeding.
如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
To cope with initialization conditions that can fail, define one or more failable initializers as part of a class, structure, or enumeration definition. You write a failable initializer by placing a question mark after theinitkeyword (init?).
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面添加问号(init?)。
NOTE
You cannot define a failable and a nonfailable initializer with the same parameter types and names.
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
A failable initializer creates anoptionalvalue of the type it initializes. You writereturn nilwithin a failable initializer to indicate a point at which initialization failure can be triggered.
可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”。
NOTE
Strictly speaking, initializers do not return a value. Rather, their role is to ensure thatselfis fully and correctly initialized by the time that initialization ends. Although you writereturn nilto trigger an initialization failure, you do not use thereturnkeyword to indicate initialization success.
严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。
The example below defines a structure calledAnimal, with a constantStringproperty calledspecies. TheAnimalstructure also defines a failable initializer with a single parameter calledspecies. This initializer checks if thespeciesvalue passed to the initializer is an empty string. If an empty string is found, an initialization failure is triggered. Otherwise, thespeciesproperty’s value is set, and initialization succeeds:
下例中,定义了一个名为Animal的结构体,其中有一个名为species的String类型的常量属性。同时该结构体还定义了一个接受一个名为species的String类型参数的可失败构造器。这个可失败构造器检查传入的参数是否为一个空字符串。如果为空字符串,则构造失败。否则,species属性被赋值,构造成功。
struct Animal{
let species:String
init?(species:String) {
if species.isEmpty{returnnil}
self.species=species
}
}
You can use this failable initializer to try to initialize a newAnimalinstance and to check if initialization succeeded:
你可以通过该可失败构造器来构建一个Animal的实例,并检查构造过程是否成功:
let someCreature=Animal(species:"Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe=someCreature{
print("An animal was initialized with a species of\(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
If you pass an empty string value to the failable initializer’sspeciesparameter, the initializer triggers an initialization failure:
如果你给该可失败构造器传入一个空字符串作为其参数,则会导致构造失败:
let anonymousCreature=Animal(species:"")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature==nil{
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
NOTE
Checking for an empty string value (such as""rather than"Giraffe") is not the same as checking fornilto indicate the absence of anoptionalStringvalue. In the example above, an empty string ("") is a valid, nonoptionalString. However, it is not appropriate for an animal to have an empty string as the value of itsspeciesproperty. To model this restriction, the failable initializer triggers an initialization failure if an empty string is found.
空字符串(如"",而不是"Giraffe")和一个值为nil的可选类型的字符串是两个完全不同的概念。上例中的空字符串("")其实是一个有效的,非可选类型的字符串。这里我们之所以让Animal的可失败构造器构造失败,只是因为对于Animal这个类的species属性来说,它更适合有一个具体的值,而不是空字符串。
Failable Initializers for Enumerations (枚举类型的可失败构造器)
You can use a failable initializer to select an appropriate enumeration case based on one or more parameters. The initializer can then fail if the provided parameters do not match an appropriate enumeration case.
你可以通过一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。如果提供的参数无法匹配任何枚举成员,则构造失败。
The example below defines an enumeration calledTemperatureUnit, with three possible states (kelvin,celsius, andfahrenheit). A failable initializer is used to find an appropriate enumeration case for aCharactervalue representing a temperature symbol:
下例中,定义了一个名为TemperatureUnit的枚举类型。其中包含了三个可能的枚举成员(Kelvin,Celsius,和Fahrenheit),以及一个根据Character值找出所对应的枚举成员的可失败构造器:
enum TemperatureUnit{
case kelvin,celsius,fahrenheit
init?(symbol:Character) {
switch symbol{
case"K":
self= .kelvin
case"C":
self= .celsius
case"F":
self= .fahrenheit
default:
return nil
}
}
}
You can use this failable initializer to choose an appropriate enumeration case for the three possible states and to cause initialization to fail if the parameter does not match one of these states:
你可以利用该可失败构造器在三个枚举成员中获取一个相匹配的枚举成员,当参数的值不能与任何枚举成员相匹配时,则构造失败:
let fahrenheitUnit=TemperatureUnit(symbol:"F")
if fahrenheitUnit!=nil{
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit=TemperatureUnit(symbol:"X")
if unknownUnit==nil{
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
Failable Initializers for Enumerations with Raw Values (带原始值的枚举类型的可失败构造器)
Enumerations with raw values automatically receive a failable initializer,init?(rawValue:), that takes a parameter calledrawValueof the appropriate raw-value type and selects a matching enumeration case if one is found, or triggers an initialization failure if no matching value exists.
带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。
You can rewrite theTemperatureUnitexample from above to use raw values of typeCharacterand to take advantage of theinit?(rawValue:)initializer:
因此上面的TemperatureUnit的例子可以重写为:
enum TemperatureUnit:Character{
case kelvin="K",celsius="C",fahrenheit="F"
}
let fahrenheitUnit=TemperatureUnit(rawValue:"F")
if fahrenheitUnit!=nil{
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit=TemperatureUnit(rawValue:"X")
if unknownUnit==nil{
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
Propagation of Initialization Failure (构造失败的传递)
A failable initializer of a class, structure, or enumeration can delegate across to another failable initializer from the same class, structure, or enumeration. Similarly, a subclass failable initializer can delegate up to a superclass failable initializer.
类,结构体,枚举的可失败构造器可以横向代理到类型中的其他可失败构造器。类似的,子类的可失败构造器也能向上代理到父类的可失败构造器。
In either case, if you delegate to another initializer that causes initialization to fail, the entire initialization process fails immediately, and no further initialization code is executed.
无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。
NOTE
A failable initializer can also delegate to a nonfailable initializer. Use this approach if you need to add a potential failure state to an existing initialization process that does not otherwise fail.
可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。
The example below defines a subclass ofProductcalledCartItem. TheCartItemclass models an item in an online shopping cart.CartItemintroduces a stored constant property calledquantityand ensures that this property always has a value of at least1:
下面这个例子,定义了一个名为CartItem的Product类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为quantity的常量存储型属性,并确保该属性的值至少为1:
class Product{
let name:String
init?(name:String) {
if name.isEmpty{returnnil}
self.name=name
}
}
class CartItem:Product{
let quantity:Int
init?(name:String,quantity:Int) {
if quantity<1{returnnil}
self.quantity=quantity
super.init(name:name)
}
}
The failable initializer forCartItemstarts by validating that it has received aquantityvalue of1or more. If thequantityis invalid, the entire initialization process fails immediately and no further initialization code is executed. Likewise, the failable initializer forProductchecks thenamevalue, and the initializer process fails immediately ifnameis the empty string.
CartItem可失败构造器首先验证接收的quantity值是否大于等于 1 。倘若quantity值无效,则立即终止整个构造过程,返回失败结果,且不再执行余下代码。同样地,Product的可失败构造器首先检查name值,假如name值为空字符串,则构造器立即执行失败。
If you create aCartIteminstance with a nonempty name and a quantity of1or more, initialization succeeds:
如果你通过传入一个非空字符串name以及一个值大于等于 1 的quantity来创建一个CartItem实例,那么构造方法能够成功被执行:
if let twoSocks=CartItem(name:"sock",quantity:2) {
print("Item:\(twoSocks.name), quantity:\(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
If you try to create aCartIteminstance with aquantityvalue of0, theCartIteminitializer causes initialization to fail:
倘若你以一个值为 0 的quantity来创建一个CartItem实例,那么将导致CartItem构造器失败:
if let zeroShirts=CartItem(name:"shirt",quantity:0) {
print("Item:\(zeroShirts.name), quantity:\(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Prints "Unable to initialize zero shirts"
Similarly, if you try to create aCartIteminstance with an emptynamevalue, the superclassProductinitializer causes initialization to fail:
同样地,如果你尝试传入一个值为空字符串的name来创建一个CartItem实例,那么将导致父类Product的构造过程失败:
if let oneUnnamed=CartItem(name:"",quantity:1) {
print("Item:\(oneUnnamed.name), quantity:\(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Prints "Unable to initialize one unnamed product"
Overriding a Failable Initializer (重写一个可失败构造器)
You can override a superclass failable initializer in a subclass, just like any other initializer. Alternatively, you can override a superclass failable initializer with a subclassnonfailableinitializer. This enables you to define a subclass for which initialization cannot fail, even though initialization of the superclass is allowed to fail.
如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。
Note that if you override a failable superclass initializer with a nonfailable subclass initializer, the only way to delegate up to the superclass initializer is to force-unwrap the result of the failable superclass initializer.
注意,当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。
NOTE
You can override a failable initializer with a nonfailable initializer but not the other way around.
你可以用非可失败构造器重写可失败构造器,但反过来却不行。
The example below defines a class calledDocument. This class models a document that can be initialized with anameproperty that is either a nonempty string value ornil, but cannot be an empty string:
下例定义了一个名为Document的类,name属性的值必须为一个非空字符串或nil,但不能是一个空字符串:
class Document{
var name:String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name:String) {
if name.isEmpty{returnnil}
self.name=name
}
}
The next example defines a subclass ofDocumentcalledAutomaticallyNamedDocument. TheAutomaticallyNamedDocumentsubclass overrides both of the designated initializers introduced byDocument. These overrides ensure that anAutomaticallyNamedDocumentinstance has an initialnamevalue of"[Untitled]"if the instance is initialized without a name, or if an empty string is passed to theinit(name:)initializer:
下面这个例子,定义了一个Document类的子类AutomaticallyNamedDocument。这个子类重写了父类的两个指定构造器,确保了无论是使用init()构造器,还是使用init(name:)构造器并为参数传递空字符串,生成的实例中的name属性总有初始"[Untitled]":
class AutomaticallyNamedDocument:Document{
override init() {
super.init()
self.name="[Untitled]"
}
override init(name:String) {
super.init()
if name.isEmpty{
self.name="[Untitled]"
} else {
self.name=name
}
}
}
TheAutomaticallyNamedDocumentoverrides its superclass’s failableinit?(name:)initializer with a nonfailableinit(name:)initializer. BecauseAutomaticallyNamedDocumentcopes with the empty string case in a different way than its superclass, its initializer does not need to fail, and so it provides a nonfailable version of the initializer instead.
AutomaticallyNamedDocument用一个非可失败构造器init(name:)重写了父类的可失败构造器init?(name:)。因为子类用另一种方式处理了空字符串的情况,所以不再需要一个可失败构造器,因此子类用一个非可失败构造器代替了父类的可失败构造器。
You can use forced unwrapping in an initializer to call a failable initializer from the superclass as part of the implementation of a subclass’s nonfailable initializer. For example, theUntitledDocumentsubclass below is always named"[Untitled]", and it uses the failableinit(name:)initializer from its superclass during initialization.
你可以在子类的非可失败构造器中使用强制解包来调用父类的可失败构造器。比如,下面的UntitledDocument子类的name属性的值总是"[Untitled]",它在构造过程中使用了父类的可失败构造器init?(name:):
class UntitledDocument:Document{
override init() {
super.init(name:"[Untitled]")!
}
}
In this case, if theinit(name:)initializer of the superclass were ever called with an empty string as the name, the forced unwrapping operation would result in a runtime error. However, because it’s called with a string constant, you can see that the initializer won’t fail, so no runtime error can occur in this case.
在这个例子中,如果在调用父类的可失败构造器init?(name:)时传入的是空字符串,那么强制解包操作会引发运行时错误。不过,因为这里是通过非空的字符串常量来调用它,所以并不会发生运行时错误。
The init! Failable Initializer (可失败构造器 init!)
You typically define a failable initializer that creates an optional instance of the appropriate type by placing a question mark after theinitkeyword (init?). Alternatively, you can define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. Do this by placing an exclamation mark after theinitkeyword (init!) instead of a question mark.
通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
You can delegate frominit?toinit!and vice versa, and you can overrideinit?withinit!and vice versa. You can also delegate frominittoinit!, although doing so will trigger an assertion if theinit!initializer causes initialization to fail.
你可以在init?中代理到init!,反之亦然。你也可以用init?重写init!,反之亦然。你还可以用init代理到init!,不过,一旦init!构造失败,则会触发一个断言。
Required Initializers (必要构造器)
Write therequiredmodifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:
在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器:
class SomeClass{
required init() {
// initializer implementation goes here
}
}
You must also write therequiredmodifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain. You do not write theoverridemodifier when overriding a required designated initializer:
在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符:
class SomeSubclass:SomeClass{
required init() {
// subclass implementation of the required initializer goes here
}
}
NOTE
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。
Setting a Default Property Value with a Closure or Function (通过闭包或函数设置属性的默认值)
If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.
如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
These kinds of closures or functions typically create a temporary value of the same type as the property, tailor that value to represent the desired initial state, and then return that temporary value to be used as the property’s default value.
这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。
Here’s a skeleton outline of how a closure can be used to provide a default property value:
下面介绍了如何用闭包为属性提供默认值:
class SomeClass{
let someProperty:SomeType= {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
NOTE
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicitselfproperty, or call any of the instance’s methods.
如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的self属性,或者调用任何实例方法。
The example below defines a structure calledChessboard, which models a board for the game of chess. Chess is played on an 8 x 8 board, with alternating black and white squares.
下面例子中定义了一个结构体Checkerboard,它构建了西洋跳棋游戏的棋盘:
To represent this game board, theChessboardstructure has a single property calledboardColors, which is an array of 64Boolvalues. A value oftruein the array represents a black square and a value offalserepresents a white square. The first item in the array represents the top left square on the board and the last item in the array represents the bottom right square on the board.
西洋跳棋游戏在一副黑白格交替的10x10的棋盘中进行。为了呈现这副游戏棋盘,Checkerboard结构体定义了一个属性boardColors,它是一个包含100个Bool值的数组。在数组中,值为true的元素表示一个黑格,值为false的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
TheboardColorsarray is initialized with a closure to set up its color values:
boardColor数组是通过一个闭包来初始化并设置颜色值的:
struct Chessboard{
let boardColors: [Bool] = {
var temporaryBoard= [Bool]()
var isBlack=false
for i in1...8{
forjin1...8{
temporaryBoard.append(isBlack)
isBlack= !isBlack
}
isBlack= !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row:Int,column:Int) ->Bool{
return boardColors[(row*8) +column]
}
}
Whenever a newChessboardinstance is created, the closure is executed, and the default value ofboardColorsis calculated and returned. The closure in the example above calculates and sets the appropriate color for each square on the board in a temporary array calledtemporaryBoard, and returns this temporary array as the closure’s return value once its setup is complete. The returned array value is stored inboardColorsand can be queried with thesquareIsBlackAtRowutility function:
每当一个新的Checkerboard实例被创建时,赋值闭包会被执行,boardColors的默认值会被计算出来并返回。上面例子中描述的闭包将计算出棋盘中每个格子对应的颜色,并将这些值保存到一个临时数组temporaryBoard中,最后在构建完成时将此数组作为闭包返回值返回。这个返回的数组会保存到boardColors中,并可以通过工具函数squareIsBlackAtRow来查询:
let board=Chessboard()
print(board.squareIsBlackAt(row:0,column:1))
// Prints "true"
print(board.squareIsBlackAt(row:7,column:7))
// Prints "false"