woshidan's blog

あいとゆうきとITと、とっておきの話。

UIViewのメソッドにCGContextRefオブジェクトを渡してStoryBoard上に図形を描画する

内容

上の本を参考にiOSのUIViewと戯れています。

  • 図形の描画
    • 色や太さなどの設定と描画にCGContextRefを使う
    • 直線の描画
    • 円の描画
    • 塗りつぶしについて

タッチイベントの処理、タイマー処理までやったけど、それは一旦寝て起きてからまとめます。

図形の描画

色や太さなどの設定と描画にCGContextRefを使う CGContextRefのオブジェクトを使います。

色とか透明度とか太さとかいろいろもって描画の設定を持ったクラスの名前をContextとして~みたいな話を読んでから、AndroidのContextよりダイレクトにそういう事例に見えるクラスが来た~という感じです*1

基本的に、CGContextRefのオブジェクトをUIViewのインスタンスメソッド?(クラスメソッドかもしれないのでアップ前に調査)に渡して設定したり描画したりします。

// UIGraphicsGetCurrentContext()メソッドで取得
// 塗りつぶし用と線描画用で分かれている
CGContextSetRGBFillColor(_contextRef, CGFloat(r) / 255.0, CGFloat(g) / 255.0, CGFloat(b) / 255.0, CGFloat(alpha))
CGContextSetRGBStrokeColor(_context, CGFloat(r) / 255.0, CGFloat(g) / 255.0, CGFloat(b) / 255.0, CGFloat(alpha))

// 図形を描画するときに上のようなメソッドで設定を行ったcontextRefを渡す
CGContextStrokePath(_contextRef)

直線の描画

  • CGContextMoveToPoint(_context, CGFloat(x), CGFloat(y))で始点を設定
  • CGContextAddLineToPoint(_context, CGFloat(x1), CGFloat(y1))で次の頂点を設定(複数設定すると折れ線や多角形が書ける)
  • 最後にCGContextStrokePath(_contextRef)で描画

円の描画

CGContextAddEllipseInRect(_context, CGRectMake(CGFloat(x), CGFloat(y), CGFloat(width), CGFloat(height))) // 楕円の大きさは円が内接する四角形を渡して指定
CGContextStrokePath(_context)

塗りつぶしについて

// 四角形
CGContextFillRect(_contextRef, someRect)

// 円
// 楕円の大きさは円が内接する四角形を渡して指定
CGContextFillRect(_contextRef, someRect)

// 点を足して...とかしていないので CGContextStrokePath(_contextRef)は不要

うちの周りはドードーばかりいますが、ルアーモジュールの桜に近づいたところ、イーブイがGETできたので嬉しいです。

*1:お仕事でAndroidで線などの描画をやっていたときはPaintオブジェクトを使っていた

画像をStoryBoard上で描画する

内容

上の本を参考にiOSのUIViewと戯れています。

  • 画像ファイルをプロジェクトに追加する
  • 画像をStoryBoardに配置する
  • 画像ファイルのサイズとフォーマットについて
  • 2倍, 3倍の画像とグループ化した形で追加する
  • 画像ファイルの読み込み

画像ファイルをプロジェクトに追加する

プロジェクト本体に追加する場合の手順

  • プロジェクト直下のプロジェクト名フォルダを右クリック
  • "Add Files to PROJECT_NAME"を選択
  • 追加したいファイルを選択してAddをクリック

画像をStoryBoardに配置する

  • StoryBoardの中で画像を追加したいViewを選択します
  • 右側のユーティリティエイアの下側にViewに追加できる要素などが表示できるタブが並んでいます*1が、一番右のタブ(メディアライブラリ)を開きます
  • メディアライブラリのタブにプロジェクトに追加された画像などの一覧があるので、その中から追加したい要素をStoryBoard上にドラッグします

画像は https://placehold.it/ にて取得しています。

画像ファイルのサイズとフォーマットについて

  • プログラミングで指定するテキストや図形のサイズはポイント単位なので、端末ごとの解像度を気にしなくて良い
  • 1ポイントのドットのことをスケールと呼ぶ
    • UIScreen.mainScreen().scale
  • 画像はスケールに合わせて自動的に高解像後にはならないのでファイル名に「@2x」「@3x」をつけた2倍, 3倍の解像度の画像を用意する
  • 基本的には png を利用する

2倍, 3倍の画像とグループ化した形で追加する

  • 元の解像度の画像, 2倍, 3倍の解像度の画像をまとめてプロジェクト名のフォルダにドラッグ & ドロップ
  • もし、ダイアローグが開くので、 追加するターゲットが編集中のプロジェクトか確認してOKを押す

画像ファイルの読み込み

  • UIImage(named: "loadingTarget.png")で画像ファイルの読み込み
  • UIImage#drawAtPoint(CGPoint)で描画する位置を指定
  • UIImage#drawInRect(CGRect)でCGRectで指定した領域へと画像の拡大縮小描画が可能
    • UIImage.sizeのプロパティ(width: 幅, height: 高さ)を取得して、それを2倍, 3倍して2倍や3倍に拡大しで描画、など

明日は本気出す(と毎日言っている気も...)。

*1:ライブラリペインというそうです

カスタムビューの配置方法と文字のフォント設定、描画領域のサイズ取得について

今日から少しずつ慣れてこーってことでターミナルでREPLからXCodeに戻ってきたんですが、XCodeって入力してると、にょん!って補完が出てちょっと楽しいですね...。

内容

上の本を参考にiOSのUIViewと戯れています。

  • カスタムビューの書き方
  • 文字の配置について
    • フォントの設定の仕方
    • サイズの取得の仕方
  • カスタムビューをStoryBoardに配置する

カスタムビューの書き方

import UIKit

// UIViewから継承して書いていくのは
// AndroidのCustomViewを書くようなノリなのでしょうか
class StringEx : UIView {
  // 描画時に呼び出されます
  override func drawRect(rect: CGRect) {
    // ここに描画内容を書く
  }
}

文字の配置について

// UIViewクラスのインスタントメソッド内で
// stringはString型のリテラル
string.drawAtPoint(CGPointMake(CGFloat(x), CGFloat(y)))

Androidのひよっこなので、TextViewみたいなのがあって、それを配置してからそこに描画する文字を属性として設定する、みたいなのが基本かと思っていたので、Stringオブジェクトのインスタンスメソッドで描画処理がかけることに少し驚いて検索してみたのですが。

http://secondflush2.blog.fc2.com/blog-entry-1002.html

UIKitフレームワークでの拡張らしいですね...*1

// ターミナルで叩いてみてSwiftそのもののStringには
// そんなメソッドないみたいです
 113> "Test".drawAtPoint
repl.swift:113:1: error: value of type 'String' has no member 'drawAtPoint'
"Test".drawAtPoint
^~~~~~ ~~~~~~~~~~~

// シグネチャが間違っている時に出るエラー
// repl.swift:116:20: error: argument passed to call that takes no arguments

フォントの設定の仕方

let attrs = [
    // フォントをメソッドで生成する場合
    // (system/bold/italic)FontOfSize(size : CGFloat)
    // で標準/太字/斜体のフォントが指定できる
    
    // iOSで利用出来るフォントである必要があるが、フォント名をUIFontのイニシャライザ
    // に渡すとそのフォントに対応したUIFontのオブジェクトが利用出来る
    NSFontAttributeName: UIFont.systemFontOfSize(CGFloat(size)),
    NSForegroundColorAttributeName: color
]
// フォントの設定が入ったディクショナリを
// withAttributesのパラメータとして渡す
str.drawAtPoint(CGPointMake(CGFloat(x), CGFloat(y)), withAttributes: attrs)

サイズの取得の仕方

// strの文字列を設定したパラメータによって描画した場合の
// 描画領域のサイズを表すGCSizeクラスが帰ってきます。
// struct GCSize {
//   var width: CGFloat
//   var height: CGFloat
// }
let attrs = [
            NSFontAttributeName: UIFont.systemFontOfSize(CGFloat(size))]
        return str.sizeWithAttributes(attrs)

カスタムビューをStoryBoardに配置する

http://qiita.com/kikuchy/items/f1d6731d804b63cf7a29

上の記事を参考にやってみました。

手順は、

  • カスタムビューの親クラスにあたるビューをStoryBoardに配置
  • 配置したビューをクリック
  • 右側のユーティリティエリアshow the Identity Inspecterと吹き出しが出るタブ(アイデンティティインスペクタ)を開く
  • Custom Classの項目にカスタムビューのクラス名を追加

ひとまず、しばらくはサンプルなぞってきゃっきゃしているだけ感じです。

JavaScriptではUI作るための引き出しがなくてMVCとかは多少はわかっても実際いろいろ動きを作るのが遅くて*2Androidの時は逆に最初に一通りwidgetクラス触って少しだけ救われたので、とりあえず面白がれる範囲でやっていこうかと思います。

*1:本当はNSStringが拡張されているけれど、SwiftからはObjective-CのNSStringはStringとして見える

*2:この辺とかで時間切れしてたりする http://woshidan.hatenablog.com/entry/2015/06/15/024121

はてな教科書の「プログラミング言語 Swift」を読んでます 7

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

のんびり読んでいきます。一旦ラストォ!

Generics

特定の型ではなくて、同じ種類ならどの型に対しても対応したい、みたいな場合に使います。

Javaだとだいたい一文字で表されていることが多いのですが、Swiftの場合、一文字ではない...のでしょうか。

配列など集合を扱うクラスなどで使うことが多そうです。

と思ったら、後ろの方の関数の例に func someFunction<T>() とあって安心です...。

 102> class Printer<Content> { 
 103.     var content : Content 
 104.     init(content: Content) { 
 105.         self.content = content 
 106.     } 
 107.     func printContent() { 
 108.         print(content) 
 109.     } 
 110. } 
 111> Printer<String>(content:"Print").printContent() 
Print
 112> Printer<Int>(content: 1234).printContent() 
1234

Associated types and type constraints

protocolにはassociatedtype ItemTypeのように関連する型を定義する機能があります。

protocolに準拠する側では、typealias ItemType = Itemのように書いてその型を指定できます。

こうすることでprotocolを引数に取る関数などにおいてもその型を抽象化できます。

また、<Type: SomeProtocol>のようにすることで特定のprotocolに準拠していたり、特定の型を継承している型であることを指定でき、さらにwhere節でその型について制約をかけることも可能です。

Access control

  • public は完全に公開され、どこからでもアクセス可能
  • internal はモジュール内部からだけアクセス
  • privateそのファイル内からだけアクセス
  • デフォルトは internal
  • テスト用にinternalのメソッドなどにアクセスが必要な場合は@testable import SomeModuleと書いて、テスト時のビルド設定をEnable Testabilityを有効にします

protectedはないのですか...!

Availability

実行されるプラットフォームやバージョンによって利用できない関数などを表すために @available 属性が利用できます。

とりあえずここまでで一旦おしまいで次からViewをいじいじします。

はてな教科書の「プログラミング言語 Swift」を読んでます 6

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

のんびり読んでいきます。もうちょい!

Casting

isでインスタンスの型を確認.

 84> class Cat: Animal { 
 85.     // 
 86.     // A'(A をオーバーライド。ここに required 修飾子がないと、サブクラス PersianCat に、このイニシャライザのオーバーライドを強制できない) 
 87.     // 
 88.     required init() { 
 89.         super.init() 
 90.     } 
 91. }
 92> class PersianCat: Cat {}
 93> var persianCat = Per
Available completions:
  PermutationGenerator
  PersianCat
 93> var persianCat = PersianCat()
persianCat: PersianCat = {
  __lldb_expr_47.Cat = {
    __lldb_expr_41.Animal = {
      name = "unknown"
    }
  }
}
 94> persianCat is Cat
$R8: Bool = true

asでキャストだが、スーパークラスにキャストする場合は、 as, サブクラスにキャストする場合は、as?(Optionalで失敗したらnil)as!(キャスト失敗でランタイムエラー)になります。

 95> persianCat as Cat
$R9: PersianCat = {
  __lldb_expr_47.Cat = {
    __lldb_expr_41.Animal = {
      name = "unknown"
    }
  }
}
 96> var cat = Cat()
cat: Cat = {
  __lldb_expr_41.Animal = {
    name = "unknown"
  }
}
 97> cat as? PersianCat
$R10: PersianCat? = nil
 98> cat as! PersianCat
Could not cast value of type 'Cat' (0x101336ab8) to 'PersianCat' (0x101336fe8).
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

どちらかというと、キャストに失敗したらelseみたいな例文が自信ないです...。

if let swift = source as? SwiftFile {
    compiled.append(swift.compile())
} else {
    compiled.append(source)
}
 128> if cat as? PersianCat { 
 129.     print("persian") 
 130. } 
repl.swift:128:8: error: optional type 'PersianCat?' cannot be used as a boolean; test for '!= nil' instead
if cat as? PersianCat {
       ^
   ((                ) != nil)

 128> if let persia = cat as? PersianCat { 
 129.     print("persian") 
 130. } 

letなどブロックの中で使う変数の定義が条件式に入った場合のif文がよくわからないみたいなので気をつけてみようと思います。

Protocol

いわゆるインタフェース。

  • プロパティ
  • メソッド
  • イニシャライザ
  • subscript*1

の宣言ができます。

また、メソッドは、 { get }, { get, set }で可変かを指定できます。

また、Protocol同士も継承できます。

  1> protocol Man { 
  2.     var firstName: String { get } 
  3.     var lastName: String { get } 
  4.     init(firstName: String, lastName: String) 
  5.     func printFullName()
  6. } 
  7> struct Japanese : Man { 
  8.     var firstName: String 
  9.     var lastName: String
 10.     init(firstName: String, lastName: String) { 
 11.         self.firstName = firstName
 12.         self.lastName = lastName 
 13.     } 
 14.     func printFullName() { 
 15.         print("\(firstName)\(lastName)") 
 16.     } 
 17. } 
 18> struct American : Man { 
 19.     var firstName: String 
 20.     var lastName: String 
 21.     init(firstName: String, lastName: String) { 
 22.         self.firstName = firstName 
 23.         self.lastName = lastName 
 24.     } 
 25.     func printFullName() { 
 26.         print("\(lastName) \(firstName)") 
 27.     } 
 28. } 
 29> var Taro = Japanese(firstName: "Yoshida", lastName: "Taro") 
Taro: Japanese = {
  firstName = "Yoshida"
  lastName = "Taro"
}
 30> var Ken = American(firstName: "Yoshida", lastName: "Ken")
Ken: American = {
  firstName = "Yoshida"
  lastName = "Ken"
}
 31> Taro.printFullName()
YoshidaTaro
 32> Ken.printFullName()
Ken Yoshida

 33> struct Arian : Man { 
 34.     var firstName: String { set {} } 
 35.   
 36.     init(firstName: String, lastName: String) { 
 37.         self.firstName = firstName 
 38.         self.lastName = lastName 
 39.     } 
 40.     func printFullName() { 
 41.         print("\(lastName) \(firstName)") 
 42.     } 
 43. } 
repl.swift:34:29: error: variable with a setter must also have a getter
    var firstName: String { set {} }
                            ^

Protocolは複数継承させることができますが、複数の protocol に適合していることを protocol<SomeProtocol, AnotherProtocol> と表現できるそうです。

 33> Taro as? protocol<Man>
$R0: Man? = (payload_data_0 = 0x0000000100509980 -> 0x00000001004fdde0 , payload_data_1 = 0x0000000000000000, payload_data_2 = 0x0000000000000000, instance_type = 0x00000001004f1218 nominal type descriptor for __lldb_expr_6.Japanese + 72, protocol_witness_0 = 0x00000001004f1110 protocol witness table for __lldb_expr_6.Japanese : __lldb_expr_4.Man in __lldb_expr_6) {
  payload_data_0 = 0x0000000100509980 -> 0x00000001004fdde0 
  payload_data_1 = 0x0000000000000000
  payload_data_2 = 0x0000000000000000
  instance_type = 0x00000001004f1218 nominal type descriptor for __lldb_expr_6.Japanese + 72
  protocol_witness_0 = 0x00000001004f1110 protocol witness table for __lldb_expr_6.Japanese : __lldb_expr_4.Man in __lldb_expr_6
}
 34> protocol Animal {}
 35> Taro as? protocol<Man, Animal> 
$R1: protocol<Animal, Man>? = nil

Extension

メタプロらしいものです。

computed プロパティやメソッド、イニシャライザや subscript を追加できますが、storedプロパティは追加できません。

 36> extension Int { 
 37.     var hex: String { 
 38.         return String(self, radix: 16) 
 39.     } 
 40. } 
 41> 245.hex
$R2: String = "f5"

また、extension は protocol も拡張でき、Protocol Extensionsといいます。

エラー処理

  • 例外を発生するメソッドに throws をつける
  • 例外を投げる時は、 throw
  • 例外は、ErrorTypeを継承した自分のクラスで定義できる
  • エラーを投げる関数の前に try をつけて、その関数を do { } catch ErrorType { } ...ブロックで囲む
    • 実際にエラーが発生しないことがわかっている場合は try! にしてエラーハンドリングしないことを明示できる(ただし実際に発生したらランタイムエラーになる)
    • try?にすると返り値がオプショナルになって、例外が発生した時はnilが返る
    • キャッチされなかったエラーはそのブロックの呼び出し元に伝播していきます
  • エラーを throws するクロージャの案件はおかわりで...
 42> enum SomeError : ErrorType { 
 43.     case Empty 
 44.     case Numeric 
 45. } 
 46> func throwError(str: String) throws -> String { 
 47.     if str.isEmpty { 
 48.         throw SomeError.Empty
 49.     } 
 50.     if let num = Int(str) { 
 51.         throw SomeError.Numeric 
 52.     } 
 53.     return str
 54. } 
 55> do { 
 56.   let res = try throwError("") 
 57.   print(res) 
 58. } catch SomeError.Empty { 
 59.     print("Empty") 
 60. } catch SomeError.Numeric { 
 61.     print("Numeric") 
 62. }
Empty
 63> do { 
 64.   let res = try throwError("123") 
 65.   print(res) 
 66. } catch SomeError.Empty { 
 67.     print("Empty") 
 68. } catch SomeError.Numeric { 
 69.     print("Numeric") 
 70. }
Numeric
 71> do { 
 72.   let res = try throwError("abc") 
 73.   print(res) 
 74. } catch SomeError.Empty { 
 75.     print("Empty") 
 76. } catch SomeError.Numeric { 
 77.     print("Numeric") 
 78. }
abc

defer

関数の最後に必ず実行したい処理がある場合、defer 文を利用することができます。

 79> func deferTest(str: String) { 
 80.     defer { 
 81.         print(str) 
 82.     } 
 83.     print(str + str + str) 
 84. } 
 85> deferTest("ABCD!")
ABCD!ABCD!ABCD!
ABCD!
// 途中でreturnする場合...?
 86> func deferTest2(str: String) { 
 87.     defer { 
 88.         print(str) 
 89.     } 
 90.     return
 91.     print(str + str + str) 
 92. } 
 93> deferTest2("ABCD!")
ABCD!ABCD!ABCD!
ABCD!
// 途中で例外を投げる場合
 94> func deferTest2(str: String) throws { 
 95.     defer { 
 96.         print(str) 
 97.     } 
 98.     throw SomeError.Empty
 99.     print(str + str + str) 
100. } 
 101> deferTest2("ABCD!") 
ABCD!
warning: could not load any Objective-C class information from the dyld shared cache. This will significantly reduce the quality of type information available.
 102>  

*1:ArrayやDictionaryのように[]の記法でクラスに値を変えさせるあれ

はてな教科書の「プログラミング言語 Swift」を読んでます 5

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

イニシャライザが難しかったのですが、のんびり読んでいきます。

Inheritance

class名の右に:をつけてスーパークラスを指定することでそのクラスを継承することができます。

スーパークラスの実装を明示的に利用する場合は、superキーワードを用います。

  1> class Man { 
  2.     func greeting() -> String { 
  3.         return "Hello" 
  4.     } 
  5. } 
  6> class Student : Man { 
  7.     override func greeting() -> String { 
  8.         return "\(super.greeting()), Teacher" 
  9.     } 
 10.     } 
 11> let taro: Man = Student()
taro: Student = {
  __lldb_expr_1.Man = {}
}
 12> taro.greeting()
$R0: String = "Hello, Teacher"

finalキーワードをclassfuncにつけることで継承を制限することができます。

Initializers and deinitializers

Initializers

  • class, enum, structではイニシャライザが定義できる
  • イニシャライザでは、Optional型ではないstoredプロパティ(let, varをつけるいわゆる普通のインスタンス変数ないしクラス変数)で、初期値が設定されていないものをすべて初期化する必要がある
  • イニシャライザは複数定義可能で、メソッドとは異なり第一引数からラベルを明記する
    • 型が同じでもラベルによってイニシャライザが区別される
  • 下記の条件で、class はデフォルトで空のイニシャライザを持つ
    • イニシャライザが一つも定義されておらず
    • かつイニシャライザで初期化するべき stored プロパティが存在しない
  • 下記の条件で、 structは全ての初期化すべき stored プロパティを定義順に指定するデフォルトのイニシャライザを持つ
    • イニシャライザが一つも定義されていない場合

値型のイニシャライザ

http://qiita.com/cotrpepe/items/3052d91468f1b852582a http://qiita.com/qoAop/items/a28e3649feaf38af1134

// 参考: http://qiita.com/_mpon/items/4491d19fa50b2039af35

 18> enum Size { 
 19.     case Small, Medium, Large 
 20.  
 21.     // 引数なしコンストラクタ 
 22.     init() { 
 23.         self = .Small // デフォルト値の設定など
 24.     } 
 25. }
 26> Size()
$R3: Size = Small
// 値型の場合は self.init で自身のイニシャライザを呼び出すことができるのenumのそれがわからない..。
 27> enum Size { 
 28.     case Small, Medium, Large 
 29.  
 30.     // 引数なしコンストラクタ 
 31.     init() { 
 32.         self = .Small 
 33.     } 
 34.     init(type: Int) { 
 35.         if type % 3 == 0 { 
 36.             self = .Medium 
 37.         } else { 
 38.             self = self.init 
 39.         } 
 40.     } 
 41. } 
repl.swift:38:25: error: cannot assign value of type '_ -> _' to type 'Size'
            self = self.init
                   ~~~~~^~~~
// Structの場合は https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html より
  1> struct Size { 
  2.     var width = 0.0, height = 0.0 
  3. } 
  4. struct Point { 
  5.     var x = 0.0, y = 0.0 
  6. }
  7>  
  8> struct Rect { 
  9.     var origin = Point() 
 10.     var size = Size() 
 11.     init() {} 
 12.     init(origin: Point, size: Size) { 
 13.         self.origin = origin 
 14.         self.size = size 
 15.     } 
 16.     init(center: Point, size: Size) { 
 17.         let originX = center.x - (size.width / 2) 
 18.         let originY = center.y - (size.height / 2) 
 19.         self.init(origin: Point(x: originX, y: originY), size: size) 
 20.     } 
 21. }

参照型のイニシャライザ

  • designated イニシャライザはclass中の初期値が与えられていないletやvarのstoredプロパティの値をすべて定義する
  • ほかのイニシャライザには convenience修飾子をつける
    • convenienceイニシャライザはdesignated イニシャライザを呼び出さなければいけない
  • クラスを継承している場合は、必ずsuperclassのイニシャライザを呼び出さなければいけない
    • 基本的には、サブクラスはスーパークラスのイニシャライザを継承しない。
    • スーパークラスの designated イニシャライザと同様のイニシャライザを定義する場合、override 修飾子が必要
    • サブクラスが designated イニシャライザを定義していない場合、スーパークラスの designated イニシャライザが利用できる
    • required 修飾子をイニシャライザに前置することで、サブクラスにそのイニシャライザの実装を強制することができる
 42> class Initial { 
 43.     var name: String 
 44.     init() { 
 45.         name = "Sample" 
 46.     } 
 47.      
 48.    convenience init(initName: String) { 
 49.        self.init() 
 50.        self.name = initName 
 51.     } 
 52.     func display() { 
 53.         print(name.substringToIndex(name.startIndex.advancedBy(1)))
 54.     } 
 55. } 
 56> Initial().display()
S
 57> Initial(initName: "Example").display() 
E

SubクラスがSuperクラスのdesignated イニシャライザを定義していない場合、スーパークラスの designated イニシャライザが利用できます。

 27> class Super { 
 28.     init() { 
 29.         print("Super")
 30.     } 
 31. } 
 32> class Sub : Super {} 
 33> Sub() 
Super
$R1: Sub = {
  __lldb_expr_11.Super = {}
}

required 修飾子で実装を強制することもできます。

 34> class Super { 
 35.     init() { 
 36.         print("Super") 
 37.     } 
 38.     required convenience init(printString: String) { 
 39.         self.init()
 40.         print(printString) 
 41.     } 
 42. } 
 43> class Sub : Super {} 
 44> Sub()
Super
$R2: Sub = {
  __lldb_expr_19.Super = {}
}
 45> Sub(printString: "Test")
Super
Test
$R3: Sub = {
  __lldb_expr_19.Super = {}
}

Failable Initializers

  • init??をつけたイニシャライザは与えられた引数によっては初期化を失敗させてnilを返すようにできる
  • そのイニシャライザの返り値をしては、Optionalとなる

Deinitializers

クラスのインスタンスが破棄されるときに実行したい処理はデイニシャライザ(deinit {...})にて定義できる。

新卒向け社内研修でテーブル設計と正規化の基本の話をしました

speakerdeck.com

新卒向け社内研修でテーブル設計と正規化の基本の話をしました。

いま新卒の方々はRails tutorialを進めながら「理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL」を読んでいるところなのですが、中には入社以前にはWeb系のプログラミング経験のない方もいらっしゃいます。

その状態で上記の本を読み進めるのは少しつかみにくいところもあるだろうな、ということで、この資料に関してはかなり基本に絞り、演習で設計から第1から第3正規化の一連のプロセスについて手を動かしてみて経験値の底上げをしてもらうという構成にしました*1

理論的な背景をもちろん抑える必要はあるのですが、何度か手を動かしてみて頭の中に回路を作ってみる、というのも車の両輪みたいに大切で、一人でも正規化の手続きの流れが追えるようにしたつもりです。

「変わりそうな値を主キーにしてはいけない」「商品の値段が変わった場合、過去の注文の合計金額を変えないためにはどうしたらよいか」「あとから特定の状態になった日を知りたくなるような場合があるときは、どうしたらよいか」

他にもいろいろあるんですけど、そういったもう少し応用的な内容については、少しだけ演習の合間に声をかけさせていただきましたが、興味を持っていただけたら他の実践テクニックに寄せた本なども読んでみると面白いと思います。

自分が読んだ中でオススメは下記です。

〔入門〕はじめてのデータベース

〔入門〕はじめてのデータベース

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQLアンチパターン

SQLアンチパターン

*1:この本を去年の新卒に勧めたのは私なのですが、去年の新卒は非常にすごい人ぞろいでして...