Successfully reported this slideshow.
Your SlideShare is downloading. ×

objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 83 Ad

More Related Content

Slideshows for you (20)

Viewers also liked (20)

Advertisement

Similar to objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト (20)

More from Taketo Sano (14)

Advertisement

Recently uploaded (20)

objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト

  1. 1. objc2swift ∼ Objective-C から Swift への「コード & パラダイム」シフト ∼ @taketo1024 2015/11/11 iOS 9 Bootcamp @ dots. by Classmethod, inc.
  2. 2. About Me • ヤフー(株) 所属 iOS アプリ開発者/開発リーダ • iOS歴 4年 (Xcode 3.x 後半ぐらいから) • 31歳 1児(♀) の父
  3. 3. 「プログラマのための数学勉強会」主催 • 11/21 (土) 第5回開催します! • 現在参加者募集中!(11/15 (日) まで)
  4. 4. 今日のテーマ Objective-C から Swift への 「コード」と「パラダイム」のシフトについて
  5. 5. 背景 • 1年半ほど ObjC で開発していたプロジェクトがあった。当時 iOS 版の開発はほぼ僕一人。 • 4月からリニューアルプロジェクト始動、開発メンバーも4名に 増員。新メンバーに iOS アプリ開発経験者はいなかった。 → せっかくのリニューアルだし、メンバーの学習も兼ねて Swift で作り直そう! → 旧プロジェクトのソフト資産はできるだけ活用したい。 (Bridging-Header は使いたくない)
  6. 6. ObjC → Swift 移行プロセス • まずは単純に ObjC → Swift に書き換える。 • そのあと Swift らしい書き方を探りながら改善してく。
  7. 7. 単純な書き換え • 普通の ObjC のコードはほぼ機械的に Swift に置き換えられる。 • こんなものは人間がやるべき作業ではない。正規表現でいけるんじゃ? ObjC Swift @interface MyClass class MyClass @property NSString *text; var text: String @protocol MyProtocol protocol MyProtocol @interface MyClass(Hoge) extension MyClass
  8. 8. 今年の2月、社内チャットにて
  9. 9. ANTLR...?
  10. 10. ANTLR v4
  11. 11. ANTLR文法ファイル Parser 2秒でわかる ANTLR
  12. 12. コンバータを作ろう! • ANTLR 用 ObjC の文法は公開されている。 • Swift への変換を力技で実装していく。 • せっかくなので Java ではなく Scala で! (他の関数型言語も触っておきたかった)
  13. 13. 今の所できること
  14. 14. class A : NSObject { } Swift merge .h & .m @interface MyClass : NSObject @end @implementation MyClass @end Obj-C
  15. 15. convert method-call self.somethingWithArg1("hello", arg2:0) Swift [self somethingWithArg1:@"hello" arg2:0]; Obj-C
  16. 16. control-flow for var i = 0; i < 10; i++ { } Swift for (int i = 0; i < 10; i++) { } Obj-C
  17. 17. & many more! • primitive-types • class-method • enum • protocol • property • block -> closure • stringWithFormat • error reporting
  18. 18. Web UI
  19. 19. 利用者の声 満足度 普通 20% 満足 40% とても満足 40% とても満足 満足 普通 やや不満 不満 作業効率 上がった 20% とても上がった 80% とても上がった 上がった 変わらない むしろ下がった 勘弁して頂きたかった N=5N=5
  20. 20. • Swiftへの書き換え方が分からない場合だけでなく、対象のObjective-Cコー ドが初見の場合などでもSwiftに変換することでグッと読みやすくなり、コー ドの理解が早まるのが素敵だなと思いました。 • 大量のソースを扱う場合やチーム開発では特に、書き換え方が統一される ため非常に開発効率が上がると感じました。 • Swift と Objective-C を比べると差異が多くSwift の学習に苦労したので すが、objc2swift があることで学習コストが下がりました! • リアルタイムで変換してくれる機能があってその場でもコードを書けるのが 良かった。Scala で動いてると思うとそれだけで楽しくなれた。これがな かったら手作業で同じことをしていたのかと思うとぞっとする。 利用者の声
  21. 21. DEMO
  22. 22. OpenSourced! https://github.com/yahoojapan/objc2swift WebUI も近日公開します!
  23. 23. Goodbye ObjC !!! • No more too many @s! • No more YES / NO ! • No more [brackets forMethodCall] !
  24. 24. これからは Swift で モダンプログラミングや! (やっとモテる!) 👍
  25. 25. いざやってみると… • So many ? s... • So many if let s... • unexpectedly found nil while blabla... 💧 let result = try? NSJSONSerialization.JSONObjectWithData(…) let name = ((result?["person"] as? [String: AnyObject])? ["name"] as? String) if let name = name { print("My name is: (name)") } 思てたんと違う…
  26. 26. Swift とは… • Swift は ObjC の「癖」を無くしただけの言語じゃない。 (Objective-C without the C ...? wtf) • Swift に導入された新しいパラダイムをちゃんと理解しな いと上手く書けない。 • さらに ObjC / Foundation への理解もないと、「なぜこ うなっているのか」が分からないところがある。 • Swift には Swift の「癖」がある!
  27. 27. 0. Swift 誕生の背景 1. Optional 型との付き合い方 2. 関数が First-Class であるということ 3. Protocol Oriented とは何なのか これから話すこと
  28. 28. 0. Swift 誕生の背景
  29. 29. Objective-C の歴史 • 1983年に開発され、1985年に Apple社を去った Steve Jobs が NeXT Computer 社を創立、その主力言語として採用される。 • 1997年、Apple社 が NeXT社 を買収し、2001年の Mac OS X の Cocoaフレームワーク のコア言語として採用される。 • Foundation クラスの NS は OS名 NeXTSTEP 由来!
  30. 30. • ObjC は C言語に Smalltalk 風のオブジェクト指向システムを追加したもの。 • クラスもオブジェクトもただの構造体へのポインタ。 • メソッドコールも C の関数呼び出しに変換される。つまり実態は C 。 • ObjC が @ だらけなのは C に対するプリプロセスだったから。 typedef struct objc_class *Class; struct objc_class {     struct objc_class *isa;     struct objc_class *super_class;     const char *name;     …⋯ }; typedef struct objc_object {     class isa; } *id; 2005年の連載:
 「ダイナミックObjective-C - クラスとは何か」
  31. 31. Obj-C の弱点 • ランタイム時処理によるオーバーヘッド • コンパイル時の最適化ができない • 弱い型付けによる安全性の犠牲 • 言語仕様の時代遅れ感の強まり
  32. 32. Objective-C の進化と Swift の誕生 GCC Objective-C 1983 GCC Objective-C 2.0 2007 LLVM Modern
 Objective-C 2012 Swift 1.0 2014 参考:Swiftから透けて⾒見見えるAppleのコンパイラ技術 LLVM Clang swiftc コンパイラも Apple が独自に開発! LLVM 作った人が 
 Swift も作った!
  33. 33. 新言語 Swift への要請 (勝手な想像ですが) • Objective-C との相互互換性がある。 • 安全かつコンパイル時最適化が可能。 • 言語はできるだけ簡易に保ったまま関数型の 特性も取り入れたい。
  34. 34. The Swift language … also greatly benefited from the experiences hard-won by many other languages in the field, drawing ideas from Objective-C, Rust, Haskell, Ruby, Python, C#, CLU, and far too many others to list. http://nondot.org/sabre/ Chris Lattner
  35. 35. ObjC にはなかった新たなパラダイム • Strongly Typed (w/ Optional Type) • Functional • Protocol-Oriented
  36. 36. 1. Optional 型との付き合い方
  37. 37. Apple のドキュメントより if let johnsStreet = john.residence?.address?.street { print("John's street name is (johnsStreet).") } else { print("Unable to retrieve the address.") } Swift also introduces optional types, which handle the absence of a value. Optionals say either “there is a value, and it equals x” or “there isn’t a value at all”. Using optionals is similar to using nil with pointers in Objective-C, but they work for any type, not just classes.
  38. 38. なるほど、 nil を入れたきゃ 型に ? をつけときゃいいのね
  39. 39. なるほど、 nil を入れたきゃ 型に ? をつけときゃいいのね ❌この理解のまま実際にコードを書いてしまうと 永久に型不一致のエラーに悩まされることになる。
  40. 40. public enum Optional<Wrapped> : NilLiteralConvertible { case None case Some(Wrapped) } • Optional は任意の型を持てるジェネリックな enumで、 Int? は Optional<Int> と等価。 • 任意の値は Optional 型への代入によって Optional 型に暗黙 キャストされる。 • nil はただのリテラルで、実態は Optional 型の値 None 。 → イメージが掴めるまで難しい。 Swift Standard Library
  41. 41. Optional 型は 「値が入ってるかもしれないし空かもしれない箱」に例えられる Functors, Applicatives, And Monads In Pictures
  42. 42. もっとわかりやすく、 Optional は長さが 0 または 1 の Array と思おう!
  43. 43. let maybeInt : Optional<Int> = .Some(1) let actualInt = maybeInt! let maybeInt : Optional<Int> = 1 let actualInt = maybeInt!
  44. 44. let maybeInt : Optional<Int> = .Some(1) let actualInt = maybeInt! let actualInt = maybeInt! let maybeInt : Array<Int> = [1] let actualInt = maybeInt[0] こうだと思う
  45. 45. let maybeInt : Optional<Int> = nil let actualInt = maybeInt!
 // fatal error: unexpectedly found nil while unwrapping an Optional value let maybeInt : Array<Int> = [] let actualInt = maybeInt[0] // fatal error: Array index out of range nil は空配列みたいなモンなんだから、 中身を取り出そうとしたら怒られるに決まってる。
  46. 46. let maybeInt : Optional<Int> = .Some(1) if let actualInt = maybeInt { print("int: (actualInt)") } let maybeInt : Array<Int> = [1] for actualInt in maybeInt { print("int: (actualInt)") } if let は 1要素に対する for in と同じ! (空なら for は回らないよネ)
  47. 47. Swift は Optional に対して限定的に暗黙キャストを認めている。 (他は変数に代入されたものは明示キャストする必要がある) 無駄なコードを書かずに暗黙的に Optional 型を使えるのは便利だが、 Objective-C の感覚で nil を捉えているとずっと辛い思いをすることになる。
  48. 48. Optional は要素を最大一個だけ入れられる箱!
  49. 49. とても不思議な guard let let maybeInt: Optional = 1 guard let actualInt = maybeInt else { fatalError() } print("value: (actualInt)")
  50. 50. とても不思議な guard let let value: Optional = 1 guard let value = value else { fatalError() } print("value: (value)") unwrap した値を同じ変数に代入できる…
  51. 51. とても不思議な guard let let value: Optional = 1 guard let value = value else { fatalError() } print("value: (value)") 同じスコープ内で変数の型が変わってる…!!! Optional<Int> 型 Int 型
  52. 52. guard let はその前後で世界が変わる。
  53. 53. 2. 関数が First-Class であるということ
  54. 54. 関数を変数に代入したり、引数にとったり、 戻り値として返せるということ。
  55. 55. let a = [2, 3, 1, 4] a.sort(<) // [1, 2, 3, 4] a.sort(>) // [4, 3, 2, 1]
  56. 56. let a = [2, 3, 1, 4] a.sort(<) // [1, 2, 3, 4] a.sort(>) // [4, 3, 2, 1] これができるのは: • 演算子も関数であり • sort は (T, T) -> Bool 型の関数を引数にとるから
  57. 57. let a = [2, 3, 1, 4] let lt: (Int, Int) -> Bool = (<) lt(1, 2) // true. (1 < 2) と等価 a.sort(lt) // [1, 2, 3, 4] こう書くと関数が変数に代入されてる様子が分かる。
  58. 58. ObjC では関数、メソッド、ブロック、 演算子は全て別のものであったが、 Swift では関数に統一されている。
  59. 59. let a = [2, 3, 1, 4] // [2, 3, 1, 4] let b = a.map {$0 * 2} // [4, 6, 2, 8] let c = b.filter {$0 < 5} // [4, 2] map や filter を使いこなそう!
  60. 60. override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var result = [UICollectionViewLayoutAttributes]() for i in 0 ..< items.count { let indexPath = NSIndexPath(forItem: i, inSection: 0) let attr = layoutAttributesForItemAtIndexPath(indexPath) if attr.alpha == 0 { continue } if !CGRectIntersectsRect(attr.frame, rect) { continue } result.append(attr) } return result } map や filter を使うとループ処理が簡潔に書ける before
  61. 61. override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let indexPaths = (0 ..< items.count).map { NSIndexPath(forItem: $0, inSection: 0) } let attrs = indexPaths.map( layoutAttributesForItemAtIndexPath ) let attrsInScreen = attrs.filter { $0.alpha > 0 } .filter{ CGRectIntersectsRect($0.frame, rect)} return attrsInScreen } map や filter を使うとループ処理が簡潔に書ける after
  62. 62. override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { return (0 ..< items.count) .map { NSIndexPath(forItem: $0, inSection: 0) } .map ( layoutAttributesForItemAtIndexPath ) .filter { $0.alpha != 0 } .filter { CGRectIntersectsRect($0.frame, rect)} } 慣れてきたらこう
  63. 63. let a : Optional = 1 // .Some(1) let b = a.map { $0 * 2 } // .Some(2) もちろん Optional に対しても map は使える .Some(1) は [1] みたいなモンなんだから当たり前
  64. 64. let a : Optional = "1" // .Some("1") let b = a.map { Int($0) } // .Some( .Some(1) ) let s : Optional = "hehe" // .Some("hehe") let t = s.map { Int($0) } // .Some( .None ) map する関数が Optional を返す場合は 2重に包まれてしまう… String -> Int は失敗する場合があるので Optional
  65. 65. let a : Optional = "1" // .Some("1") let b = a.flatMap { Int($0) } // .Some(1) let s : Optional = "hehe" // .Some("hehe") let t = s.flatMap { Int($0) } // .nil そういうときは flatMap !
  66. 66. let a = ["1", "2", "heh", "4", "lol"] let b = a.flatMap{ Int($0) } // [1, 2, 4] 配列に Optional な戻り値を返す関数を flatMap して filter みたいなこともできる
  67. 67. • ObjC 出身者が普通に Swift コードを書いてても関数型な書 き方はあまり鍛えられない。 • チームに Scala や Haskell などの経験者がいると頼もしい! いない場合はこれらの言語で鍛錬しましょう(僕も鍛錬中) • もっと設計レベルで関数型を取り入れたい場合は Promise(通 信)や Reactive(UI)も検討してみましょう。
  68. 68. 3. Protocol-Oriented とは何なのか
  69. 69. WWDC2015 - Protocol-Oriented Programming in Swift
  70. 70. Swift is the first protocol oriented language. (ドヤァ)
  71. 71. ん…? ただのデフォルト実装付きの インターフェースじゃないの?
  72. 72. 僕なりにその「偉大さ」を考えてみた。
  73. 73. Swift の protocol とは… • Java の interface のように型として扱えて、 • Scala の trait のように mix-in ができ、 • Haskell の type class のような AdHoc ポリ モーフィズムが実現できる素晴らしい仕組み!
  74. 74. Haskell の「型クラス」 class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) data Point = Pt Double Double instance Eq Point where (Pt x y) == (Pt x' y') = x == x' && y == y' Eq クラス Point 型を Eq クラスのインスタンスに http://walk.wgag.net/haskell/typeclass.html より
  75. 75. そっくり! public protocol Equatable { public func ==(lhs: Self, rhs: Self) -> Bool } struct Point: Equatable { let x, y: Double } func ==(p1: Point, p2: Point) -> Bool { return p1.x == p2.x && p1.y == p2.y } Equatable プロトコル Point 型を Equatable に適合
  76. 76. Scala / Haskell 大好き Uくん に「Protocol は 型クラスみたいなモンと思う」と言って、 「なるほど、わかった!」と言って作ってくれた例。 protocol AddMonoid { static var zero: Self { get } static func plus(lhs: Self, _ rhs: Self) -> Self } extension Int: AddMonoid { static var zero: Int { return 0 } static func plus(lhs: Int, _ rhs: Int) -> Int { return lhs + rhs } } extension String: AddMonoid { static var zero: String { return "" } static func plus(lhs: String, _ rhs: String) -> String { return lhs + rhs } } extension SequenceType where Generator.Element: AddMonoid { func sum() -> Generator.Element { return reduce(Generator.Element.zero) { Generator.Element.plus($0, $1) } } } [1, 2, 3].sum() // 6 ["abc", "def", "ghi"].sum() // "abcdefghi"
  77. 77. class-oriented vs protocol-oriented (僕のイメージ) struct 継承によってデータと 機能が増えていく 具体型が必要最小のデータを持ち、 Protocol によって機能を足していく protocolA protocolB
  78. 78. 純粋に protocol-oriented なプログラミングは可能か? • 現段階では厳しい。 • Swift 標準ライブラリは protocol-oriented に作られている が、 Foundation / UIKit はもちろんなっていない。 • 型パラメータの変位がサポートされていないので複合型を作ろ うとすると辛い。特に Self Requirement がやばい(次ページ)。
  79. 79. protocol MyProtocol: Equatable {} struct A: MyProtocol {} struct B: MyProtocol {} let a: [MyProtocol] = [A(), B()] 
 // protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements
  80. 80. 僕なりの結論: 今は extension を便利に使うぐらいにしといて、そんな にどっぷり protocol-oriented に浸かることもない。 Swift や Foundation / UIKit の進化を待とう。
  81. 81. まとめ(口頭)
  82. 82. Thanks! Twitter: taketo1024 Blog: http://taketo1024.hateblo.jp/

×