Swift 単項マイナス演算子のオーバーロード

sample1-1

let num1=3
let num2=(-num1)
print(num2)
-3

2行目で定数num1に対して単項マイナス演算子を作用させています。その結果定数num2に-3がセットされ、表示されています。期待通り、簡単なコードですが、単項マイナス演算子について我々が何の実装もせずに、期待通りの挙動が実現されているわけですね。


sample1-2

struct Container {
    var width:Int=0
    var height:Int=0
}
let con1=Container(width:10,height:20)
let con2=Container(width:20,height:30)

print(con1)
print(-con1)
main.swift:19:8: error: unary operator '-' cannot be applied to an operand of type 'Container'
print(-con1)

sample1-2では独自の型(構造体)Containerを定義し、インスタンス化、そのインスタンスをセットした定数con1に対して単項マイナス演算子を付けています。結果はエラー。


独自の型に対して単項マイナス演算子を使用できるようにするには、単項マイナス演算子の演算子メソッドを定義する必要があります。…

Swift 算術加算演算子のオーバーロード

sample1-1

let int1:Int=10
let int2:Int=20
let plus:Int=int1+int2
print(plus)
30

sample1-1は、Int型の変数int1,int2に値をセットして+演算子(算術加算演算子)で加算して結果を表示する、という何の変哲もないコードです。まさに何の変哲もないのですが、

つまり+演算子に関して我々が全く何の実装もせずに、当たり前に加算が行われている、ということですね。

このようにInt型ではプログラマは+演算子に関して何も実装せずとも、普通に加算が行われるわけですが、これが自分で作った型の場合はどうか。


sample1-2

struct Container {
    var width:Int=0
    var height:Int=0
}

let con1=Container(width:10,height:20)
let con2=Container(width:20,height:30)

print(con1+con2)

 

main.swift:17:11: error: binary operator '+' cannot be applied to two 'Container' operands
print(con1+con2)
      

Swift Equatableプロトコルに準拠する

1.基本

sample1

struct Container {
    var x:Int=0
    var y:Int=0
    var z:Int=0
}

extension Container : Equatable{
    static func ==(left:Container,right:Container)->Bool{
        return (left.x==right.x)&&(left.y==right.y)&&(left.z==right.z)
    }
}

var con1=Container(x:1,y:1,z:1)
var con2=Container(x:4,y:4,z:4)
print(con1==con2)
con2.x=1
con2.y=1
con2.z=1
print(con1==con2)
false
true

独自の型(sample1ではContainer型)に==演算子を使いたい場合、Equatableプロトコルに準拠する必要があります。そのための記述が7~11行目。extensionキーワードを用いてEquatableプロトコルへの準拠を宣言しています。


2.簡易的な実装

sample1の形が基本ですが、以下のいくつかのシンプルなケース

●すべてのストアドプロパティがEquatableプロトコルに準拠している構造体
●すべての連想値がEquatableプロトコルに準拠している列挙型
●連想値を持たない列挙型…

Swift プロトコルの連想型(associatedtype)

1.プロトコルの連想型(associatedtype)の基本

プロトコルの基本的な定義では、

  • プロパティの型
  • メソッドの引数の型
  • メソッドの戻り値の型

上記のような型をプロトコルの定義で決定するのが普通ですが、プロトコル定義時ではなく、準拠する型(クラス・構造体など)の定義時に上記の型を決定する方法もあります。これがプロトコルの連想型(associatedtype)。

プロトコルの連想型(associatedtype)の定義方法

protocol プロトコル名{
    associatedtype 連想型名
    
    var プロパティ名:連想型名
    func メソッド名(引数名:連想型名)
    func メソッド名()->連想型名
}

プロトコル定義時に上記のように連想型を宣言し、そのプロトコルを準拠する型の定義で連想型の型を具体的に指定する、という感じですね。


連想型の指定方法

typealias 連想型名=指定する型名

sample1-1

protocol Container{  //(1)
    associatedtype SVT
}

protocol SomeData{  //(2)
    associatedtype ValueContainer:Container where ValueContainer.SVT:Equatable
}


struct IntContainer:Container{  //(3)
    

2020/1/12 Swift エクステンション(extension)

エクステンション(Extensions)は、既存のクラス・構造体・列挙型・プロトコルに対して機能を追加できます。

Swiftのエクステンションで以下のようなことができます。

  • コンピューテドプロパティ(インスタンスプロパティとタイププロパティ)を追加
  • インスタンスメソッドとタイプメソッドを定義
  • イニシャライザを追加
  • サブスクリプとを定義
  • 既存の型の定義内に新しい型の定義・新しい型の使用
  • 既存の型をあるプロトコルに準拠させる

既存の型にストアドプロパティを追加することはできません。

既存の型にプロパティオブザーバを追加することはできません。

NOTE

エクステンションは新しい機能を型に追加しますが、既存の機能をオーバーライドすることはできません。

1.すでにある型にプロパティ・メソッドなどを追加する。

1-1.メソッドを追加する

extension 要素を追加したい型名{
    追加したい要素
}

sample1-2

extension String{
    func exStr(){
        print("この文字列は\(self.count)文字です")
    }
}

let str1="ghjjkkdddd"
str1.exStr()
この文字列は10文字です

sample1-2では、既にSwiftの標準ライブラリの用意されているString型にexStr()メソッドを追加しています。

1-2.コンピューテドプロパティを追加する。

 

2.プロトコルエクステンション

既存のプロトコルに対して、プロパティやメソッドの実装を追加する。(sample2-1)

sample2-1

protocol Books{
    var color:String{get}
    

Swift ジェネリクスとオーバーロード

汎用的な関数・メソッドを作りたい

汎用的とは?汎用的とは国語的には「広くいろいろな方面に用いること」のような意味ですが、この場合の汎用的は、「複数の種類の型の引数を受け取れる関数・メソッド」くらいの意味でしょうか。

sample1-1

func plus(_ x:Int,_ y:Int)->Int{
    return x+y
}

print(plus(1,1))
print(plus(1,12))
2
13

sample1-1はInt型の二つの引数x,yを受け取り、その和を返す関数plus()を定義しています。上記のようにx,yにInt型の引数が渡された場合その和を得ることができます。


sample1-2

func plus(_ x:Int,_ y:Int)->Int{
    return x+y
}

print(plus(1,1.2))
main.swift:7:14: error: cannot convert value of type 'Double' to expected argument type 'Int'
print(plus(1,1.2))
             ^~~
             Int( 

Swift プロトコル、プロトコルのプロパティ

プロトコルにプロパティを定義する

protocol プロトコル名{
    var プロパティ名 : 型名{get} //ゲッターのみ必要
}

protocol プロトコル名{
    var プロパティ名 : 型名{get set}  //ゲッターとセッターが必要
}

プロトコルにプロパティを定義する場合上記のようにプロパティ名・型名・ゲッターとセッターの有無を定義します。

プロパティは常にvarキーワードで宣言します。


(プロパティの実装を要求する)プロトコルに準拠する

プロトコルは、準拠する型(sample1では構造体)に対して実装を要求します。

準拠する型が、プロトコルが要求するプロパティを実装する方法、つまりプロトコルに準拠する方法が以下です。

sample1

protocol ProtocolA{
    var prop:Int{get}
}

struct StructA:ProtocolA{
    var prop:Int  //←変数のストアドプロパティ
}

struct StructB:ProtocolA{
    let prop:Int  //←定数のストアドプロパティ

Swift プロトコル、パート2

プロトコルは型注釈(型アノテーション)などの型指定に使用できます。つまり構造体・クラス・列挙型と同じように「型として使用できる」ということですね。

sample1

protocol ProtocolA{
    var prop:Int{get}
}

func printProp(a:ProtocolA){
    print(a.prop)
}

struct StructA:ProtocolA{
    var prop=1002
}

let structA=StructA()
printProp(a:structA)
1002

sample1の関数printPropはProtocolAプロトコルに準拠した型のインスタンスを引数として受け取り、受け取ったインスタンスのpropプロパティを表示する関数です。引数の型注釈としてProtocolA型を指定していますので、引数として渡せるのはProtocolAに準拠した型のインスタンスのみ、ということになります。

sample1では、ProtocolAに準拠した構造体StructAを定義し、StructA型のインスタンスを生成、それをprintProp関数に渡しています。

structAのプロパティpropには1002がセットされていますので、それが表示されます。


 

Swift プロトコル、パート1

プロトコルの基本

プロトコルの定義方法

構造体・クラス・列挙型などの型がプロトコルAに準拠するとプロトコルAの持つ機能・特性を準拠した型も持つことができます。

プロトコルAに準拠する方法

複数のプロトコルに準拠した型を定義する場合は上記のように,(カンマ)で区切って記述します。

上記のように記述したうえで、準拠するプロトコルが要求するプロパティ・メソッドなどを型の中で実装すれば、プロトコルに準拠した型を定義できます。

sample1

protocol ProtocolA{
    func methA()
}

struct StructA:ProtocolA{
    func methA(){
        
    }
}

sample1ではProtocolAという名のプロトコルを定義し、次にProtocolAに準拠した構造体StructAを定義しています。

ProtocolAは、準拠する型(StructA)に、メソッドmethA()の実装を要求していますので、StructAの定義内でメソッドmethA()を定義する必要があります。

プロトコルが要求するプロパティ・メソッドを準拠する型内で定義していないとコンパイルエラーとなります。(sample2)

sample2

protocol ProtocolA{
    func methA()
    func methB()
}

struct StructA:ProtocolA{
    func methA(){
        
    }
}
main.swift:7:8: error: type 'StructA' does 

Swiftの基本 イニシャライザ、パート2

パート1でイニシャライザの定義、挙動を見ました。そこで次のサンプル(sample1)

sample1

struct Sample{
    let prop1:Int    //構造体定義時にプロパティを初期化していない
    let prop2:String
    let prop3:String
    //イニシャライザを定義していない
}

let sample1=Sample(prop1:10,prop2:"wow",prop3:"good")

print(sample1.prop1)
print(sample1.prop2)
print(sample1.prop3)
10
wow
good

構造体Sample型に定義されているのは

prop1(Int型)

prop2(String型)

prop3(String型)

のみで、イニシャライザは定義されていません。さらに構造体Sample定義時にプロパティを初期化していません。しかし実際実行すると期待通りに結果が出力され、エラーは出ません。つまりイニシャライザを定義していないが、すべてのプロパティが初期化されています。


構造体の場合、イニシャライザを自分で定義しない場合、自動的にメンバワイズイニシャライザ(memberwise initializer)が内部的に用意されます。

メンバワイズイニシャライザは、型が持つすべてのストアドプロパティを引数に持つイニシャライザで、上記の例だと、

init(prop1:Int,prop2:String,prop3:String){
    self.prop1=prop1
    self.prop2=prop2
    self.prop3=prop3
}

このようなイニシャライザを定義したのと同じ状態(実際には定義していない)になります。

ということで、sample1を見ると一見エラーが出そうですが、実際エラーは出ずにすべてのプロパティが初期化されている、ということですね。