@ WWDC 16
Local Reasoning
local reasoning์ด ๋ญ๋, ํ ํจ์๊ฐ ์๋ค๊ณ ํ ๋ ์ธ๋ถ ์ฝ๋๋ค์ ๋ํด์๋ ์๊ฐํ ํ์๊ฐ ์๋ค๋ ๊ฒ์ ๋งํจ
์ด ์ฑ์ ํ์ฌ ์ํ. ๊ต์ฅํ ๋ณต์กํ๋ค. ์ด๋ฅผ ์ ์ง์ ์ผ๋ก ๊ฐ์ ์์ผ๊ฐ๋ฉด์ protocol๊ณผ value type์ ๋ํด ๊ณต๋ถํ๋ ๊ฒ์ด ์ด๋ฒ ์ธ์ ์ ๋ชฉํ!
์ธํฐ๋ท์์๋ ํํ ๊ฐ๋จํ ๋ชจ๋ธ ํ์ ๋ค์ value ํ์ ์ ์ฌ์ฉํ๋ผ๊ณ ๋งํ์ง๋ง ์ด๋ ํ๋ ธ๋ค! UIKit์์ value ํ์ ์ ์ด๋ป๊ฒ ์ฌ์ฉํ ์ ์์ ์ง ์ดํด๋ณด์!
ํ์ฌ cell์ UITableViewCell ์ด๊ณ ๊ทธ ์์ UIView ๋ค์ด ์์ ์ ์๊ณ , SKNode๊ฐ ์์ ์๋ ์๋ค! ์ด๊ฑธ value ํ์ ์ธ Layout์ ์ด์ฉํด์ ๊ตฌ์กฐ ๋ณ๊ฒฝ์ ํด๋ณด์.
class DecoratingLayoutCell: UITableViewCell {
var content: UIView
var decoration: UIView
// Perform layout...
}
์ด๊ฑธ struct๋ก ๋ฐ๊ฟ๋ณด์.
struct DecoratingLayout {
var content: UIView
var decoration: UIView
mutating func layout(in rect: CGRect) {
// Perform layout...
}
}
class DreamCell: UITableViewCell {
...
override func layoutSubviews() {
var decoratingLayout = DecoratingLayout(content: content, decoration: decoration)
decoratingLayout.layout(in: bounds)
}
}
์ด๋ฐ ์์ผ๋ก ์ชผ๋ ํ์ฅ์ฑ์ด ์๊ฒผ๋ค. ์ด๋ ๊ฒ ๋๋ฉด ๋ค๋ฅธ cell์์๋ ์ฌ์ฉํ ์ ์๊ณ , ๋ค๋ฅธ ๋ทฐ์์๋ ์ฌ์ฉํ ์ ์๊ฒ ์ง?
func testLayout() {
let child1 = UIView()
let child2 = UIView()
var layout = DecoratingLayout(content: child1, decoration: child2)
layout.layout(in: CGRect(x: 0, y: 0, width: 120, height: 40))
XCTAssertEqual(child1.frame, CGRect(x: 0, y: 5, width: 35, height: 30))
XCTAssertEqual(child2.frame, CGRect(x: 35, y: 5, width: 70, height: 40))
}
๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ํ ์คํธ๋ ๊ฐ๋ฅํ ๊ฑฐ์ผ!
Local Reasoning์ผ๋ก ์ฐ๋ฆฌ๋ ์ข ๋ ์ฝ๊ฒ ์ดํดํ ์ ์๊ณ , ์ฝ๊ฒ ํ ์คํธํ ์ ์์ง!
๊ทผ๋ฐ ์์์ ๋ดค๋ DecoratingLayout
์ content
์ decoration
์ UIView
ํ์
์ผ๋ก ๊ฐ๊ณ ์์ด. ๊ทธ๋ผ SKNode
๋ฅผ content
๋ decoration
์ผ๋ก ๊ฐ๊ณ ์ถ๋ค๋ฉด ๋ ํ๋์ Layout์ ์์ฑํด์ผ ๋ผ. NodeDecratingLayout
์ด๋ฐ ์์ผ๋ก.
ํ์ง๋ง ์ด๋ฐ ๋ฐฉ๋ฒ๋ ์์ง.
struct DecoratingLayout {
var content: Layout
var decoration: Layout
mutating func layout(in rect: CGRect) {
content.frame = ...
decoration.frame = ...
}
}
protocol Layout {
var frame: CGRect { get set }
}
extension UIView: Layout {}
extension SKNode: Layout {}
layout์ ์ค์ ํด์ฃผ๊ธฐ ์ํด์ ์ฐ๋ฆฌ๋ frame
์ด ํ์ํด. ๊ทธ๋์ frame
์ ๊ฐ์ง๋๋ก ํ๋ protocol, Layout
์ ๋ง๋ค์๊ณ , ์ด๊ฑธ content
์ decoration
์ ํ์
์ผ๋ก ์ฌ์ฉํ์ด.
๊ทผ๋ฐ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ๊ณผ ์ฝ๊ฐ ๋ฌ๋ผ! ์๋๋ฉด content
์ decoration
์ ๋ ๋ค UIView
์ด๊ฑฐ๋ SKNode
์ด๊ธฐ๋ฅผ ๋ฐ๋ผ๋๋ฐ, ์์ ์ฝ๋์์๋ Layout
ํ๋กํ ์ฝ์ ์ฑํํ๊ณ ์๊ธฐ๋ง ํ๋ฉด ๋๊ฑฐ๋ .
struct DecoratingLayout<Child: Layout> {
var content: Child
var decoration: Child
mutating func layout(in rect: CGRect) {
content.frame = ...
decoration.frame = ...
}
}
protocol Layout {
var frame: CGRect { get set }
}
extension UIView: Layout {}
extension SKNode: Layout {}
๊ทธ๋์ ์ด๋ ๊ฒ Generic์ ์ด์ฉํด์ content
์ decoration
์ด ๊ฐ์ ํ์
์ด๋๋ก ๋ง๋ค์ด์คฌ์ด.
Generic์ ์ด์ฉํ๋ฉด ํ์ ๋ค์ ๋ ์ ์ปจํธ๋กคํ ์ ์๊ณ , ์ปดํ์ผ ํ์์ ์ข ๋ ์ต์ ํ๋ฅผ ์ํฌ ์ ์์ด.
์์ธํ ์ ๋ณด๋ Understanding Swift Performance๋ฅผ ์ฐธ๊ณ ํ๋๋ก~~
Composition์ ์ด์ฉํด์ ์ฐ๋ฆฌ๋ ์ฝ๋๋ฅผ local reasoning์ ํด์น์ง ์๊ณ ์ฌ์ฌ์ฉํ ์ ์์ด.
ํด๋์ค ์ธ์คํด์ค๋ ํ ์์ญ์ ์ ์ฅ๋๊ณ ๋น์ฉ์ด ๋น์ธ. ์ฐ๋ฆฌ๋ ๊ทธ๋์ view์ composition์ด ์๋ value์ composition์ ํ ๊ฑฐ์ผ. struct๋ ์ ๋ ดํ๊ฑฐ๋ ! composition์ value semantics์ ํจ๊ปํ ๋ ๋ ์ข์.
struct CascadingLayout<Child: Layout> {
var children: [Child]
mutating func layout(in rect: CGRect) {
...
}
}
struct DecoratingLayout<Child: Layout> {
var content: Child
var decoration: Child
mutating func layout(in rect: CGRect) {
content.frame = ...
decoration.frame = ...
}
}
protocol Layout {
var frame: CGRect { get set }
}
composition์ ์ด๋ป๊ฒ ํด ์ค์ผ ํ ๊น? ๋จผ์ Layout
ํ๋กํ ์ฝ์ ์ข ๋ฐ๊ฟ๊ฑฐ์ผ.
protocol Layout {
mutating func layout(in rect: CGRect)
}
์ด๋ ๊ฒ!
extension UIView: Layout { ... }
extension SKNode: Layout { ... }
struct DecoratingLayout<Child: Layout, ...>: Layout { ... }
struct CascadingLayout<Child: Layout>: Layout { ... }
๋บ~!!
let decoration = CascadingLayout(children: accessories)
var composedLayout = DecoratingLayout(content: content, decoration: decoration)
composedLayout.layout(in: rect)
์ด๋ ๊ฒ ์ฌ์ฉํ๋ค!
protocol Layout {
mutating func layout(in rect: CGRect)
var contents: [Layout] { get }
}
๊ทผ๋ฐ contents๊ฐ UIView ์ด๊ฑฐ๋ SKNode๊ฑฐ๋ ๋ ์ค ํ๋์์ ์ข๊ฒ ์ด. ๊ทธ๋์ associatedtype
์ ์ด์ฉํ ๊ฑฐ์ผ.
protocol Layout {
mutating func layout(in rect: CGRect)
associatedtype Content
var contents: [Content] { get }
}
struct DecoratingLayout<Child: Layout, Decoration: Layout
where Child.Content == Decoration.Content>: Layout {
var content: Child
var decoration: Decoration
mutating func layout(in rect: CGRect)
typealias Content = Child.Content
var contents: [Content] { get }
}
์ด๋ ๊ฒ ๋ ํ์ฅํ ์๋ ์์ง~~! ๋ ์ด๋ ๊ฒ ๋ง๋ Layout์ ํ ์คํธํ๊ธฐ๋ ์ฌ์
์ฌ๊ธฐ์ ์ ๋ฆฌ ํ ๋ฒ ํ ๊ฒ์ค
- Local reasoning with value types
- Generic types for fast, safe polymorphism
- Composition of values
undo๋ฅผ ํ๋ ค๊ณ ํ ๋
์ด๋ ๊ฒ ํ์ง ๋ง๊ณ
์ด๋ ๊ฒ ํ๋ ค๊ณ ํด๋ด!
class DreamListViewController: UITableViewController {
var dreams: [Dream]
var favoriteCreature: Creature
...
} // ์ด๋ฌ๋ ๊ฑธ ์๋์ฒ๋ผ ๋ชจ๋ธ๋ก ๋ถ๋ฆฌ์์ผ๋ณด์
struct Model: Equatable {
var dreams: [Dream]
var favoriteCreature: Creature
}
์ญ์ญ ๊ณ์ํด๋ณด๊ฒ ์ต๋๋ค
class DreamListViewController: UITableViewController {
...
func modelDidChange(old: Model, new: Model) {
if old.favoriteCreature != new.favoriteCreature {
tableView.reloadSections(...)
}
...
undoManager?.registerUndo(withTarget: self, handler: { target in
target.model = old
})
}
}
์๋ฌ์ ๋ ์ด์ ์
- Single code path
- Better lcal reasoning
- Values compose well with other values
class DreamListViewController: UITableViewController {
var isInViewingMode: Bool
var sharingDreams: [Dream]?
var selectedRows: IndexSet?
...
}
์ด๋ฐ ๊ฒ๋ค์ด UI State๋ค! ์ด๋ฐ State๋ enum์ผ๋ก ๊ด๋ฆฌํด๋ณด์.
enum State {
case viewing
case sharing(dreams: [Dream])
case selecting(selectedRows: IndexSet)
}
class DreamListViewController: UITableViewController {
var state: State
...
}
๊ทธ๋ผ value type์ด๊ธฐ์ ๊ฐ์ง ์ ์๋ ์ด์ ๋ค์ ๊ฐ์ ธ์ฌ ์ ์์ด
- Customization through composition
- Protocols for generics, reusable code
- Taking advantage of value semantics
- Local reasoning <- ์ด๊ฑด ์ด๋ค ์ธ์ด๋ฅผ ์ฌ์ฉํ๋ ์ค์ํ๋๊น ๊ผญ ์ผ๋์ ๋๊ณ ํ๋ก๊ทธ๋๋ฐํ์๊ตฌ