Swift/構造体 †struct 型名 { [変数/英数定義] [イニシャライザ定義] [メソッド定義] [その他の定義] } struct内の変数はプロパティ(property)と呼ばれる。 struct Date { var year: Int var month: Int var day: Int } 構造体の初期値 †初期化に使う手続きがイニシャライザ (initializer) である。 構造体の定義には、各プロパティの初期値を記述しておくことができる(default initializer)。 struct Date { var year:Int = 2016 // 型を明記した場合 var month = 7 // 型を初期値から推測させた場合 var day = 28 } var d = Date() print(d.day) // 28 と表示される 全項目イニシャライザ †個々のプロパティの値を指定してインスタンスを生成することができる(memberwise initializer)。 var a = Date(year:2016, month:5, day: 13) 構造体は値型であり、代入はコピーによって行われる。 定数に代入した構造体の各プロパティは値を変更できない。 let b = Date(year: 2016, month: 5, day: 13) b.year = 2017 // エラー 構造体の定数プロパティ †構造体のプロパティとして定数を宣言することができる。 定数の値は指定できない。 struct Time { let in24h: Bool = false // 定数のプロパティ var hour = 0, min = 0 // 変数のプロパティ } var t1 = Time() // 0:00 (12時間制) var t2 = Time(hour: 8, min: 50) // 8:50 (12時間制) var t3 = Time(in24h:true, hour:7, min:0) // 定数の値を与えようとするとエラーとなる。 初期値が与えられていない定数プロパティがある構造体は、初期値を設定しなければならない。 struct Time { let in24h: Bool // 初期値が指定されていない定数 var hour = 0, min = 0 } var t4 = Time(in24h:true, hour:7, min:0) // 7:00 (24時間制) イニシャライザの定義 †イニシャライザは init という名前で定義する。 init以外の名前(たとえば initWithData?: )を指定することはできない。 イニシャライザの中では、その構造体のプロパティに自由にアクセスできる。 定数プロパティの初期化もこの中で行う。 イニシャライザ内で構造体のメソッドを使うことができるのは、すべてのプロパティの 初期設定が済んでから。したがって、メソッドを使ってプロパティの初期値を決めることはできない。 struct Date { var year, month, day:Int init() { year = 2016; month = 5; day = 13 // self.year = 2016 のように書くこともできる } } var m = Date() print(m.year) // 2016と表示される 複数個のイニシャライザを定義する †関数の定義とは異なり、イニシャライザの定義では第1引数として記述したキーワードも 外部引数名として使われる。 struct Time { let in24h: Bool // 初期値のない定数 var hour = 0, min = 0 init(hour:Int, min:Int) { in24h = false // 定数の初期化 self.hour = hour self.min = min } init(hourIn24 h:Int) { in24h = true // 定数の初期化 hour = h } init(_ hour:Int) { self.init(hourIn24: hour) // 上のイニシャライザを使う // in24h = true /* 定数の初期化が終わっているので、これはエラーになる */ } } var a = Time(hour:10, min:30) // 12時制 10:30 var b = Time(hourIn24:15) // 24時制 15:00 var c = Time(1) // 24時制 1:00 var d = Time() // エラー var e = Time(in24h:True, hour:13, min: 30) // エラー init を定義すると、default initializer や memberwise initializer は使えなくなる。 必要ならば、それと同じ呼び出し形式を持つinitを定義する。 関数とイニシャライザ表記のまとめ †
以下に関しては、どちらも指定可能である。
他の構造体を含む構造体 †struct DateWithTime { var date = Date() var time = Time(hour:0, min:15) } var u = DateWithTime() print(u.date.year) ネスト型 †struct SimpleTime { var hourr, min: Int init(_ hour:Int, _ min:Int) { self.hour = hour self.min = min } } struct PointOfTime { struct Date { var year, month, day: Int } typealias Time = SimpleTime var date: Date var time: TIme init(year:Int, month:Int, day:Int, hour:Int, min:Int) { date = Date(year:year, month:month, day:day) time = Time(hour,min) } } var a = PointOfTime(year:2024,month:11,day:7,hour:14,min:55) print(a.date.month) // 11と表示される var b = PointOfTime.Date(year:2022,month:11,day:6) print(b.year) // 2022と表示される a.time = PointOfTime.Time(10,21) print(a.time.hour) // 10と表示される メソッド †struct Time { let hour, min : Int func add(min:Int) -> Time { var m = self.min + min var h = self.hour if m >= 60 { h = (h + m / 60) % 24 m %= 60 } return Time(hour:h, min:m) } func toString() -> String { let h = hour < 10 ? "0\(hour)" : "\(hour)" // 2桁の文字列に let m = min < 10 ? "0\(min)" : "\(min)" return h + ":" + m } } var t1 = Time(hour:22, min:45) var t2 = t1.add(140) print(t1.toString()) // "22:45" と表示される print(t2.toString()) // "01:05" と表示される 構造体の内容を変更するメソッドは、funcの前に mutating というキーワードを置く必要がある。 struct Clock { var hour = 0, min = 0 mutating func advance(min:Int) { let m = self.min + min if m >= 60 { self.min = m % 60 let t = self.hour + m / 60 self.hour = t % 24 } else { self.min = m } } mutating func inc() { self.advance(1) } func toString() -> String { 省略 } } var tic = Clock(hour:19, min:40) tic.advance(19) // 19:59 と表示される tic.inc() print(tic.toString() // 20:00 と表示される 構造体の全インスタンスから呼ばれるようなメソッドは、静的メソッドまたはタイプメソッド (type method) と呼ばれる。 タイプメソッドの内部では、その定義を含む構造体(インスタンスではなく、構造体の型自体を)を表すために self を使う。 initの中で、全てのプロパティが初期化される前にインスタンスメソッドを呼び出すことはできないが、 タイプメソッドを呼び出すことはできる。 struct Date { var year, month, day: Int static func isLeap(y: Int) -> Bool { return (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0) } static func daysOfMonth(m:Int, year:Int) -> Int { switch m { case 2: return isLeap(year) ? 29 : 28 // self.isLeap(year)と記述してもよい case 4,6,9,11: return 30 default: return 31 } } } print(Date.isLoop(2000)) // true が表示される print(Date.daysOfMonth(2, year:2000)) // 29 が表示される プロパティ †タイププロパティは、定義の先頭に static というキーワードを付ける。 タイププロパティはその構造体全体に関係する情報を表す。 struct DateWithString { let string: String let year, month, day: Int static let mons = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] static var longFormat = false init(_ y:Int, _ m:Int, _ d:Int) { year = y; month = m; day = d string = DateWithString.longFormat ? DateWithString.longString(y,m,d) : DateWithString.shortString(y,m,d) } static func twoDigits(n:Int) -> String { let i = n % 100 return i < 10 ? "0\(i)" : "\(i)" } static func longString(y:Int, _ m:Int, _ d:Int) -> String { return "\(y)-" + twoDigits(m) + "-" + twoDigits(d) } static func shortString(y:Iint, _ m:Int, _ d:Int) -> String { return twoDigits(d) + mons[m-1] + twoDigits(y%100) } } let a = DateWithString(2025,1,20) // longFormat = false print(a.string) // "20Jan25"と表示される DateWithtstring.longFormat = true let b = DateWithString(2025,1,21) print(b.string) // "2025-01-20"と表示される |