Swift クラスは、コードを構築するための一般的で柔軟な構造です。
クラスのプロパティ (定数、変数) とメソッドを定義できます。
他のプログラミング言語とは異なり、Swift ではカスタム クラス用に個別のインターフェイス ファイルと実装ファイルを作成する必要がありません。単一のファイルでクラスを定義するだけで、システムが他のコードへの外部インターフェイスを自動的に生成します。
クラスと構造体の比較
Swift のクラスと構造体には多くの共通点があります。彼らの共通点は次のとおりです。
- 値を保存するプロパティを定義する
- 機能を提供するメソッドを定義する
- 値にアクセスするための補助スクリプトを定義する
- 初期化値を生成するコンストラクターを定義する
- デフォルト実装の機能を拡張する拡張機能
- プロトコルに準拠してクラスの標準機能を提供する
構造体と比較して、クラスには次の追加機能があります。
- 継承により、あるクラスが別のクラスの特性を継承できるようになります。
- 型変換により、クラス インスタンスの型を実行時に検査して解釈できるようになります。
- デストラクターを使用すると、クラス インスタンスが割り当てられたリソースを解放できるようになります。
- 参照カウントにより、クラスへの複数の参照が可能になります
文法:
class クラス名 {
定義 1
定義 2
……
定義 N
}
クラス定義
クラス学生{
var スタッド名: String
var mark: Int
var mark2: Int
}
クラスをインスタンス化します:
スタッドレコード = Student() にしましょう
クラスのプロパティに参照型としてアクセスする
クラスのプロパティに は、を介してアクセスできます 。形式は次のとおりです:インスタンス化クラス名.プロパティ名:
アイデンティティ演算子
クラスは参照型であるため、複数の定数と変数がバックグラウンドで特定のクラス インスタンスを同時に参照することが可能です。
2 つの定数または変数が同じクラス インスタンスを参照しているかどうかを判断するために、Swift には 2 つの組み込みの恒等演算子があります。
アイデンティティ演算子 | 不等演算子 |
---|---|
演算子は次のとおりです: === | 演算子は: !== |
2 つの定数または変数が同じクラス インスタンスを参照している場合に true を返します。 | 2 つの定数または変数が異なるクラス インスタンスを参照している場合は true を返します。 |
例
import Cocoa class SampleClass: Equatable { let myProperty: String init(s: String) { myProperty = s } } func ==(lhs: SampleClass, rhs: SampleClass) -> Bool { return lhs.myProperty == rhs.myProperty } let spClass1 = SampleClass(s: "Hello") let spClass2 = SampleClass(s: "Hello") if spClass1 === spClass2 {// false print("同じクラス インスタンスを参照\(spClass1)") } if spClass1 ! = = spClass2 {// true print("異なるクラス インスタンスを参照\(spClass2)") }
上記のプログラムを実行した出力結果は次のとおりです。
さまざまなクラス インスタンスへの参照 SampleClass
スイフトのプロパティ
Swift プロパティは、値を特定のクラス、構造、または列挙に関連付けます。
プロパティは、保存されたプロパティと計算されたプロパティに分類できます。
保存されたプロパティ | 計算されたプロパティ |
---|---|
存储常量或变量作为实例的一部分 | 计算(而不是存储)一个值 |
用于类和结构体 | 用于类、结构体和枚举 |
存储属性和计算属性通常用于特定类型的实例。
属性也可以直接用于类型本身,这种属性称为类型属性。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。
存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。
-
可以在定义存储属性的时候指定默认值
-
也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值
延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。
在属性声明前使用 lazy 来标示一个延迟存储属性。
注意:
必须将延迟存储属性声明成变量(使用var
关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
延迟存储属性一般用于:
-
延迟对象的创建。
-
当属性的值依赖于其他未知类
import Cocoa class sample { lazy var no = number() // `var` 关键字是必须的 } class number { var name = "Runoob Swift 教程" } var firstsample = sample() print(firstsample.no.name)
以上程序执行输出结果为:
Runoob Swift チュートリアル
インスタンス化変数
Objective-C の使用経験がある場合は、Objective-C がクラス インスタンスの値と参照を格納するための 2 つのメソッドを提供していることを知っているはずです。プロパティの場合、インスタンス変数をプロパティ値のバックエンド ストレージとして使用することもできます。
Swift プログラミング言語では、これらの理論は属性を使用して統合され、実装されます。Swift のプロパティには対応するインスタンス変数がなく、プロパティのバックエンド ストレージに直接アクセスすることはできません。これにより、さまざまなシナリオでのアクセス方法の問題が回避され、属性の定義が 1 つのステートメントに簡素化されます。
名前、型、メモリ管理特性など、型のプロパティに関するすべての情報は、型定義という 1 つの場所で定義されます。
計算されたプロパティ
格納されたプロパティに加えて、クラス、構造体、および列挙型で計算プロパティを定義できます。計算プロパティは値を直接格納しませんが、値を取得するためのゲッターと、他のプロパティまたは変数の値を間接的に設定するためのオプションのセッターを提供します。
import Cocoa クラスのサンプル { var no1 = 0.0, no2 = 0.0 var length = 300.0, width = 150.0 var middle: (Double, Double) { get{ return (length / 2, width / 2) } set(axis){ no1 = axis.0 - (長さ / 2) no2 = axis.1 - (幅 / 2) } } } var result = sample() print(result.middle) result.middle = (0.0, 10.0) print(result.no1) print(結果.no2)
上記のプログラムを実行した出力結果は次のとおりです。
(150.0、75.0) -150.0 -65.0
計算されたプロパティのセッターが新しい値を表すパラメーター名を定義していない場合は、デフォルト名 newValue を使用できます。
読み取り専用の計算プロパティ
ゲッターのみを持ちセッターを持たない計算プロパティは、読み取り専用の計算プロパティです。
読み取り専用の計算プロパティは常に値を返し、ドット (.) 演算子を介してアクセスできますが、新しい値を設定することはできません。
import Cocoa class film { var head = "" var period = 0.0 var metaInfo: [String:String] { return [ "head": self.head, "duration":"\(self.duration)" ] } } var movie = film() movie.head = "Swift プロパティ" movie.duration = 3.09 print(movie.metaInfo["head"]!) print(movie.metaInfo["duration"]!)
上記のプログラムを実行した出力結果は次のとおりです。
Swift プロパティ 3.09
知らせ:
var
読み取り専用の計算プロパティを含む計算プロパティは、値が固定されていないため、キーワードを使用して定義する必要があります。let
このキーワードは、初期化後に変更できない値を示す定数プロパティを宣言するためにのみ使用されます。
財産観察者
プロパティ オブザーバーは、プロパティ値の変更を監視し、応答します。プロパティ オブザーバーは、新しい値が現在の値と同じである場合でも、プロパティが設定されるたびに呼び出されます。
プロパティ オブザーバーは、遅延ストアド プロパティ以外のストアド プロパティに追加でき、プロパティ オブザーバーは、プロパティをオーバーロードすることで継承プロパティ (ストアド プロパティと計算プロパティを含む) に追加できます。
注:
値の変更はセッターを通じて直接監視および応答できるため、オーバーロードできない計算プロパティにはプロパティ オブザーバーを追加する必要はありません。
次のオブザーバーの 1 つまたはすべてをプロパティに追加できます。
willSet
新しい値を設定する前に呼び出されますdidSet
新しい値が設定された直後に呼び出されます- willSet および DidSet オブザーバーはプロパティの初期化中に呼び出されません
import Cocoa class Samplepgm { var counter: Int = 0{ willSet(newTotal){ print("计数器: \(newTotal)") } DidSet{ if counter > oldValue { print("新增数 \(counter - oldValue)") Let NewCounter = Samplepgm () NewCounter.counter = 100 NewCounter.counter = 800
上記のプログラムを実行した出力結果は次のとおりです。
カウンタ: 100 新しいカウント 100 カウンタ: 800 新しいカウント 700
グローバル変数とローカル変数
計算プロパティとプロパティ オブザーバーについて説明したパターンは、グローバル変数とローカル変数にも使用できます。
ローカル変数 | グローバル変数 |
---|---|
関数、メソッド、またはクロージャー内で定義された変数。 | 任意の型の外部で定義された関数、メソッド、クロージャ、または変数。 |
値の保存と取得に使用されます。 | 値の保存と取得に使用されます。 |
保存されたプロパティは、値の取得と設定に使用されます。 | 保存されたプロパティは、値の取得と設定に使用されます。 |
計算されたプロパティにも使用されます。 | 計算されたプロパティにも使用されます。 |
タイプ属性
型属性は、型の最も外側の中括弧 ({}) 内に型定義の一部として書き込まれます。
値型の型属性を定義するにはキーワード static を使用し、クラスの型属性を定義するにはキーワード class を使用します。
型プロパティの値を取得および設定します
インスタンス属性と同様に、タイプ属性にはドット演算子 (.) を介してアクセスします。ただし、型プロパティは、インスタンスを通じてではなく、型自体を通じて取得および設定されます。例は次のとおりです。
迅速なメソッド
Swift メソッドは特定の型に関連付けられた関数です
Objective-C では、メソッドを定義できる型はクラスだけです。しかし、Swift では、クラス/構造体/列挙体を定義するかどうかを選択できるだけでなく、作成した型 (クラス/構造体/列挙体) のメソッドを柔軟に定義することもできます。
インスタンスメソッド
Swift 言語では、インスタンス メソッドは、特定のクラス、構造体、または列挙型のインスタンスに属するメソッドです。
インスタンス メソッドには次のメソッドが用意されています。
-
インスタンスのプロパティにアクセスして変更できます
-
インスタンスの目的に関連する機能を提供する
インスタンス メソッドは、それが属する型を囲む中括弧 ({}) の間に記述する必要があります。
インスタンス メソッドは、その型の他のすべてのインスタンス メソッドおよびプロパティに暗黙的にアクセスできます。
インスタンス メソッドは、それが属するクラスの特定のインスタンスによってのみ呼び出すことができます。
インスタンス メソッドは、既存のインスタンスから独立して呼び出すことはできません。
文法
func funcname(Parameters) -> returntype
{
Statement1
Statement2
……
Statement N
戻りパラメータ
}
メソッドのローカルパラメータ名と外部パラメータ名
Swift 関数のパラメーターには、ローカル名 (関数本体内で使用される) と外部名 (関数の呼び出し時に使用される) の両方を持つことができます。
Swift のメソッドは、Objective-C のメソッドと非常に似ています。Objective-C と同様に、Swift のメソッド名は通常、with、for、by などのメソッドの最初のパラメータを指す前置詞を使用します。
デフォルトでは、Swift はメソッドの最初のパラメータ名にのみローカル パラメータ名を与え、2 番目以降のパラメータ名にもグローバル パラメータ名を与えます。
次の例では、「no1」が Swift のローカルパラメータ名として宣言されています。「no2」は、グローバル宣言と外部プログラム経由のアクセスに使用されます。
外部名設定を提供するかどうか
外部名を最初のパラメータに強制的に追加し、このローカル名を外部名として使用します(Swift 2.0 より前では、# 記号が使用されていました)。
逆に、アンダースコア (_) を使用して、外部名を指定せずに 2 番目以降のパラメーターを設定することもできます。
自己属性
型のすべてのインスタンスには、self と呼ばれる暗黙のプロパティがあり、これはインスタンス自体とまったく同じです。
この暗黙的な self 属性をインスタンスのインスタンス メソッドで使用して、現在のインスタンスを参照できます。
インスタンスメソッドの値の型を変更する
構造体と列挙型は、Swift 言語の値の型です。一般に、値型のプロパティは、そのインスタンス メソッド内で変更できません。
ただし、特定のメソッドの構造体または列挙型のプロパティを変更する必要がある場合は、メソッドを変更することを選択すると、メソッドはメソッド内からそのプロパティを変更でき、メソッドが行う変更はすべて元の構造です。メソッドの最後にも保持されます。
メソッドは、暗黙的な self プロパティに新しいインスタンスを割り当てることもできます。これにより、メソッドの終了後に元のインスタンスが置き換えられます。
import Cocoa struct area { var length = 1 var width = 1 func area() -> Int { return length * width } mutating funcscaleBy(res: Int) { length *= res width *= res print(length) print(breadth) ) } } var val = 面積(長さ: 3、幅: 5) val.scaleBy(res: 3) val.scaleBy(res: 30) val.scaleBy(res: 300)
上記のプログラムを実行した出力結果は次のとおりです。
9
15
270
450
81000
135000
変数メソッドで自分自身に値を代入する
可変メソッドは、暗黙的なプロパティ self に新しいインスタンスを割り当てることができます。
変更関数scaleBy(res: Int) {
self.length *= res
self.breadth *= res
print(length)
print(breadth)
}
型メソッド
インスタンス メソッドは、型のインスタンスによって呼び出されるメソッドです。型自体によって呼び出されるメソッドを定義することもできます。このメソッドは型メソッドと呼ばれます。
構造体および列挙型の型メソッドを宣言するには、メソッドの func キーワードの前にキーワード static を追加します。クラスはキーワード class を使用して、サブクラスが親クラスの実装メソッドをオーバーライドできるようにすることができます。
型メソッドは、インスタンス メソッドと同様にドット (.) 構文を使用して呼び出されます。
import Cocoa
class Math
{
class func abs(number: Int) -> Int
{
if number < 0
{
return (-number)
}
else
{
return number
}
}
}
struct absno
{
static func abs(number: Int) -> Int
{
if 数値 < 0
{
return (-number)
}
else
{
return 数値
}
}
}
let no = Math.abs(number: -35)
let num = absno.abs(number: -5)
print(no)
印刷(数値)
上記のプログラムを実行した出力結果は次のとおりです。
35
5
Swift 下标脚本
下标脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 someArray[index] ,访问字典(Dictionary)实例中的元素可以这样写 someDictionary[key]。
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
下标脚本语法及应用
语法
下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。
语法类似于实例方法和计算型属性的混合。
与定义实例方法类似,定义下标脚本使用subscript关键字,显式声明入参(一个或多个)和返回类型。
与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:
subscript(index: Int) -> Int {
get {
// 用于下标脚本值的声明
}
set(newValue) {
// 执行赋值操作
}
}
实例 1
import Cocoa
struct subexample {
let decrementer: Int
subscript(index: Int) -> Int {
return decrementer /index
}
}
let Division = subexample(decrementer: 100)
print("100 を 9 で割った値は \(division[9] )" )
print("100 割る 2 は \(division[2])")
print("100 割る 3 は \(division[3])")
print("100 割る 5 は \(division[5] ]) ")
print("100 を 7 で割ると \(division[7])")
上記のプログラムを実行した出力結果は次のとおりです。
100 割る 9 は 11
100 割る 2 は 50
100 割る 3 は 33
100 割る 5 は 20
100 割る 7 は 14
上記の例では、除算演算のインスタンスがサブサンプル構造を通じて作成されます。値 100 がパラメーターとして構造体コンストラクターに渡され、インスタンス メンバーのデクリメンターが初期化されます。
スクリプトに添字を付けることで結果を取得できます。たとえば、division[2] は 100 を 2 で割った値です。
下付きスクリプトのオプション
添字スクリプトでは、任意の数の入力パラメーター インデックスを使用でき、各入力パラメーター タイプに制限はありません。
下标脚本的返回值也可以是任何类型。
下标脚本可以使用变量参数和可变参数。
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过传入参数的类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是下标脚本的重载。
Swift 继承
继承我们可以理解为一个类获取了另外一个类的方法和属性。
当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本,并且可以重写它们。
我们也可以为类中继承来的属性添加属性观察器。
基类
没有继承其它类的类,称之为基类(Base Class)。
子类
子类指的是在一个已有类的基础上创建一个新的类。
为了指明某个类的超类,将超类名写在子类名的后面,用冒号(:)分隔,语法格式如下
class SomeClass: SomeSuperclass { // 类的定义 }
实例
以下实例中我们定义了超类 StudDetails,然后使用子类 Tom 继承它:
class StudDetails
{
var mark1: Int;
var マーク 2: 整数;
init(stm1:Int, 結果 stm2:Int)
{
mark1 = stm1;
マーク2 = stm2;
}
func show()
{
print("Mark1:\(self.mark1), Mark2:\(self.mark2)") }
}
class
Tom : StudDetails
{
init()
{
super.init(stm1: 93, results: 89) )
}
}
let tom = Tom()
tom.show()
上記のプログラムを実行した出力結果は次のとおりです。
マーク1:93、マーク2:89
オーバーライド
サブクラスは、継承されたインスタンス メソッド、クラス メソッド、インスタンス属性、またはサブスクリプト スクリプトを通じて独自のカスタマイズされた関数を実装できます。この動作をオーバーライドと呼びます。
override キーワードを使用して書き換えを実現できます。
スーパークラスのメソッド、プロパティ、添字にアクセスする
スーパー プレフィックスを使用すると、スーパー クラスのメソッド、プロパティ、またはサブスクリプトにアクセスできます。
リライト | アクセスメソッド、プロパティ、添え字スクリプト |
---|---|
方法 | super.somemethod() |
属性 | super.someProperty() |
下付き文字 | スーパー[いくつかのインデックス] |
メソッドとプロパティをオーバーライドする
オーバーライドされたメソッド
サブクラスでは、override キーワードを使用してスーパークラスのメソッドをオーバーライドできます。
次の例では、show() メソッドをオーバーライドします。
class SuperClass {
func show() {
print("これはスーパークラスの SuperClass")
}
}
class SubClass: SuperClass {
override func show() {
print("これはサブクラスの SubClass") }
}
let
superClass = SuperClass()
superClass .show()
let subClass = SubClass()
subClass.show()
上記のプログラムを実行した出力結果は次のとおりです。
これはスーパークラス SuperClass であり
、これはサブクラス SubClass です
プロパティを上書きする
保存されているか計算されているかに関係なく、継承されたプロパティをオーバーライドするカスタム ゲッター (またはセッター) を提供できます。
サブクラスは、継承されたプロパティが保存されているのか計算されているのかを認識せず、継承されたプロパティが名前と型を持つことだけを知っています。したがって、属性をオーバーライドするときは、その名前と型を書き出す必要があります。
注意点:
-
オーバーライドされたプロパティにセッターを指定する場合は、ゲッターも指定する必要があります。
-
オーバーライドされたバージョンのゲッターで継承されたプロパティ値を変更したくない場合は、super.someProperty を通じて継承された値を直接返すことができます。ここで、someProperty はオーバーライドするプロパティの名前です。
次の例では、スーパークラス Circle とサブクラス Rectangle を定義します。Rectangle クラスでは、属性エリアをオーバーライドします。
lass Circle { var radius = 12.5 var area: String { return "Rectangle radius \(radius) " } } // スーパークラスを継承 Circle class Rectangle: Circle { var print = 7 override var area: String { return super.area + " ですが、\(print)" に書き直されました } } let rect = Rectangle() rect.radius = 25.0 rect.print = 3 print("Radius \(rect.area)")
上記のプログラムを実行した出力結果は次のとおりです。
半径長方形の半径は 25.0 ですが、3 にオーバーライドされました。
プロパティオブザーバーのオーバーライド
プロパティ オーバーライドで継承されたプロパティのプロパティ オブザーバーを追加できます。このようにして、継承されたプロパティ値がいつ変更されたかを検出できます。
注:継承された定数格納プロパティまたは継承された読み取り専用の計算プロパティに対してプロパティ オブザーバーを追加することはできません。
class Square: Rectangle {
override var radius: Double {
DidSet {
print = Int(radius/5.0)+1
}
}
}
書き換えを防止する
Final キーワードを使用して、それらがオーバーライドされるのを防ぐことができます。
Final メソッド、プロパティ、または添字スクリプトをオーバーライドすると、コンパイル時にエラーが報告されます。
キーワード class の前に、final 属性 (final クラス) を追加することで、クラス全体を Final としてマークできます。このようなクラスは継承できません。継承しないと、コンパイル エラーが報告されます。
Final class Circle {
final var radius = 12.5
var area: String {
return "長方形の半径は \(radius) "
}
}
class Rectangle: Circle {
var print = 7
override var area: String {
return super.area + "ですが、現在は \(print)" に書き換えられています。
}
}
上記の例では、final キーワードが使用されており、書き換えが許可されていないため、実行するとエラーが報告されます。
エラー: var は「final」をオーバーライドします var
override var area: String {
^
注: オーバーライドされた宣言はここにあります
var area: String {
^
エラー: var は「final」をオーバーライドします var
override var radius: Double {
^
注: オーバーライドされた宣言はここにあります
Final var radius = 12.5
^
エラー: 最終クラス 'Circle'
クラスからの継承 Rectangle: Circle {
^
スピーディな施工工程
構築は、クラス、構造体、または列挙型のインスタンスを使用するための準備プロセスです。このプロセスには、インスタンス内の各プロパティの初期値の設定と、必要な準備および初期化タスクの実行が含まれます。
Swift コンストラクターは init() メソッドを使用します。
Objective-C のコンストラクターとは異なり、Swift のコンストラクターは値を返す必要がなく、その主なタスクは、新しいインスタンスが初めて使用される前に適切に初期化されていることを確認することです。
クラス インスタンスは、初期化解除子を定義することによって、クラス インスタンスが解放される前にメモリをクリーンアップする作業を実行することもできます。
保存されたプロパティの初期割り当て
クラスと構造体は、インスタンスの作成時に、保存されているすべてのプロパティに適切な初期値を設定する必要があります。
格納されたプロパティがコンストラクターで割り当てられると、その値は直接設定され、プロパティ オブザーバーはトリガーされません。
コンストラクターでのストレージ プロパティの割り当てプロセスは次のとおりです。
-
初期値を作成します。
-
デフォルトのプロパティ値はプロパティ定義で指定されます。
-
インスタンスを初期化し、init() メソッドを呼び出します。
コンストラクタ
コンストラクターは、特定の型の新しいインスタンスを作成するときに呼び出されます。その最も単純な形式は、パラメータのないインスタンス メソッドに似ており、キーワード init にちなんで名付けられます。
文法
init()
{
// インスタンス化後にコードが実行される
}
例
次の構造は、パラメーターなしでコンストラクター init を定義し、保存された属性の長さと幅の値を 6 と 12 に初期化します。
struct rectangle {
var length: Double
var width: Double
init() {
length = 6
width = 12
}
}
var area = Rectangle()
print("四角形面积は \(area.length*area.breadth)")
上記のプログラムを実行した出力結果は次のとおりです。
長方形の面積は72.0です
デフォルトの属性値
コンストラクターで保存されたプロパティの初期値を設定できます。同様に、プロパティの宣言時にデフォルト値を設定できます。
デフォルト値を使用すると、コンストラクターをよりシンプルかつ明確にすることができ、プロパティの型をデフォルト値から自動的に推測できます。
次の例では、プロパティの宣言時にデフォルト値を設定します。
struct rectangle { //デフォルト値を設定 var length = 6 var width = 12 } var area = rectangle() print("長方形の面積は \(area.length*area.breadth)")
上記のプログラムを実行した出力結果は次のとおりです。
長方形の面積は72です
構築パラメータ
次のようにコンストラクター init() を定義するときに、構築パラメーターを指定できます。
struct Rectangle {
var length: Double
var width: Double
var area: Double
init(fromLength length: Double, fromBreadth width: Double) {
self.length = 長さ
self.breadth = width
area = length * width
}
init(fromLeng leng: Double , fromBread パン: Double) {
self.length = 長さ
self.breadth = パン
面積 = 長さ * パン
}
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积のために: \(ar.area)" )
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积範囲: \(are.area)")
上記のプログラムを実行した出力結果は次のとおりです。
面積: 72.0
面積: 432.0
内部パラメータ名と外部パラメータ名
関数およびメソッドのパラメーターと同様、構築パラメーターにも、コンストラクター内で使用されるパラメーター名と、コンストラクターの呼び出し時に使用される外部パラメーター名があります。
ただし、コンストラクターには、関数やメソッドのように、括弧の前に識別可能な名前がありません。したがって、コンストラクターを呼び出す場合、呼び出されるコンストラクターは主にコンストラクター内のパラメーターの名前と型によって決まります。
コンストラクターを定義するときにパラメーターの外部名を指定しない場合、Swift は各コンストラクター パラメーターに対して内部名と同じ外部名を自動的に生成します。
struct Color { let red, green, blue: Double init(red: Double, green: Double, blue: Double) { self.red = red self.green = green self.blue = blue } init(white: Double) { red = 白 緑 = 白 青 = 白 } }
外部名パラメータがありません
コンストラクター パラメーターに外部名を指定したくない場合は、アンダースコアを使用して、_
それを説明する外部名を表示できます。
struct Rectangle {
var length: Double
init(frombreadth width: Double) {
length = width * 10
}
init(frombre bre: Double) {
length = bre * 30
}
//外部名字
init(_ area: Double) {
length = 面積
}
/ 外部名を指定せずに呼び出します
letectarea = Rectangle(180.0)
print("領域は: \(rectarea.length)")
オプションの属性タイプ
カスタマイズした型に、論理的に null 値を許可する格納された属性が含まれている場合は、それをオプションの型 (オプションの属性タイプ) として定義する必要があります。
格納されたプロパティがオプションとして宣言されると、自動的に nil に初期化されます。
struct Rectangle {
var length: Double?
init(frombreadth width: Double) {
length = width * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
構築中に定数属性を変更する
構築プロセスの完了前に定数の値を決定できる限り、構築プロセス中のいつでも定数プロパティの値を変更できます。
クラス インスタンスの場合、その定数属性は、それを定義するクラスの構築プロセス中にのみ変更できます。サブクラス内で変更することはできません。
length プロパティは定数になりましたが、引き続きそのクラスのコンストラクターでその値を設定できます。
struct Rectangle { let length: Double? init(frombreadth width: Double) { length = width * 10 } init(frombre bre: Double) { length = bre * 30 } init(_ area: Double) { length = area } } let rectarea = Rectangle(180.0) print( "面积:\(rectarea.length)")
上記のプログラムを実行した出力結果は次のとおりです。
領域は次のとおりです: オプション(180.0)
デフォルトのコンストラクター
デフォルトのコンストラクターは、すべてのプロパティ値をデフォルト値に設定してインスタンスを作成するだけです。
次の例では、ShoppingListItem クラスのすべてのプロパティにデフォルト値があり、親クラスのない基本クラスであるため、すべてのプロパティにデフォルト値を設定できるデフォルト コンストラクターが自動的に取得されます。
class ShoppingListItem {
var name: String?
varquantity = 1
var Purchase = false
}
var item = ShoppingListItem()
print("名前は: \(item.name)")
print("計算式は次のとおりです: \(item.quantity) )" )
print("支払うかどうか: \(item.purchased)")
上記のプログラムを実行した出力結果は次のとおりです。
名前: nil
数学: 1
支払うかどうか: false
構造体のメンバーごとの初期化子
構造体がすべての格納されたプロパティのデフォルト値を提供し、それ自体がカスタム初期化子を提供しない場合、メンバーごとの初期化子を自動的に取得できます。
メンバーごとのコンストラクターを呼び出すときは、メンバー属性名と同じパラメーター名を渡して、メンバー属性の初期割り当てを完了します。
次の例では、長さと幅の 2 つのプロパティを含む構造体 Rectangle を定義します。Swift は、100.0 と 200.0 の初期割り当てに基づいて、これら 2 つのプロパティの Double 型を自動的に推測できます。
struct Rectangle {
var length = 100.0, width = 200.0
}
let area = Rectangle(length: 24.0, width: 32.0)
print("長方形の面積: \(area.length)")
print("長方形の面積長方形: \(面積 .幅)")
両方の格納プロパティにはデフォルト値があるため、構造体 Rectangle はメンバーごとの初期化子 init(width:height:) を自動的に取得します。これを使用して Rectangle の新しいインスタンスを作成できます。
上記のプログラムを実行した出力結果は次のとおりです。
長方形の面積:24.0
長方形の面積:32.0
値型のコンストラクター プロキシ
コンストラクターは、他のコンストラクターを呼び出すことで、インスタンスの構築プロセスの一部を完了できます。このプロセスはコンストラクターの委任と呼ばれ、複数のコンストラクター間でのコードの重複を削減します。
次の例では、Rect 構造体が Size と Point の構築プロセスを呼び出します。
struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { varorigin = Point() var size = Size() init() {} init(origin: Point, size: サイズ) { self.origin = 原点 self.size = サイズ } init(center: ポイント, size: サイズ) { letoriginX = center.x - (size.width / 2) letoriginY = center.y - (size .height / 2) self.init(origin: Point(x:originX, y:originY), size: size) } } // 原点とサイズ都プロパティを使用する定時の默认值Point(x: 0.0, y: 0.0 )和サイズ(幅: 0.0、高さ: 0.0): = Rect() print("サイズ 结构体初值: \(basicRect.size.width 、basicRect.size.height) ") print("Rect 構造体の初期値: \(basicRect.origin.x, BasicRect.origin.y) ") // 原点とサイズのパラメーター値を対応する格納された属性に割り当てます letoriginRect = Rect(origin: Point( x : 2.0, y: 2.0)、 size: Size(width: 5.0, height: 5.0)) print("サイズ構造体初期値: \(originRect.size.width,originRect.size.height) ") print(" 初期値Rect 構造体の: \(originRect.origin.x,originRect.origin.y) ") //まず中心とサイズの値を通じて原点の座標を計算します。 //次に、 init(origin:size:) コンストラクターを呼び出して (またはプロキシして)、新しい原点とサイズの値を対応する属性に割り当てます。 let centerRect = Rect(center: Point(x: 4.0, y: 4.0) , size: Size(width: 3.0, height: 3.0)) print("Size 構造体初期値: \(centerRect.size.width, centerRect.size.height) ") print("Rect 構造体初期値: \( centerRect.origin .x, centerRect.origin.y) ")
上記のプログラムを実行した出力結果は次のとおりです。
Size 構造体の初期値: (0.0, 0.0) Rect 構造体の初期値: (0.0, 0.0) Size 構造体の初期値: (5.0, 5.0) Rect 構造体の初期値: (2.0, 2.0) Size 構造体の初期値: (3.0, 3.0) Rect構造体の初期値: (2.5, 2.5)
クラスの継承と構築プロセス
Swift では、すべてのクラス インスタンスに格納されているプロパティが初期値を取得できるようにするために、指定イニシャライザとコンビニエンス イニシャライザの 2 種類のクラスイニシャライザが提供されています。
指定施工者 | コンビニエンスコンストラクター |
クラスのメインコンストラクター | クラス内の二次的な補助コンストラクター |
クラスで提供されるすべてのプロパティを初期化し、親クラス チェーンの上の親クラスのコンストラクターを呼び出して、親クラスの初期化を実装します。 | コンビニエンス イニシャライザは、同じクラス内の指定されたイニシャライザを呼び出し、そのパラメータのデフォルト値を提供するように定義できます。便利な初期化子を定義して、特別な目的または特定の入力を使用してインスタンスを作成することもできます。 |
すべてのクラスには少なくとも 1 つの指定されたコンストラクターが必要です | 必要な場合にのみクラスに便利な初期化子を提供する |
Init(パラメータ) { ステートメント } |
便利なinit(parameters) {
ステートメント
} |
コンストラクターインスタンスを指定する
class mainClass {
var no1 : Int // ローカル記憶変数
init(no1 : Int) {
self.no1 = no1 // 初期化
}
}
class subClass : mainClass {
var no2 : Int // 新しいサブクラス記憶変数
init(no1 : Int, no2 : Int) {
self.no2 = no2 //
super.init(no1:no1) を初期化 // スーパークラスを初期化
}
}
let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)
print("res は: \(res.no1)")
print("res2 は: \(res2.no1)")
print("res2 は: \(res2.no2)")
上記のプログラムを実行した出力結果は次のとおりです。
解像度: 10
解像度: 10
解像度: 20
コンビニエンスコンストラクターインスタンス
class mainClass {
var no1 : Int // ローカル記憶変数
init(no1 : Int) {
self.no1 = no1 // 初期化
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self .no2 = no2
super.init(no1:no1)
}
// コンビニエンス メソッドに必要なパラメータは 1 つだけです。
コンビニエンス オーバーライド init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1) : 20)
let res2 = subClass(no1: 30, no2: 50)
print("res は: \(res.no1)")
print("res2 は: \(res2.no1)")
print("res2 は: \(res2.no2)")
上記のプログラムを実行した出力結果は次のとおりです。
解像度: 20
解像度 2 : 30
解像度 2 : 50
コンストラクターの継承とオーバーロード
Swift のサブクラスは、デフォルトでは親クラスのコンストラクターを継承しません。
親クラスのコンストラクターは、決定的で安全な場合にのみ継承されます。
親クラス指定のイニシャライザをオーバーライドする場合は、オーバーライド修飾子を記述する必要があります。
class SuperClass {
varcorners = 4
var description: String {
return "\(corners)edges"
}
}
let Rectangle = SuperClass()
print("Rectangle: \(rectangle.description)")
class SubClass: SuperClass {
override init() { //オーバーロードされたコンストラクター
super.init()
corners = 5
}
}
let subClass = SubClass()
print("五角形型: \(subClass.description)")
上記のプログラムを実行した出力結果は次のとおりです。
長方形:4辺
五角形:5辺
指定されたイニシャライザおよびコンビニエンス イニシャライザ インスタンス
次の例は、指定されたイニシャライザ、コンビニエンス イニシャライザ、および自動イニシャライザの動作中の継承を示しています。
MainClass と SubClass という 2 つのクラスを含むクラス階層を定義し、それらのコンストラクターがどのように対話するかを示します。
class MainClass {
var name: String
init(name: String) {
self.name = name
}
コンビニエンス init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Runoob")
print("MainClass 名字: \(main.name)")
let main2 = MainClass()
print("没有对应名字: \(main2.name)")
class SubClass: MainClass {
var count: Int
init(name: String) , count: Int) {
self.count = count
super.init(name: name)
}
利便性をオーバーライドします init(name: String) {
self.init(name: name, count: 1)
}
}
let sub = SubClass(name: "Runoob")
print("MainClass 名字: \(sub.name)")
let sub2 = SubClass(name: "Runoob", count: 3)
print("count 变量: \(sub2 。カウント)")
上記のプログラムを実行した出力結果は次のとおりです。
MainClass 名: Runoob
該当する名前はありません: [Anonymous]
MainClass 名: Runoob
カウント変数: 3
クラスの初期化子が失敗しました
クラス、構造体、または列挙型オブジェクトがそれ自体の構築中に失敗する可能性がある場合は、そのオブジェクトに対して失敗可能な初期化子を定義します。
変数の初期化が失敗する考えられる理由は次のとおりです。
-
無効なパラメータ値が渡されました。
-
必要な外部リソースがありません。
-
特定の条件が満たされていません。
この建設プロセスが失敗する可能性がある状況に適切に対処するため。
クラス、構造体、または列挙型の定義に1 つ以上の失敗可能な初期化子を追加できます。構文は、init キーワードの後に疑問符 (init?) を追加することです。
列挙型の失敗した初期化子
1 つ以上のパラメーターを取る失敗可能な初期化子を構築することで、列挙型の特定の列挙メンバーを取得できます。
例
次の例では、TemperatureUnit という名前の列挙型が定義されています。これには、3 つの可能な列挙メンバー (ケルビン、摂氏、華氏) と、Character 値に対応する列挙メンバーを見つけるために使用される失敗可能な初期化子が含まれています。
enumTemperatureUnit { // ケルビン、摂氏、華氏 case ケルビン、摂氏、華氏 init?(symbol: Character) { switch シンボル { case "K": self = .Kelvin case "C": self = .Celsius case "F": self = .Fahrenheit デフォルト: nil を返します } } } let fahrenheitUnit = TemperatureUnit(symbol: "F") if fahrenheitUnit != nil { print("これは定義された温度単位であるため、初期化は成功しました。") } let anonymousUnit =温度単位(記号:"X") ifunknownUnit == nil { print("これは定義された温度単位ではないため、初期化に失敗しました。") }
上記のプログラムを実行した出力結果は次のとおりです。
これは定義された温度単位であるため、初期化は成功しました。
これは定義された温度単位ではないため、初期化に失敗しました。
クラスの初期化子が失敗しました
値型 (構造体や列挙型など) の失敗可能な初期化子には、構築の失敗がいつ、どこでトリガーされるかについての制限はありません。
ただし、クラスの失敗可能なイニシャライザは、すべてのクラス プロパティが初期化され、クラス内のコンストラクター間のすべてのプロキシ呼び出しが発生した後でのみ、失敗動作をトリガーできます。
例
次の例では、StudRecord という名前のクラスが定義されています。studname 属性は定数であるため、StudRecord クラスが正常に構築されたら、studname 属性は非 nil 値を持つ必要があります。
class StudRecord { let スタッド名: String! init?(スタッド名: String) { self.studname = スタッド名 if スタッド名.isEmpty { return nil } } } if let stname = StudRecord(studname: "失敗したコンストラクタ") { print("モジュールは\(stname.studname)") }
上記のプログラムを実行した出力結果は次のとおりです。
モジュールは失敗したコンストラクターです
失敗可能なイニシャライザをオーバーライドする
他のイニシャライザと同様に、基本クラスのfailableイニシャライザをサブクラスのfailableイニシャライザでオーバーライドできます。
あるいは、基本クラスの失敗可能なイニシャライザをサブクラスの失敗しないイニシャライザでオーバーライドすることもできます。
失敗可能なイニシャライザを失敗しないイニシャライザでオーバーライドすることはできますが、その逆は機能しません。
失敗しないイニシャライザは、失敗可能なイニシャライザに呼び出しを委任することはできません。
例
次の例では、失敗可能なイニシャライザと失敗しないイニシャライザについて説明します。
class Planet { var name: String init(name: String) { self.name = name } コンビニエンス init() { self.init(name: "[No Planets]") } } let plName = Planet(name: "Mercury" ) print("行星の名字です: \(plName.name)") let noplName = Planet() print(" 存在 しないこの名字の行星: \(noplName.name)") class Planets: Planet { var count: Int init (name: String, count: Int) { self.count = count super.init(name: name) } 利便性をオーバーライドする init(name: String) { self.init(name: name, count: 1) } }
上記のプログラムを実行した出力結果は次のとおりです。
行星的名字是: Mercury 没有这个名字的行星: [No Planets]
可失败构造器 init!
通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!)。实例如下:
struct StudRecord { let stname: String init!(stname: String) { if stname.isEmpty {return nil } self.stname = stname } } let stmark = StudRecord(stname: "Runoob") if let name = stmark { print("指定了学生名") } let blankname = StudRecord(stname: "") if blankname == nil { print("学生名为空") }
以上程序执行输出结果为:
指定了学生名 学生名为空
Swift 析构过程
在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit
来标示析构函数,类似于初始化函数用init
来标示。析构函数只适用于类类型。
析构过程原理
Swift 会自动释放不再需要的实例以释放资源。
Swift 通过自动引用计数(ARC)处理实例的内存管理。
通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。
例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。
语法
在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:
deinit {
// 执行析构过程
}
实例
var counter = 0; // 引用计数器
class BaseClass {
init() {
counter += 1;
}
deinit {
counter -= 1;
}
}
var show: BaseClass? = BaseClass()
print(counter)
show = nil
print(counter)
以上程序执行输出结果为:
1
0
当 show = nil 语句执行后,计算器减去 1,show 占用的内存就会释放。
var counter = 0; // カウンター
クラスの参照 BaseClass {
init() {
counter += 1;
}
deinit {
counter -= 1;
}
}
var show: BaseClass? = BaseClass()
print(counter)
print(counter)
上記のプログラムを実行した出力結果は次のとおりです。
1
1
Swift オプションのチェーニング
Optional Chaining は、プロパティ、メソッド、およびサブスクリプトをリクエストおよび呼び出すことができるプロセスです。リクエストまたは呼び出しのターゲットは nil である場合があります。
オプションの連鎖は 2 つの値を返します。
-
ターゲットに値がある場合、呼び出しは成功し、値が返されます。
-
ターゲットが nil の場合、呼び出しは nil を返します。
複数のリクエストまたは呼び出しをチェーンにリンクできますが、いずれかのノードが nil の場合、チェーン全体が失敗します。
強制解決の代替としてのオプションの連鎖
オプションのチェーンを定義するには、プロパティ、メソッド、または添え字スクリプトのオプションの値の後に疑問符 (?) を置きます。
オプションのチェーン「?」 | 感嘆符 (!) は、メソッド、プロパティ、および添え字スクリプトのオプション チェーンを強制的に展開します。 |
? オプションの値を入力し、後でメソッド、プロパティ、添え字スクリプトを呼び出します。 | ! オプションの値に配置され、後でメソッド、プロパティ、添え字スクリプトを呼び出して値を強制的に展開します。 |
オプションが nil の場合、わかりやすいエラー メッセージが出力されます。 | オプションが nil の場合、強制アンワインド実行エラー |
感嘆符 (!) を使用したオプションのチェーン インスタンス
class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() //将导致运行时错误 let roomCount = john.residence!.numberOfRooms
以上程序执行输出结果为:
fatal error: unexpectedly found nil while unwrapping an Optional value
想使用感叹号(!)强制解析获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供解析的residence值。
使用问号(?)可选链实例
class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() // 链接可选residence?属性,如果residence存在则取回numberOfRooms的值 if let roomCount = john.residence?.numberOfRooms { print("John 的房间号为 \(roomCount)。") } else { print("不能查看房间号") }
以上程序执行输出结果为:
不能查看房间号
因为这种尝试获得numberOfRooms的操作有可能失败,可选链会返回Int?类型值,或者称作"可选Int"。当residence是空的时候(上例),选择Int将会为空,因此会出现无法访问numberOfRooms的情况。
要注意的是,即使numberOfRooms是非可选Int(Int?)时这一点也成立。只要是通过可选链的请求就意味着最后numberOfRooms总是返回一个Int?而不是Int。
为可选链定义模型类
你可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
实例
定义了四个模型类,其中包括多层可选链:
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var BuildingName: 文字列?
var BuildingNumber: 文字列?
var ストリート: 文字列?
funcbuildingIdentifier() -> 文字列? {
if (建物名 != nil) {
建物名を返す
} else if (建物番号 != nil) {
建物番号を返す
} else {建物
名を返す
}
}
}
オプションのチェーンによるメソッドの呼び出し
オプションのチェーンを使用して、オプションの値でメソッドを呼び出し、メソッド呼び出しが成功したかどうかを確認できます。このメソッドが値を返さない場合でも、この目的でオプションのチェーンを使用できます。
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if ((john.residence?.printNumberOfRooms()) != nil) {
print("输出房间号")
} else {
print("无法输出房间号")
}
以上程序执行输出结果为:
无法输出房间号
使用if语句来检查是否能成功调用printNumberOfRooms方法:如果方法通过可选链调用成功,printNumberOfRooms的隐式返回值将会是Void,如果没有成功,将返回nil。
使用可选链调用下标脚本
オプションのチェーンを使用して、サブスクリプト スクリプトから値を取得し、サブスクリプト スクリプトの呼び出しが成功したかどうかを確認することはできますが、オプションのチェーンを使用してサブスクリプト スクリプトを設定することはできません。
例1
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var建物名:文字列?
var建物番号:文字列?
var street:文字列?
funcbuildingIdentifier() -> String? {
if (建物名 != nil) {
戻り建物名
} else if (建物番号 != nil) {
戻り建物番号
} else {
戻り値nil
}
}
}
let john = person()
if let firstRoomName = john.residence?[0].name {
print("最初の部屋名\(firstRoomName).")
} else {
print("部屋を取得できません" )
}
上記のプログラムを実行した出力結果は次のとおりです。
部屋を取得できません
添字スクリプト呼び出しのオプションの連鎖疑問符は、john.residence の直後、添字スクリプト括弧の前に続きます。これは、john.residence がオプションのチェーンが取得しようとしているオプションの値であるためです。
例 2
インスタンス内の john.residence に Residence インスタンスを作成し、彼の rooms 配列に 1 つ以上の Room インスタンスがある場合、オプションのチェーンを使用して、Residence サブスクリプト スクリプトを通じて rooms 配列内のインスタンスを取得できます。
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var BuildingName: 文字列?
var BuildingNumber: 文字列?
var ストリート: 文字列?
funcbuildingIdentifier() -> 文字列? {
if (建物名 != nil) {
建物名を返す
} else if (建物番号 != nil) {
建物番号を返す
} else {
nil を返す
}
}
}
let john = person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room) (name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse
let johnsAddress = Address()
johnsAddress.buildingName = "カラマツ"
johnsAddress.street = "ローレル・ストリート"
john.residence!.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("ジョンの通りは\(johnsStreet)です。")
} else {
print( "不可能アドレスを取得します。")
}
上記のプログラムを実行した出力結果は次のとおりです。
ジョンの通りはローレル・ストリートです。
オプションのリンク呼び出しを介してサブスクリプトにアクセスする
オプションのチェーン呼び出しを通じて、サブスクリプトを使用してオプションの値を読み取りまたは書き込み、サブスクリプト呼び出しが成功したかどうかを判断できます。
例
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var BuildingName: 文字列?
var BuildingNumber: 文字列?
var ストリート: 文字列?
funcbuildingIdentifier() -> 文字列? {
if (建物名 != nil) {
建物名を返す
} else if (建物番号 != nil) {
建物番号を返す
} else {
nil を返す
}
}
}
let john = person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room) (name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("第一个房间名である\(firstRoomName)")
} else {
print("部屋を取得できません")
}
上記のプログラムを実行した出力結果は次のとおりです。
最初の部屋はリビングルームと呼ばれます
オプションのタイプの添え字にアクセスする
添字が null 許容型の値を返す場合 (Swift の Dictionary のキー添字など)。添字の閉じ括弧の後に疑問符を置くことで、添字の null 許容戻り値を連鎖させることができます。
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]] testScores["Dave"]?[0] = 91 testScores["Bev"]?[0] ++ testScores["Brian"]?[0] = 72 // 「Dave」配列は [91, 82, 84] になり、「Bev」配列は [80, 94, 81] になりました
上記の例では、testScores 配列が定義されています。この配列には 2 つのキーと値のペアが含まれており、String 型のキーを整数配列にマップしています。
この例では、オプションの連鎖呼び出しを使用して、「Dave」配列の最初の要素を 91 に設定し、「Bev」配列の最初の要素を +1 して、「Brian」配列の最初の要素を 72 に設定しようとします。
これら 2 つのキーが存在するため、最初の 2 つの呼び出しは成功します。しかし、キー「Brian」は辞書に存在しないため、3 回目の呼び出しは失敗します。
複数のリンク層を接続する
複数レベルのオプション チェーンを接続したり、モデル内で下位レベルのプロパティ メソッドや添字をマイニングしたりできます。ただし、マルチレベルのオプション チェーンでは、すでに返されているオプションの値を超えるレベルを追加することはできません。
オプションのチェーンを通じて Int 値を取得しようとすると、使用されるチェーンのレベルに関係なく、結果は常に Int? になります。同様に、オプションの連鎖を通じて Int? 値を取得しようとすると、使用される連鎖のレベルに関係なく、結果は常に Int? になります。
例1
次の例では、john の住居属性の住所の番地属性を取得しようとします。ここでは、住居属性と住所属性を接続するために 2 レベルのオプション チェーンが使用されています。どちらもオプションのタイプです。
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var建物名:文字列?
var建物番号:文字列?
var street:文字列?
funcbuildingIdentifier() -> String? {
if (建物名 != nil) {
戻り建物名
} else if (建物番号 != nil) {
戻り建物番号
} else {
戻り値nil
}
}
}
let john = person()
if let johnsStreet = john.residence?.address?.street {
print("ジョンの住所は \(johnsStreet) です。")
} else {
print("住所を取得できません")
}
上記のプログラムを実行した出力結果は次のとおりです。
アドレスを取得できません
例 2
Address のインスタンスを john.residence.address の値として設定し、address の番地プロパティに実際の値を設定すると、複数レベルのオプションの連鎖を通じてそのプロパティ値を取得できます。
class 人 {
var 住居: 住居?
}
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get{
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("房间号は \(numberOfRooms)")
}
var address: 住所?
}
class Room {
let name: String
init(name: String) { self.name = name }
}
class Address {
var BuildingName: 文字列?
var BuildingNumber: 文字列?
var ストリート: 文字列?
funcbuildingIdentifier() -> 文字列? {
if (建物名 != nil) {
建物名を返す
} else if (建物番号 != nil) {
建物番号を返す
} else {
nil を返す
}
}
}
let john = person()
john.residence?[0] = Room(name: "浴室")
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse
if let firstRoomName = john.住居?[0].name {
print("最初の部屋は\(firstRoomName)")
} else {
print("部屋を取得できません")
}
上記の例の出力結果は次のとおりです。
最初の部屋はリビングルームです
オプションの値を返すチェーン関数
オプションのチェーンを通じて null 許容値を返すメソッドを呼び出すこともでき、オプションの値をチェーンし続けることもできます。
例
class Person {
var house: Residence?
}
// 変数 rooms が定義され、Room[] 型の空の配列に初期化されます
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("部屋番号は\(numberOfRooms)")
}
var address: Address?
}
// Room は name 属性と設定 Initializer
クラス ルーム名を決定する Room {
let name: String
init(name: String) { self.name = name }
}
// モデルの最後のクラスは Address
クラス Address {
var建物名:文字列?
var建物番号:文字列?
var street:文字列?
funcbuildingIdentifier() -> String? {
if (建物名 != nil) {
戻り建物名
} else if (建物番号 != nil) {
戻り建物番号
} else {
戻り値nil
}
}
}
let john = Person()
if john.residence?.printNumberOfRooms() != nil {
print("部屋番号指定あり")
} else {
print("部屋番号指定なし")
}
上記のプログラムを実行した出力結果は次のとおりです。
部屋番号が指定されていません
Swift 自動参照カウント (ARC)
Swift は、アプリケーションのメモリを追跡および管理するメカニズムである自動参照カウント (ARC) を使用します。
クラスのインスタンスが使用されなくなった場合、ARC はクラスのインスタンスによって占有されていたメモリを自動的に解放するため、通常はメモリを手動で解放する必要はありません。
ただし、コード内にメモリ管理を実装する必要がある場合もあります。
ARC機能
-
init() メソッドを使用してクラスの新しいインスタンスを作成するたびに、ARC はインスタンス情報を保存するために大きなメモリ チャンクを割り当てます。
-
メモリには、インスタンスのタイプ情報と、このインスタンスのすべての関連プロパティの値が含まれます。
-
インスタンスが使用されなくなると、ARC はインスタンスによって占有されていたメモリを解放し、解放されたメモリを他の用途に使用できるようにします。
-
使用中のインスタンスが破壊されないように、ARC は各インスタンスによって参照されているプロパティ、定数、変数の数を追跡し、カウントします。
-
インスタンスがプロパティ、定数、または変数に割り当てられると、インスタンスへの強参照が作成され、強参照が存在する限り、インスタンスを破棄することはできません。
クラスインスタンス間の循環強参照
上記の例では、ARC は新しく作成された person インスタンスへの参照の数を追跡し、その person インスタンスが不要になったときにそれを破棄します。
ただし、クラスの強参照が決して 0 にならないようなコードを作成することもできます。これは、2 つのクラス インスタンスが相互への強い参照を維持し、相互の破棄を防ぐ場合に発生します。これを強参照サイクルと呼びます。
インスタンス間の強参照サイクルを解決する
Swift は、クラス プロパティを使用するときに発生する強参照サイクルの問題を解決する 2 つの方法を提供します。
- 弱引用
- 所有されていない参照
弱参照と非所有参照により、循環参照内の 1 つのインスタンスが、強参照を維持せずに別のインスタンスを参照できます。このようにして、インスタンスは、強い参照サイクルを作成せずに相互に参照できます。
存続期間中に nil になるインスタンスには弱い参照を使用します。逆に、最初の代入後に決して nil に割り当てられないインスタンスには、所有されていない参照を使用します。
弱参照インスタンス
class Module { let name: String init(name: String) { self.name = name } var sub: SubModule? deinit { print("\(name) 主模块") } } class SubModule { letnumber: Int init(number: Int) { self.number = number } weak var topic: Module? deinit { print("子模块 topic 数: \(number)") } } var toc: モジュール? 変数リスト: サブモジュール? toc = モジュール(名前: "ARC") list = サブモジュール(番号: 4) toc!.sub = list list!.topic = toc toc = nil list = nil
上記のプログラムを実行した出力結果は次のとおりです。
ARC メイン モジュールの
サブモジュールのトピック番号は 4 です
所有されていない参照インスタンス
class Student { let name: String var セクション: Marks? init(name: String) { self.name = name } deinit { print("\(name)") } } class Marks { let marks: Int unowned let stname: Student init(marks: Int, stname: Student) { self .marks = マーク self.stname = stname } deinit { print("学生の分数は \(marks)") } } var module: 学生? module = Student(name: "ARC") module!.section = Marks(marks: 98, stname: module!) module = nil
上記のプログラムを実行した出力結果は次のとおりです。
ARC の
生徒は 98 点を獲得しました
クロージャによる強い参照サイクル
強参照サイクルは、クラス インスタンスのプロパティにクロージャを割り当て、そのインスタンスがクロージャ本体で使用される場合にも発生する可能性があります。このクロージャ本体は、クロージャ内で self.someProperty などのインスタンスのプロパティにアクセスしたり、self.someMethod などのインスタンスのメソッドを呼び出したりすることができます。どちらの場合も、クロージャが自己を「捕捉」し、強力な参照サイクルが作成されます。
例
次の例は、クロージャが self を参照するときに強参照サイクルがどのように生成されるかを示しています。この例では、HTMLElement というクラスを定義します。このクラスは、単純なモデルを使用して HTML 内の単一要素を表します。
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) is being deinitialized") } } // 创建实例并打印信息 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML())
HTMLElement クラスは、クラス インスタンスと asHTML デフォルト値のクロージャーの間に強力な参照サイクルを作成します。
インスタンスの asHTML 属性は、クロージャへの強い参照を保持します。ただし、クロージャはクロージャ本体で self を使用する (self.name と self.text を参照) ため、クロージャは self をキャプチャします。これは、クロージャが HTMLElement インスタンスへの強い参照を保持していることを意味します。このように、2 つのオブジェクトには強力な循環参照が存在します。
クロージャによる強参照循環を解決する: クロージャを定義する際に、クロージャの一部としてキャプチャリストを定義することで、クロージャとクラスインスタンス間の強参照循環を解決できます。
弱い参照と所有されていない参照
クロージャとキャプチャされたインスタンスが常に相互参照し、常に同時に破棄される場合は、クロージャ内のキャプチャを非所有参照として定義します。
逆に、キャプチャ参照が nil になる場合がある場合は、クロージャ内のキャプチャを弱い参照として定義します。
キャプチャされた参照が nil に設定されない場合は、弱い参照の代わりに所有されていない参照を使用する必要があります。
例
前の HTMLElement の例では、所有されていない参照は、強参照サイクルを解決する正しい方法です。強い参照の循環を避けるために、次のように HTMLElement クラスを記述します。
class HTMLElement { let name: String let text: String? 遅延変数 asHTML: () -> String = { [所有されていない self] in if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) 被析构") } } //创建并打印HTMLElement实例 var段落: HTMLElement? = HTMLElement(名前: "p"、テキスト: "こんにちは、 // HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息 paragraph = nil
以上程序执行输出结果为:
<p>hello, world</p>
p 被析构
Swift 类型转换
Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例。
Swift 中类型转换使用 is 和 as 操作符实现,is 用于检测值的类型,as 用于转换类型。
类型转换也可以用来检查一个类是否实现了某个协议。
检查类型
类型转换用于检测实例类型是否属于特定的实例类型。
你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。
类型检查使用 is 关键字。
操作符 is 来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false。
クラス 主題 { var 物理学: String init(physics: String) { self.physics = 物理学 } } クラス 化学: 主題 { var 方程式: String init(物理学: String, 方程式: String) { self.equations = 方程式 super.init(物理学: 物理学) } } class 数学: 科目 { var Formulae: String init(physics: String, Formulae: String) { self.formulae = Formulas super.init(物理学: 物理学) } } let sa = [ Chemistry(physics: "固体物理"、方程式: "赫兹")、 数学(物理学:「流体力学」、公式:「GHz」)、 化学(物理学:「熱物理学」、方程式:「デシベル」)、 数学(物理学:「天体物理学」、公式:「MHz」)、数学(物理学:「熱物理学」、公式:「MHz」 ) : "微分方程式", 式: "コサイン級数")] let samplechem = Chemistry(物理学: "固体物理学", 方程式: "ヘルツ") print("物理学の例: \(samplechem.physics)") print ("方程式の例: \(samplechem.equations)") let samplemaths = Maths(物理学: "流体力学", 公式: "ギガヘルツ") print("物理学の例: \(samplemaths.physics)") print("式の例は次のとおりです: \(samplemaths.Formulae)") var chemCount = 0 var mathsCount = 0 for item in sa { // Chemistry タイプのインスタンスの場合は true を返し、それ以外の場合は false を返します。 if item が Chemistry { ++chemCount } else if item is Maths { ++mathsCount } } print("化学の科目には \(chemCount) のトピックが含まれ、数学には \(mathsCount) のトピックが含まれます")
上記のプログラムを実行した出力結果は次のとおりです。
物理学の例は次のとおりです: 固体物理学 方程式の例は次のとおりです: ヘルツ 物理学の例は次のとおりです: 流体力学 数式の例は次のとおり です: ギガヘルツ 化学の科目には 2 つのトピックが含まれ、数学には 3 つのトピックが含まれます
下方変換
下方変換の場合は、型変換演算子 (as? または as!) を使用します。
ダウンキャストが成功するかどうかわからない場合は、型変換の条件付き形式 (as?) を使用します。条件付き形式のキャストは常にオプションの値を返します。ダウンキャストが不可能な場合、オプションの値は nil になります。
強制形式 (as!) は、下方変換が成功することが確実な場合にのみ使用してください。間違った型にダウンキャストしようとすると、キャストによってランタイム エラーが発生する可能性があります。
クラス 主題 {
var 物理学: String
init(physics: String) {
self.physics = 物理学
}
}
クラス 化学: 主題 {
var 方程式: String
init(物理学: String, 方程式: String) {
self.equations = 方程式
super.init(物理学: 物理学)
}
}
class 数学: 科目 {
var Formulae: String
init(physics: String, Formulae: String) {
self.formulae = Formulas
super.init(物理学: 物理学)
}
}
let sa = [
Chemistry(physics: "固体物理"、方程式: "赫兹")、
Maths(physics: "流体动力学", formulae: "千兆赫"),
Chemistry(physics: "热物理学", equations: "分贝"),
Maths(physics: "天体物理学", formulae: "兆赫"),
Maths(physics: "微分方程", formulae: "余弦级数")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
var chemCount = 0
var mathsCount = 0
for item in sa {
// 类型转换的条件形式
if let show = item as? Chemistry {
print("化学主题是: '\(show.physics)', \(show.equations)")
// 必須形式
} else if let example = item as? Maths {
print("数学のトピックは: '\(example.physics)', \(example.formulae)")
}
}
上記のプログラムを実行した出力結果は次のとおりです。
物理学の例: 固体物理学
方程式
の例: Hz 物理学の例: 流体力学 式の
例: GHz
化学のトピック: 「固体物理学」、Hz数学のトピック: 「流体力学」、GHz
化学のトピックは次
のとおりです:「流体力学」、GHz
数学のトピックは次のとおりです:「天体物理学」、MHz
数学のトピックは次のとおりです:「微分方程式」、コサイン級数
Any と AnyObject 間の型変換
Swift は、不定型に対して 2 つの特別な型エイリアスを提供します。
AnyObject
任意のクラス型のインスタンスを表すことができます。(Objective-C の id に似ています)Any
関数型を含む任意の型を表すことができます。
注: とは、
その動作と機能が明らかに必要な場合にのみ使用してください。コード内で予期される明示的な型を使用することをお勧めします。Any
AnyObject
任意のインスタンス
クラス 主題 { var 物理学: String init(physics: String) { self.physics = 物理学 } } クラス 化学: 主題 { var 方程式: String init(物理学: String, 方程式: String) { self.equations = 方程式 super.init(物理学: 物理学) } } class 数学: 科目 { var Formulae: String init(physics: String, Formulae: String) { self.formulae = Formulas super.init(物理学: 物理学) } } let sa = [ Chemistry(physics: "固体物理"、方程式: "赫兹")、 Maths(physics: "流体动力学", formulae: "千兆赫"), Chemistry(physics: "热物理学", equations: "分贝"), Maths(physics: "天体物理学", formulae: "兆赫"), Maths(physics: "微分方程", formulae: "余弦级数")] let samplechem = Chemistry(physics: "固体物理", equations: "赫兹") print("实例物理学是: \(samplechem.physics)") print("实例方程式: \(samplechem.equations)") let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫") print("实例物理学是: \(samplemaths.physics)") print("实例公式是: \(samplemaths.formulae)") var chemCount = 0 var mathsCount = 0 for item in sa { // 类型转换的条件形式 if let show = item as? Chemistry { print("化学主题是: '\(show.physics)', \(show.equations)") // 必須形式 } else if let example = item as? Maths { print("数学のトピックは: '\(example.physics)', \(example.formulae)") } } // 型 の配列を格納できます任意の例 var exampleany = [Any]() exampleany.append(12) exampleany.append(3.14159) exampleany.append("任意のインスタンス") exampleany.append(Chemistry(physics: "Solid State Physics", 方程式: "MHz") )) for item2 in exampleany { switch item2 { case let someInt as Int: print("整数値は \(someInt)") case let someDouble as Double where someDouble > 0: print("Pi の値は \(someDouble )") の場合、someString を String として使用します。 print("\(someString)") case let phy as Chemistry: print("主题 '\(phy.physics)', \(phy.equations)") default: print("None") } }
以上程序执行输出结果为:
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学', 千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学', 兆赫
数学主题是: '微分方程', 余弦级数
整型值为 12
Pi 值为 3.14159
Any 实例
主题 '固体物理', 兆赫
AnyObject 实例
クラス 主題 { var 物理学: String init(physics: String) { self.physics = 物理学 } } クラス 化学: 主題 { var 方程式: String init(物理学: String, 方程式: String) { self.equations = 方程式 super.init(物理学: 物理学) } } class Maths: Subjects { var Formulae: String init(physics: String, Formulae: String) { self.formulae = Formulas super.init(physics:physics) } } // [AnyObject] タイプの数组 let saprint: [任意のオブジェクト] = [ 化学(物理学:「固体物理学」、方程式:「ヘルツ」)、 数学(物理学:「流体力学」、公式:「ギガヘルツ」)、 化学(物理学:「熱物理学」、方程式:「デシベル」)、 数学(物理学: "天体物理学", 式: "MHz"), Maths(物理学: "微分方程式", 式: "コサイン級数")] letsamplechem = Chemistry(物理学: "固体物理学", 方程式: "ヘルツ") print( "物理学の例: \(samplechem.physics)") print("方程式の例: \(samplechem.equations)") letsamplemaths = Maths(物理学: "流体力学", 公式: "ギガヘルツ") print("物理学の例は次のとおりです: \(samplemaths.physics)") print("数式の例は次のとおりです: \(samplemaths.physics)Formulae)") var chemCount = 0 var mathsCount = 0 for item in saprint { // 型変換の条件付き形式 if let show = item as? Chemistry { print("化学のトピックは: '\(show.physics)', \(show.equations)") // 必須形式 } else if let example = item as? Maths { print("数学のトピックは: '\ (example .physics)', \(example.formulae)") } } var exampleany = [Any]() exampleany.append(12) exampleany.append(3.14159) exampleany.append("任意のインスタンス") exampleany.append( Chemistry( 物理学: "固体物理学", 方程式: "MHz")) for item2 in exampleany { switch item2 { case let someInt as Int: print("整数値は \(someInt)") case let someDouble as Double where someDouble > 0 : print("Pi 値は \(someDouble)") の場合、someString を文字列として使用します。 print("\(someString)") case let phy as Chemistry: print("主题 '\(phy.physics)', \(phy.equations)") default: print("None") } }
以上程序执行输出结果为:
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学', 千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学', 兆赫
数学主题是: '微分方程', 余弦级数
整型值为 12
Pi 值为 3.14159
Any 实例
主题 '固体物理', 兆赫
在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。
Swift 扩展
扩展就是向一个已有的类、结构体或枚举类型添加新功能。
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
Swift 中的扩展可以:
- 添加计算型属性和计算型静态属性
- 定义实例方法和类型方法
- 提供新的构造器
- 定义下标
- 定义和使用新的嵌套类型
- 既存の型をプロトコルに準拠させる
文法
拡張子の宣言では、キーワード extensionを使用します。
extension SomeType {
// SomeType に追加された新しい関数はここに書かれます
}
拡張機能は、既存の型を拡張して 1 つ以上のプロトコルに適応させることができます。構文は次のとおりです。
extension SomeType: SomeProtocol, AnotherProctocol {
//プロトコルの実装はここに書かれています
}
計算されたプロパティ
拡張機能では、計算されたインスタンスのプロパティと計算された型のプロパティを既存の型に追加できます。
例
次の例では、5 つの計算インスタンス プロパティを Int 型に追加し、その機能を拡張します。
extension Int {
var add: Int {return self + 100 }
var sub: Int { return self - 10 }
var mul: Int { return self * 10 }
var div: Int { return self / 5 }
}
let加算 = 3.add
print("加算後の値:\(加算)")
let減算 = 120.sub
print("減算後の値:\(減算)")
let multiplication = 39.mul
print("乗算後の値:\(乗算)")
let Division = 55.div
print("除算演算後の値: \(division)")
let mix = 30.add + 34.sub
print("混合演算結果: \(mix)" )
上記のプログラムを実行した出力結果は次のとおりです。
加算後の値:103
減算後の値:110
乗算後の値:390
除算後の値:11
混合結果:154
コンストラクタ
拡張機能では、既存の型に新しいコンストラクターを追加できます。
これにより、他の型を拡張したり、独自のカスタム型をコンストラクター パラメーターとして渡したり、型の元の実装に含まれていない追加の初期化オプションを提供したりすることができます。
拡張機能は、新しい便利な初期化子 init() をクラスに追加できますが、新しい指定されたコンストラクターまたはデストラクター deinit() をクラスに追加することはできません。
struct sum {
var num1 = 100, num2 = 200
}
struct diff {
var no1 = 200, no2 = 100
}
struct mult {
var a = sum()
var b = diff()
}
extension mult {
init(x: sum, y : diff) {
_ = x.num1 + x.num2
_ = y.no1 + y.no2
}
}
let a = sum(num1: 100, num2: 200)
let b = diff(no1: 200, no2: 100)
let getMult = mult(x: a, y: b)
print("getMult sum\(getMult.a.num1, getMult.a.num2)")
print("getMult diff\(getMult.b.no1, getMult.b) .no2)")
上記のプログラムを実行した出力結果は次のとおりです。
getMult sum(100, 200)
getMult diff(200, 100)
方法
拡張機能は、既存の型に新しいインスタンス メソッドと型メソッドを追加できます。
次の例では、topics という名前の新しいインスタンス メソッドを Int 型に追加します。
extension Int {
func topic(summation: () -> ()) {
for _ in 0..<self {
summation()
}
}
}
4.topics({
print("拡張モジュールの内部")
})
3.topics ( {
print("内部型変換モジュール")
})
上記のプログラムを実行した出力結果は次のとおりです。
拡張モジュール内
拡張
モジュール内 拡張モジュール内
拡張モジュール内
内部変換
モジュール内 内部
変換モジュール内
このtopics
メソッドは、() -> ()
型のパラメータを 1 つ使用します。これは、関数にパラメータも戻り値もないことを示します。
この拡張機能を定義した後、 topics
任意の整数に対してメソッドを呼び出してタスクを複数回実行できます。
可変インスタンスメソッド
拡張機能によって追加されたインスタンス メソッドは、インスタンス自体を変更することもできます。
自分自身またはそのプロパティを変更する構造体および列挙型内のメソッドは、元の実装の変更メソッドと同様に、インスタンス メソッドを変更としてマークする必要があります。
例
次の例では、square という新しい変更メソッドを Swift の Double 型に追加して、プリミティブ値の 2 乗計算を実装します。
extension Double { mutating func square() { let pi = 3.1415 self = pi * self * self } } var Trial1 = 3.3 Trial1.square() print("円の面積は: \(Trial1)") var Trial2 = 5.8 Trial2 .square() print("円の面積は \(Trial2)") var Trial3 = 120.3 Trial3.square() print("円の面積は \(Trial3) )")
上記のプログラムを実行した出力結果は次のとおりです。
円の面積: 34.210935 円
の面積: 105.68006
円の面積: 45464.070735
添字
拡張機能では、既存の型に新しい添字を追加できます。
例
次の例では、整数の添え字を Swift の組み込み型 Int に追加します。添字[n]は10進数を返します
extension Int { subscript(var multtable: Int) -> Int { var no1 = 1 while multtable > 0 { no1 *= 10 --multtable } return (self / no1) % 10 } } print(12[0]) print(7869[1]) print(786543[2])
以上程序执行输出结果为:
2
6
5
嵌套类型
扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
extension Int {
enum calc
{
case add
case sub
case mult
case div
case anything
}
var print: calc {
switch self
{
case 0:
return .add
case 1:
return .sub
case 2:
return .mult
case 3:
return .div
default :
return .anything
}
}
}
func result(numb: [Int]) {
for i in numb {
switch i.print {
case .add:
print(" 10 ")
case .sub:
print(" 20 ")
case .mult:
print(" 30 ")
case .div:
print(" 40 ")
デフォルト:
print(" 50 ")
}
}
}
result([ 0、1、2、3、4、7])
上記のプログラムを実行した出力結果は次のとおりです。
10
20
30
40
50
50
注:
拡張添え字のコードは、Swift の上位バージョンではエラーを報告する可能性があります。
この位置の 'var' は引数ラベルとして解釈されます 変更演算子の左側は変更可能ではありません: 'multtable' は不変です
書き込み方法を確認すると、次のように書くことで問題を回避できます。
extension Int{
subscript(digitIndex:Int)->Int{
var decmalBase = 1
var digit = digitIndex // digitIndex を直接使用することはできません。
digit > 0 の場合は
エラーが報告されます{
dicialBase *= 10
digit = digit - 1
}
return (self/decmalBase ) % 10
}
}
print(12[0])
print(7869[1])
print(786543[2])
インターネット上の書き方を参考にすると、次のように書くこともできます。
extension Int{
subscript(digitIndex:Int)->Int{
var decmalBase = 1
for _ in 0 ..< digitIndex{
dicialBase *= 10
}
return (self/decmalBase) % 10
}
}
print(12[0])
print( 7869[1])
プリント(786543[2])
迅速なプロトコル
プロトコルは、特定の機能を実装するために必要なメソッドと属性を指定します。
プロトコルの要件を満たすタイプは、そのプロトコルに準拠していると言われます。
クラス、構造体、または列挙型はすべてプロトコルに従い、プロトコルで定義されたメソッドと関数を完成させるための特定の実装を提供できます。
文法
プロトコルの構文形式は次のとおりです。
プロトコル SomeProtocol {
//プロトコルの内容
}
クラスをプロトコルに準拠させるには、型定義の一部として、型名の後にプロトコル名をコロン: で区切って追加する必要があります。複数のプロトコルに従う場合は、それらをカンマで区切ります。
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 構造体の内容
}
プロトコルに準拠しているクラスに親クラスがある場合、親クラス名をプロトコル名の前にカンマで区切って配置する必要があります。
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
}
对属性的规定
协议用于指定特定的实例属性或类属性,而不用指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。
协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。
プロトコルクラスa { var マーク: Int { get set } var result: Bool { get } funcattence() -> String func marksecured() -> String } プロトコルクラスb: classa { var present: Bool { get set } var subject: String { get set } var stname: String { get set } } class classc: classb { varmarks = 96 let result = true var present = false var subject = "Swift 协议" var stname = "プロトコル" funcattence() -> String { return "\(stname) は 99% の出席率を確保しました" } func marksecured() -> String { return "\(stname) は \(marks) を獲得しました" } } let stackdet = classc() stackdet.stname = "Swift" stackdet.marks = 98 stackdet.markssecured() print(studdet.marks) print(studdet.result) print(studdet.present) print(studdet.subject) print(studdet.stname)
上記のプログラムを実行した出力結果は次のとおりです。
98
true
false
Swift プロトコル
Swift
メソッドを変更するための要件
場合によっては、メソッド内のインスタンスを変更する必要があります。
たとえば、値型 (構造体、列挙型) のインスタンス メソッドでは、変更キーワードが関数のプレフィックスとして使用され、func の前に記述され、それが属するインスタンスとそのインスタンス属性の値を指定できることを示します。この方法で修正しました。
プロトコル daysofaweek {
mutating func show()
}
enum days: daysofaweek {
case sun、mon、tue、wed、thurs、fri、sat
mutating func show() {
switch self {
case .sun:
self = .sun
print("Sunday" )
case .mon:
self = .mon
print("月曜日")
case .tue:
self = .tue
print("火曜日")
case .wed:
self = .wed
print("水曜日")
case .thurs:
self = .木曜日
print("水曜日")
ケース .fri:
self = .fri
print("金曜日")
case .sat:
self = .sat
print("土曜日")
デフォルト:
print("そんな日はありません")
}
}
}
var res = days.wed
res.show()
上記のプログラムを実行した出力結果は次のとおりです。
水曜日
コンストラクターの要件
プロトコルは、その準拠者に指定されたコンストラクターを実装することを要求できます。
通常のコンストラクターを記述するのと同じように、プロトコル定義にコンストラクターの宣言を記述することができますが、中括弧とコンストラクターの実体を記述する必要はありません。構文は次のとおりです。
プロトコル SomeProtocol {
init(someParameter: Int)
}
例
プロトコル tcpprotocol {
init(aprot: Int)
}
プロトコル コンストラクターはクラス内の実装を指定します。
このプロトコルに準拠するクラスにコンストラクターを実装し、それをクラスの指定イニシャライザーまたはコンビニエンス イニシャライザーとして指定できます。どちらの場合も、コンストラクター実装を「required」修飾子でマークする必要があります。
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现
}
}
protocol tcpprotocol {
init(aprot: Int)
}
class tcpClass: tcpprotocol {
required init(aprot: Int) {
}
}
使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。
如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示required和override修饰符:
protocol tcpprotocol { init(no1: Int) } class mainClass { var no1: Int // 局部变量 init(no1: Int) { self.no1 = no1 // 初始化 } } class subClass: mainClass, tcpprotocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override" required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = mainClass(no1: 20) let show = subClass(no1: 30, no2: 50) print("res is: \(res.no1)") print("結果は: \(show.no1)") print("結果は: \(show.no2)")
上記のプログラムを実行した出力結果は次のとおりです。
解像度: 20
解像度: 30
解像度: 50
契約の種類
プロトコル自体は機能を実装しませんが、プロトコルを型として使用できます。
プロトコルは、他の通常のタイプと同様に使用できます。
- 関数、メソッド、またはコンストラクターのパラメーター型または戻り値型として
- 定数、変数、またはプロパティのタイプとして
- 配列、辞書、またはその他のコンテナ内の要素タイプとして
例
プロトコル ジェネレーター {
Associatedtype members
func next() -> members?
}
var items = [10,20,30].makeIterator()
while let x = items.next() {
print(x)
}
リストの [1,2,3].map( {i in i*5} ) {
print(リスト)
}
print([100,200,300])
print([1,2,3].map({i in i*10}))
上記のプログラムを実行した出力結果は次のとおりです。
10
20
30
5
10
15
[100、200、300]
[10、20、30]
拡張機能にプロトコル メンバーを追加する
拡張機能を通じて既存の型 (クラス、構造体、列挙型など) を拡張できます。
拡張機能では、プロパティ、メソッド、添え字スクリプト、プロトコル、およびその他のメンバーを既存の型に追加できます。
プロトコル AgeClasificationProtocol {
var age: Int { get }
func agetype() -> String
}
class Person {
let firstname: String
let lastname: String
var age: Int
init(firstname: String, lastname: String) {
self.firstname = firstname
self .lastname = lastname
self.age = 10
}
}
extension Person : AgeClasificationProtocol {
func fullname() -> String {
var c: String
c = firstname + " " + lastname
return c
}
func agetype() -> String {
switch age {
ケース0...2:
"Baby" を返す
case 2...12:
"Child" を返す
case 13...19:
"Teenager" を返す
case let x where x > 65:
"Elderly" を返す
デフォルト:
"Normal" を返す
}
}
}
プロトコルの継承
プロトコルは 1 つ以上の他のプロトコルを継承でき、継承されたプロトコルに基づいて新しいコンテンツ要件を追加できます。
プロトコルの継承構文はクラスの継承構文と似ており、複数の継承プロトコルはカンマで区切られます。
プロトコル InheritingProtocol: SomeProtocol, AnotherProtocol {
// プロトコル定義
}
例
プロトコル Classa {
var no1: Int { get set }
func calc(sum: Int)
}
プロトコル Result {
func print(target: Classa)
}
class Student2: Result {
func print(target: Classa) {
target.calc(1)
}
}
class Classb: Result {
func print(target: Classa) {
target.calc(5)
}
}
class Student: Classa {
var no1: Int = 10
func calc(sum: Int) {
no1 -= sum
print("生徒の試行\(合計) 回経過しました")
if no1 <= 0 {
print("学生が試験を欠席しました")
}
}
}
class Player {
var stmark: Result!
init(stmark: Result) {
self.stmark = stmark
}
func print(target: Classa) {
stmark.print(target)
}
}
var marks = Player(stmark: Student2())
var marksec = Student()
marks.print(marksec)
marks.print(marksec)
marks.print(marksec)
marks.stmark = Classb()
marks.print(marksec)
marks.print(marksec)
marks.print(marksec)
以上程序执行输出结果为:
学生尝试 1 次通过
学生尝试 1 次通过
学生尝试 1 次通过
学生尝试 5 次通过
学生尝试 5 次通过
学生缺席考试
学生尝试 5 次通过
学生缺席考试
类专属协议
你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。
class キーワードはプロトコルの継承リストの最初に出現し、その後に他の継承されたプロトコルが出現する必要があります。形式は次のとおりです。
プロトコル SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// プロトコル定義
}
例
protocol TcpProtocol { init(no1: Int) } class MainClass { var no1: Int // 局部变量 init(no1: Int) { self.no1 = no1 // 初始化 } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override" required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res is: \(res.no1)") print("res is: \(show.no1)") print("res is: \(show.no2)")
以上程序执行输出结果为:
res is: 20
res is: 30
res is: 50
协议合成
Swift 支持合成多个协议,这在我们需要同时遵循多个协议时非常有用。
语法格式如下:
プロトコル Stname { var name: String { get } } プロトコル Stage { var age: Int { get } } struct person: Stname, Stage { var name: String var age: Int } func show(celebrator: Stname & Stage) { print( "\(celebrator.name) は \(celebrator.age) 歳です") } let スタッド名 = 人物(名前: "プリヤ"、年齢: 21) show (スタッド名) let スタッド = 人物(名前: "レハン"、年齢: 21) : 29) print(stud) let students = 人(名前: "Roshan"、年齢: 19) print(student)
上記のプログラムを実行した出力結果は次のとおりです。
プリヤは21歳
人(名前:「リーハン」、年齢:29歳)
人(名前:「ロシャン」、年齢:19歳)
プロトコルの一貫性をチェックする
is および as 演算子を使用すると、特定のプロトコルに従っているかどうかを確認したり、特定の型にキャストしたりできます。
is
Operator は、インスタンスに遵循
特定の があるかどうかを確認するために使用されます协议
。as?
プロトコルをインスタンス化する場合は遵循
プロトコル タイプを返し、それ以外の場合はオプションの値を返しますnil
。as
強制的に下方変換するために使用されます。強制変換が失敗すると、実行時エラーが発生します。
例
次の例では、Double 型の読み取り可能領域を必要とする HasArea プロトコルを定義します。
protocol HasArea {
var area: Double { get }
}
// Circle クラスが定義されており、すべて HasArea プロトコル
クラスに従います Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init ( radius: Double) { self.radius = radius }
}
// Country クラスが定義されており、すべて HasArea プロトコル
クラスに従っています Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
/ / Animal HasArea プロトコルを実装していないクラスです class
Animal {
var Legs: Int
init(legs: Int) { self.legs = Legs }
}
let object: [AnyObject] = [
Circle(radius: 2.0),
Country(エリア: 243_610)、
Animal(legs: 4)
]
for object in objects {
// 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
if let objectWithArea = object as? HasArea {
print("面积为 \(objectWithArea.area)")
} else {
print("没有面积")
}
}
以上程序执行输出结果为:
面积为 12.5663708
面积为 243610.0
没有面积
Swift 泛型
Swift 提供了泛型让你写出灵活且可重用的函数和类型。
Swift 标准库是通过泛型代码构建出来的。
Swift 的数组和字典类型都是泛型集。
你可以创建一个Int数组,也可创建一个String数组,或者甚至于可以是任何其他 Swift 的类型数据数组。
以下实例是一个非泛型函数 exchange 用来交换两个 Int 值:
实例
//関数を定義
func swapTwoInts(_ a: inout Int, _ b: inout Int) { lettemporaryA = a a = b b =temporaryA } var numb1 = 100 var numb2 = 200 print("スワップ前データ: \(numb1 ) と \(numb2)") swapTwoInts(&numb1, &numb2) print("交換後のデータ: \(numb1) と \(numb2)")
上記のプログラムを実行した出力結果は次のとおりです。
交換前データ:100、200
交換後データ:200、100
上記の例では、Int 型の変数のみを試行し、交換します。2 つの String 値または Double 値を交換する場合は、以下に示すように、swapTwoStrings(_:_:) や swapTwoDoubles(_:_:) などの対応する関数を書き直す必要があります。
文字列および倍精度値交換関数
func swapTwoStrings(_ a: inout String, _ b: inout String) { lettemporaryA = a a = b b =temporaryA } func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { lettemporaryA = a a = b b = 一時的 A }
上記のコードを見ると、関数コードは同じですが、型が異なりますが、このとき、コードの重複を避けるためにジェネリックスを使用できます。
ジェネリックスでは、実際の型名 (Int、String、Double など) の代わりに、プレースホルダー型名 (ここでは文字 T で表されます) を使用します。
func swapTwoValues<T>(_ a: inout T、_ b: inout T)
swapTwoValues の後にはプレースホルダー型名 (T) が続き、山括弧 () で囲まれます<T>
。山括弧は、T が swapTwoValues(_:_:) 関数定義内のプレースホルダー型名であることを Swift に伝えるため、Swift は T という名前の実際の型を検索しません。
次の例は、2 つの Int 値と String 値を交換するために使用される汎用関数交換です。
例
//関数を定義
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { lettemporaryA = a a = b b =temporaryA } var numb1 = 100 var numb2 = 200 print( "交換前のデータ: \(numb1) と \(numb2)") swapTwoValues(&numb1, &numb2) print("交換後のデータ: \(numb1) と \(numb2)") var str1 = "A" var str2 = " B" print("交換前のデータ: \(str1) と \(str2)") swapTwoValues(&str1, &str2) print("交換後のデータ: \(str1) と \(str2)")
上記のプログラムを実行した出力結果は次のとおりです。
交換前データ:100、200 交換後データ:200、100 交換前データ:A、B 交換後データ:B、A
汎用タイプ
Swift では、独自のジェネリック型を定義できます。
カスタム クラス、構造体、列挙型は、配列や辞書と同様に、あらゆる型で動作します。
次に、Stack という汎用コレクション タイプを作成しましょう。スタックでは、コレクションの最後に新しい要素を追加すること (プッシュと呼ばれます) のみが可能で、最後から要素を削除すること (スタッキングと呼ばれます) のみが可能です。ポップ)。
次に、Stack という汎用コレクション タイプを作成しましょう。スタックでは、コレクションの最後に新しい要素を追加すること (プッシュと呼ばれます) のみが可能で、最後から要素を削除すること (スタッキングと呼ばれます) のみが可能です。ポップ)。
画像は次のように左から右に分析されます。
- 3 つの値がスタック上にあります。
- 4 番目の値はスタックの一番上にプッシュされます。
- スタックには 4 つの値があり、最後にプッシュされた値が一番上になります。
- スタックの最上位の値が削除またはポップされます。
- 1 つの値を削除すると、スタックには 3 つの値のみが含まれます。
次の例は、Int 型スタックを例とした、スタックの非ジェネリック バージョンです。
Int型のスタック
struct IntStack { var items = [Int]() 変異 func Push(_ item: Int) { items.append(item) } 変異 func Pop() -> Int { return items.removeLast() } }
この構造は、item と呼ばれる Array プロパティを使用して値をスタックに保存します。Stack には、push(_:) と Pop() という 2 つのメソッドがあり、値をスタックにプッシュしたり、スタックから値を削除したりするために使用されます。これらのメソッドは、構造体の items 配列の変更が必要なため、変更可能としてマークされています。
上記の IntStack 構造体は Int 型でのみ使用できます。ただし、あらゆるタイプの値を処理できる汎用スタック構造を定義できます。
同じコードの汎用バージョンを次に示します。
汎用スタック
struct Stack<要素> {
var items = [要素]()
変更関数 Push(_ item: Element) {
items.append(アイテム)
}
変更関数 Pop() -> 要素 {
items.removeLast() を返す
}
}
var stackOfStrings = Stack<String>()
print("スタックにプッシュされた文字列要素: ")
stackOfStrings.push("グーグル")
stackOfStrings.push("ルノオブ")
print(stackOfStrings.items);
let deletetos = stackOfStrings.pop()
print("ポップ要素: " + deletetos)
var stackOfInts = Stack<Int>()
print("スタックにプッシュされた整数要素: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);
この例の実行結果は次のとおりです。
文字列要素がスタックにプッシュされます:
["google", "runoob"]
要素がスタックからポップアウトされます: runoob
整数要素がスタックにプッシュされます:
[1, 2]
Stack は基本的に IntStack と同じですが、プレースホルダー型パラメーター Element が実際の Int 型を置き換えます。
上記の例では、Element が次の 3 つの場所でプレースホルダーとして使用されています。
- itemsプロパティを作成し 、 Element 型の空の配列で初期化します。
- Push(_:) メソッド の唯一のパラメータであるitemのタイプがElementタイプ である必要がある ことを指定します 。
- Pop()メソッド の戻り値の型が Element型である必要があることを指定します 。
拡張ジェネリック型
(extension キーワードを使用して) ジェネリック型を拡張する場合、拡張機能の定義で型パラメーター リストを指定する必要はありません。さらに便利なのは、元の型定義で宣言された型パラメーターのリストが拡張機能で利用可能であり、元の型のパラメーター名が元の定義の型パラメーターへの参照として使用されることです。
次の例では、topItem という読み取り専用の計算プロパティを追加することでジェネリック型 Stack を拡張します。このプロパティは、現在のスタックの最上位要素をスタックから削除せずに返します。
ジェネリック
struct Stack<Element> { var items = [Element]() mutating func Push(_ item: Element) { items.append(item) } mutating func Pop() -> Element { return items.removeLast() } }拡張スタック{ var topItem: Element? { return items.isEmpty ? nil : items[items.count - 1] } } var stackOfStrings = Stack<String>() print("スタックにプッシュされた文字列要素: ") stackOfStrings.push("google ") stackOfStrings.push("runoob") if let topItem = stackOfStrings.topItem { print("スタックの最上位要素は \(topItem) です。") } print(stackOfStrings.items)
インスタンスの topItem プロパティは、Element 型のオプションの値を返します。スタックが空の場合、topItem は nil を返し、スタックが空でない場合、topItem は項目配列の最後の要素を返します。
上記のプログラムを実行した出力結果は次のとおりです。
文字列要素がスタックにプッシュされます:
スタックの最上位要素は runoob.
["google", "runoob"]です。
既存の型を拡張して、関連付けられた型を指定することもできます。
たとえば、Swift の Array 型は、append(_:) メソッド、count 属性、および要素を取得するために Int 型のインデックス値を受け入れる添字をすでに提供しています。これら 3 つの関数はコンテナ プロトコルの要件に準拠しているため、Array がプロトコルを採用するように宣言するだけで Array を拡張できます。
次の例では、空の拡張機能を作成します。
拡張子配列: コンテナ {}
型制約
型制約は、指定されたクラスから継承するか、特定のプロトコルまたはプロトコル構造に準拠する必要がある型パラメーターを指定します。
型制約の構文
型パラメーター チェーンの一部として、型パラメーター名の後にコロンで区切って型制約を記述することができます。ジェネリック関数に対するこの型制約の基本構文は次のとおりです (ジェネリック型の構文と同じ)。
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// これは汎用関数の関数本体部分です
}
上記の関数には 2 つの型パラメータがあります。最初の型パラメーター T には、T が SomeClass のサブクラスであることを要求する型制約があり、2 番目の型パラメーター U には、U が SomeProtocol プロトコルに準拠することを要求する型制約があります。
例
ジェネリック
// 非ジェネリック関数、配列内の指定された文字列のインデックスを検索します
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { // 戻りインデックス値を検索 return Index } } return nil }
let strings = ["google", "weibo", "taobao", "runoob", "facebook"]
if let foundIndex = findIndex( ofString: "runoob", in: strings) { print("runoob のインデックスは \(foundIndex)") }
インデックスの添え字は 0 から始まります。
上記のプログラムを実行した出力結果は次のとおりです。
ルノブのインデックスは3です
アソシエーションクラス
Associatedtype キーワードは、Swift で関連付けられた型のインスタンスを設定するために使用されます。
次の例では、関連付けられたタイプの ItemType を定義するコンテナ プロトコルを定義します。
コンテナ プロトコルは、コンテナ プロトコルに準拠するタイプが提供する必要がある機能を 3 つだけ指定します。これら 3 つの条件が満たされる場合、準拠型は追加機能も提供できます。
// コンテナプロトコル
protocol Container {
Associatedtype ItemType
// コンテナに新しい要素を追加します
mutating func append(_ item: ItemType)
// コンテナ内の要素数を取得します
var count: Int { get }
// インデックス値の型is Int 添え字はコンテナ内の各要素を取得します
subscript(i: Int) -> ItemsType { get }
}
// スタック構造はコンテナ プロトコルに準拠します
struct Stack<Element>: Container {
// Stack< の元の実装部分Element>
var items = [Element]()
mutating func Push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// コンテナの実装部分プロトコル
変更関数 append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
var tos = Stack<String >()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素个数
print( tos.count)
上記のプログラムを実行した出力結果は次のとおりです。
["グーグル"、"ルノブ"、"タオバオ"]
3
Where ステートメント
型制約は、型がジェネリック関数またはクラスによって定義された制約に準拠していることを保証します。
パラメータリストの where ステートメントを使用してパラメータ制約を定義できます。
型パラメーター リストの直後に where ステートメントを記述し、その後に関連する型に対する 1 つ以上の制約や、型と関連する型の間の 1 つ以上の等価関係を記述することができます。
例
下面的例子定义了一个名为allItemsMatch的泛型函数,用来检查两个Container实例是否包含相同顺序的相同元素。
如果所有的元素能够匹配,那么返回 true,反之则返回 false。
泛型
// コンテナプロトコル
protocol Container { Associatedtype ItemType // コンテナに新しい要素を追加します mutating func append(_ item: ItemType) // コンテナ内の要素数を取得します var count: Int { get } // インデックス値の型is Int 添え字はコンテナ内の各要素を取得します subscript(i: Int) -> ItemType { get } } // // コンテナプロトコルに従う汎用 TOS タイプstruct Stack<Element>: Container { // Stack<Element> var items = [Element]()の元の実装部分 mutating func Push(_ item: Element) { items.append(item) } mutating func Pop() -> Element { return items.removeLast() } // コンテナプロトコルの実装一部
mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } // 展開、配列を追加拡張子 Array を Container として使用します: Container {} func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 2 つのコンテナに同じ数の要素が含まれているかどうかをチェック if someContainer.count != anotherContainer.count { return false } // 要素の各ペアが等しいかどうかをチェック
for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // すべての要素が一致し、true を返す return true } var tos = Stack<String>() tos.push ("google") tos.push("runoob") tos.push("taobao") var aos = ["google", "runoob", "taobao"] if allItemsMatch(tos, aos) { print("すべてに一致要素") } else { print("要素が一致しません") }
上記のプログラムを実行した出力結果は次のとおりです。
すべての要素に一致する
迅速なアクセス制御
アクセス制御により、コードが他のソース ファイルまたはモジュールにコーディングする際に必要なアクセス レベルを制限できます。
個々の型 (クラス、構造体、列挙型)、またはこれらの型のプロパティ、関数、初期化メソッド、プリミティブ型、添字インデックスなどに対してアクセス レベルを明示的に設定できます。
プロトコルは、プロトコル内のグローバル定数、変数、関数など、特定の範囲内での使用を制限することもできます。
アクセス制御はモジュールとソース ファイルに基づいて行われます。
モジュールとは、独立したユニットとして構築および公開されるフレームワークまたはアプリケーションを指します。Swift では、モジュールは import キーワードを使用して別のモジュールをインポートできます。
ソース ファイルは、通常はモジュールに属する単一のソース コード ファイルであり、ソース ファイルには複数のクラスと関数の定義を含めることができます。
Swift は、コード内のエンティティに対して、public、internal、fileprivate、および privateの 4 つの異なるアクセス レベルを提供します。
アクセスレベル | 意味 |
---|---|
公共 | 独自のモジュールでソース ファイル内の任意のエンティティにアクセスできます。また、モジュールを導入することで、他のユーザーもソース ファイル内のすべてのエンティティにアクセスできます。 |
内部 | 独自のモジュール内のソース ファイル内のエンティティにはアクセスできますが、他のユーザーはこのモジュール内のソース ファイル内のエンティティにアクセスできません。 |
ファイルプライベート | ファイル内ではプライベートであり、現在のソース ファイルでのみ使用できます。 |
プライベート | クラス内でのみアクセスでき、クラスまたは構造体のスコープ外からはアクセスできません。 |
public は最高のアクセス レベルであり、private は最低のアクセス レベルです。
例
パブリック クラス SomePublicClass {}
内部クラス SomeInternalClass {}
fileprivate クラス SomeFilePrivateClass {}
プライベート クラス SomePrivateClass {}
public var somePublicVariable = 0
Internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
特に指定がない限り、エンティティはデフォルトのアクセス レベル「内部」を使用します。
アクセス レベルが指定されていない場合、デフォルトは内部です。
class SomeInternalClass {} // アクセス レベルは内部です
let someInternalConstant = 0 // アクセス レベルは内部です
機能タイプのアクセス権限
関数のアクセス レベルは、関数のパラメーターの型と戻り値の型のアクセス レベルから導出する必要があります。
次の例では、アクセス レベルを明示的に宣言せずに、someFunction という名前のグローバル関数を定義します。
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 関数の実装
}
関数内のクラスの 1 つである SomeInternalClass のアクセス レベルは内部であり、もう 1 つのクラスである SomePrivateClass のアクセス レベルはプライベートです。したがって、タプルのアクセス レベルの原則に従って、このタプルのアクセス レベルはプライベートです (タプルのアクセス レベルは、タプル内の最も低いアクセス レベルを持つ型と一致します)。
関数の戻り値の型のアクセス レベルはプライベートであるため、private 修飾子を使用して関数を明示的に宣言する必要があります。
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 関数の実装
}
関数を public または external として宣言したり、デフォルトのアクセス レベル external を使用したりするのは間違いです。そうすると、プライベート レベルの戻り値にアクセスできなくなります。
列挙型のアクセス権限
列挙内のメンバーのアクセス レベルは列挙から継承されます。列挙内のメンバーに対して異なるアクセス レベルを宣言することはできません。
例
たとえば、次の例では、列挙 Student がパブリック レベルとして明示的に宣言されており、そのメンバー Name と Mark のアクセス レベルも public です。
例
public enum Student { case Name(String) case Mark(Int,Int,Int) } var stackDetails = Student.Name("Swift") var stackMarks = Student.Mark(98,97,95) switch stackMarks { case .Name( let stackName): print("学生名: \(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("学生成绩: \(Mark1),\(Mark2),\(Mark3) )") }
上記のプログラムを実行した出力結果は次のとおりです。
学生のスコア: 98,97,95
サブクラスのアクセス許可
子クラスのアクセス レベルは、親クラスのアクセス レベルを超えてはなりません。たとえば、親クラスのアクセス レベルが内部の場合、サブクラスのアクセス レベルをパブリックとして宣言することはできません。
例
public class SuperClass { fileprivate func show() { print("Superclass") } } // アクセス レベルはスーパークラスより高くすることはできません public > external 内部クラス SubClass: SuperClass { override external func show() { print("Subclass" ) } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()
上記のプログラムを実行した出力結果は次のとおりです。
スーパークラス サブクラス
定数、変数、属性、添字のアクセス権
定数、変数、プロパティには、その型よりも高いアクセス レベルを設定することはできません。
たとえば、パブリック レベルのプロパティを定義しますが、その型はコンパイラで許可されていないプライベート レベルであるとします。
同様に、添え字には、インデックス型や戻り値の型よりも高いアクセス レベルを設定することはできません。
定数、変数、プロパティ、添字インデックスの定義タイプがプライベートである場合、アクセス レベルがプライベートであることを明示的に宣言する必要があります。
プライベート var privateInstance = SomePrivateClass()
Getter および Setter のアクセス権限
定数、変数、プロパティ、添字インデックスのゲッターとセッターのアクセス レベルは、それらが属するメンバーのアクセス レベルから継承されます。
setter のアクセス レベルは、対応する getter のアクセス レベルよりも低くすることができるため、変数、プロパティ、または添え字インデックスの読み取りおよび書き込み権限を制御できます。
例
class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("Counter: \(newTotal)") } DidSet{ if counter > oldValue { print("新しい増加量\(counter - oldValue)") } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800
カウンタのアクセス レベルは fileprivate であり、ファイル内でアクセスできます。
上記のプログラムを実行した出力結果は次のとおりです。
カウンタ:100 新規追加数量 100 カウンタ:800 新規追加数量 700
コンストラクターとデフォルトのコンストラクターへのアクセス
初期化
カスタム初期化メソッドのアクセス レベルを宣言できますが、それが属するクラスのアクセス レベルを超えてはなりません。例外は必須コンストラクターであり、そのアクセス レベルは、コンストラクターが属するクラスのアクセス レベルと同じである必要があります。
関数またはメソッドのパラメーターと同様、初期化メソッドのパラメーターのアクセス レベルを初期化メソッドのアクセス レベルより低くすることはできません。
デフォルトの初期化方法
Swift は、構造体とクラスに対してデフォルトのパラメーターなしの初期化メソッドを提供します。これは、すべてのプロパティに対する代入操作を提供するために使用されますが、特定の値は与えられません。
デフォルトの初期化メソッドのアクセス レベルは、そのタイプのアクセス レベルと同じです。
例
各サブクラスの init() メソッドの前に required キーワードを使用して、アクセス許可を宣言します。
例
class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()
上記のプログラムを実行した出力結果は次のとおりです。
10 30 10
プロトコルアクセス権
プロトコルのアクセス レベルを明示的に宣言する場合は、宣言したアクセス レベルの範囲内でのみプロトコルが使用されるようにする必要があることに注意してください。
パブリック アクセス レベルを使用してプロトコルを定義すると、そのプロトコルによって提供される必要な機能もパブリック アクセス レベルを持つようになります。これは、メンバーのアクセス レベルが内部であるパブリック アクセス レベルを持つ他の型など、他の型とは異なります。
例
パブリックプロトコル TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // ローカルストレージ init(no1: Int) { self.no1 = no1 // 初期化 } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // 便利なメソッドの必須 オーバーライド には便利なパラメータが 1 つだけ必要です init (no1: Int) { self.init(no1) :no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50)
print("結果は: \(res.no1)")
print("結果は: \(show.no1)")
print("結果は: \(show.no2)")
上記のプログラムを実行した出力結果は次のとおりです。
解像度: 20 解像度: 30 解像度: 50
拡張アクセス
条件が許せば、クラス、構造体、列挙型を拡張できます。拡張メンバーは、元のクラス メンバーと同じアクセス レベルを持つ必要があります。たとえば、パブリック型を拡張する場合、追加する新しいメンバーは、元のメンバーと同じデフォルトの内部アクセス レベルを持つ必要があります。
あるいは、拡張機能のアクセス レベルを明示的に宣言して (たとえば、プライベート拡張機能を使用して)、拡張機能内のすべてのメンバーに対して新しいデフォルトのアクセス レベルを宣言することもできます。この新しいデフォルトのアクセス レベルは、個々のメンバーによって宣言されたアクセス レベルによって引き続きオーバーライドできます。
一般的なアクセス許可
ジェネリック型またはジェネリック関数のアクセス レベルは、ジェネリック型、関数自体、およびジェネリック型パラメーターの中で最も低いアクセス レベルです。
例
public struct TOS<T> { var items = [T]() private mutating func Push(item: T) { items.append(item) } mutating func Pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("泛型") print(tos.items) tos.push("类型パラメータ") print(tos. items) tos.push("类型パラメータ名") print(tos.items) let deletetos = tos.pop()
上記のプログラムを実行した出力結果は次のとおりです。
["Swift"] ["Swift", "Generics"] ["Swift", "Generics", "型パラメータ"] ["Swift", "ジェネリック", "型パラメータ", "型パラメータ名" ]
タイプエイリアス
定義したタイプのエイリアスは、アクセス制御の目的で別のタイプとして扱われます。タイプ エイリアスのアクセス レベルは、元のタイプのアクセス レベルより高くすることはできません。
たとえば、プライベート レベルのタイプ エイリアスはパブリック、内部、またはプライベート タイプに設定できますが、パブリック レベルのタイプ エイリアスはパブリック レベルのタイプにのみ設定でき、内部またはプライベート タイプには設定できません。レベルタイプです。
注: このルールは、プロトコル準拠のためのエイリアス関連タイプにも適用されます。
例
public protocol Container { typealias ItemType mutating func append(item:ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // オリジナルの Stack<T> 実装 var items = [T]() mutating func Push(item: T) { items.append(item) } mutating func Pop() -> T { return items.removeLast() } // コンテナプロトコルに準拠 mutating func append (アイテム: T) { self.push(item) } var count: Int { return items.count }
subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // 両方のコンテナに同じ数のアイテムが含まれていることを確認します if someContainer.count != anotherContainer.count { return false } // アイテムの各ペアを確認して、 0.. の i と同等かどうかを確認します。 <someContainer.count { if someContainer[i] != anotherContainer[i] { return false }
}
// すべての項目が一致するため、 true を返します return true
}
var
tos = Stack<String>()
tos.push("Swift")
print(tos.items)
tos.push("泛型")
print(tos.items )
tos.push("Where 语句")
print(tos.items)
var eos = ["Swift", "泛型", "Where 语句"]
print(eos)
上記のプログラムを実行した出力結果は次のとおりです。
["Swift"] ["Swift"、"Generics"] ["Swift"、"Generics"、"Where ステートメント"] ["Swift"、"Generics"、"Where ステートメント"]