#Advanced swift ##Taking control of Syntax
텍스트 머드 기반으로 설명을 하네~
class Thing {
init(location: Thing?, name: String,
longDescription: String) { ... }
weak var location: Thing?
var name: String
var longDescription: String
}
###argument name
객체 생성하려면 인자 이름(argument name)을 넘겨야 한다.
let wallWestOfHouse = Thing(location: westOfHouse,
name: "wall",
longDescription: "The plaster has crumbled " +
"away, leaving the wood beneath to rot.")
다음 둘은 동일하다.
class Thing {
init(location: Thing?, name: String,
longDescription: String) { ... }
}
class Thing {
init(location location: Thing?, name name: String,
longDescription longDescription: String) { ... }
}
뭔가, 이름은 넣어야 하지만 쓸 일은 없다면 그냥 _ 로 퉁 칠 수 있다. (스칼라랑 비슷하네)
for (key, _) in dictionary { //key와 value 로 구성된 tuple 자리이지만, value에 관심없음
println(key)
}
var red, blue: Int
(red, _, blue, _) = color.rgba //rgba 4 값 중 red, blue만 관심있음
인자 이름도 지워버리고 싶다면 _ 를 사용하자.
class Thing {
init(_ location: Thing?, _ name: String,
_ longDescription: String) { ... }
}
let wallWestOfHouse = Thing(westOfHouse, "wall",
"The plaster has crumbled away, leaving the " +
"wood beneath to rot.")
###protocol
당길 수 있는 객체라면 뭔가 특별한 처리를 하고 싶다!
// The parser will call this.
func performPull(object: Thing) {
if /* object is pullable */ {
/* pull it */
} else {
/* complain */
}
}
그렇담 protocol 만들어.
protocol Pullable {
func pull()
}
적용은 마치 class extends 하듯이. 자바의 interface처럼 protocol에 정의된 function 은 구현해야 해.
class Boards: Thing, Pullable {
func pull() {
if location === westWallOfHouse {
println("They come off with little effort.")
location = westOfHouse
} else {
println("Think of the splinters!")
}
}
}
protocol 타입 확인은 if let xxx = yyy as protocol_type {...}
형식으로.
func performPull(object: Thing) {
if let pullableObject = object as Pullable {
pullableObject.pull()
} else {
println("You aren't sure how to print a \(object.name).")
}
}
####special protocols
그냥 literal이 아니라 special protocol이라 칭하는 이유가 있나?
type | ex. |
---|---|
LogicValue | if logicValue { |
Printable | "(printable)" |
Sequence | for x in sequence |
IntegerLiteralConvertible | 65536 |
FloatLiteralConvertible | 1.0 |
StringLiteralConvertible | "abc" |
ArrayLiteralConvertible | [ a, b, c ] |
DictionaryLiteralConvertible | [ a: x, b: y ] |
####printable
string interpolation에 쓰는 녀석. 즉, interpolation 시 출력되는 내용을 지정하고 싶다면 이 protocol을 extends(맞나?)하자. java의 toString() method override 같음.
protocol Printable {
var description: String { get }
}
extension Thing: Printable {
var description: String { return name }
}
출력 내용을 꾸며보자.
extension Thing {
var nameWithArticle: String {
return "a " + name
}
}
println("You aren't sure how to pull \(an ~ object).")
연산자 오버로딩
//연산자 오버로딩 하려면 operator 선언을 해야 한다고 함. 아직 이 부분을 문법 책에서 못봐서리...
operator infix ~ {}
func ~ (decorator: (Thing) - String,
object: Thing) -> String {
return decorator(object)
}
func an(object: Thing) -> String {
return object.nameWithArticle
}
####enum
enum Direction {
case North, South, East, West
}
class Place: Thing {
init(_ name: String, _ longDescription: String) { ... }
var exits: Dictionary<Direction, Place>
}
//enum 타입 추정 가능한 위치엔 . 만 찍어서
westOfHouse.exits[.South] = gardenPath
westOfHouse[.South] = gardenPath // [] 도 빼버리고 싶다면?
//subscript 새용
extension Place {
subscript(direction: Direction) -> Place? {
get {
return exits[direction]
}
set(destination: Place?) {
exits[direction] = destination
}
}
}
####a word of caution
- Keep it natural
- Work by analogy
- New idioms should pay for themselves
##Generic programming func peekString(interestingValue: String) { println("[peek] (interestingValue)") }
func peekInt(interestingValue: Int) {
println("[peek] \(interestingValue)")
}
func peekFloat(interestingValue: Float) {
println("[peek] \(interestingValue)")
}
==>
func peek(interestingValue: Any) {
println("[peek] \(interestingValue)")
}
//반환값 가지고 뭘 더 하고싶다면? generic
func peek<T>(interestingValue: T) -> T {
println("[peek] \(interestingValue)")
return interestingValue
}
###protocol types vs. generics
- Protocol types like Any or Pullable throw away type information
- Great for Dynamic Polymorphism
- Swift Generics conserve type information
- Great for type safety
- Great for performance
type relationships
func swap<T>(inout x: T, inout y: T) {
let tmp = x
x=y
y = tmp
}
###protocol constraints
// ": 타입"
func indexOf<T : Equatable>(sought: T, inArray array: T[]) -> Int? {
for i in 0..array.count {
if array[i] == sought {
return i
} }
return nil }
protocol Equatable {
func == (lhs: Self, rhs: Self) -> Bool
}
###implementing memoization using generics 순수 재귀함수는 너무 오래걸려
func fibonacci(n: Int) -> Double {
return n < 2 ? Double(n) : fibonacci(n - 1) + fibonacci(n - 2)
}
수동 memoization -> 안이뻐
var fibonacciMemo = Dictionary<Int, Double>() // implementation detail
// Return the nth fibonacci number: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
func fibonacci(n: Int) -> Double {
if let result = fibonacciMemo[n] {
return result
}
let result = n < 2 ? Double(n) : fibonacci(n - 1) + fibonacci(n - 2)
fibonacciMemo[n] = result
return result
}
generic, closure를 이용해 구현한 자동 memoization
func memoize<T: Hashable, U>( body: (T)->U ) -> (T)->U {
var memo = Dictionary<T, U>()
return { x in
if let q = memo[x] { return q }
let r = body(x)
memo[x] = r
return r
}
}
초기값이 있다면?
func memoize<T: Hashable, U>( body: ((T)->U, T)->U ) -> (T)->U {
var memo = Dictionary<T, U>()
var result: ((T)->U)!
result = { x in
if let q = memo[x] { return q }
let r = body(result, x)
memo[x] = r
return r
}
return result
}
let factorial = memoize { factorial, x in x == 0 ? 1 : x * factorial(x - 1) }
####synergy of powerful features
- Type deduction for concision:
- let fibonacci = memoize { fibonacci, n in
- Trailing closure syntax for expressivity
- let fibonacci = memoize { fibonacci, n in
- Generic functions for generality with safety and performance
- func memoize<T: Hashable, U>( body: ((T)->U, T)->U ) -> (T)->U
###generic types struct Stack { mutating func push(x: T) { items += x }
mutating func pop() -> T {
return items.removeLast() }
var items: T[]
}
var intStack = Stack<Int>()
intStack.push(42)
근데... 다음과 같은 처리를 해 주고 싶다면?
func peekStack(s: Stack<T>) {
for x in s { println(x) }
}
for...in loop 는 내부적으로 다음과 같이 처리됨
for x in someSequence {
...
}
-->
var __q = someSequence.generate()
while let x = __g.next() {
...
}
이러려면 Generator protocol을 extends해야.
protocol Generator {
typealias Element
mutating func next() -> Element?
}
protocol Sequence {
typealias GeneratorType : Generator
func generate() -> GeneratorType
}
struct StackGenerator<T> :Generator { 
mutating func next() -> T? {
if items.isEmpty { return nil }
let ret = items[0]
items = items[1..items.count]
return ret
}
var items: Slice<T>
}
extension Stack : Sequence {
func generate() -> StackGenerator<T> {
return StackGenerator( items[0..item.Count] )
}
}
//이제 동작
func peekStack(s: Stack<T>) {
for x in s { println(x) }
}
####Summary of Swift Generics and Protocols
- Protocols are your hooks into the Swift core language
- Swift generics combine abstraction, safety, and performance in new ways
- Read, experiment, and have fun. There’s plenty to discover!
##The Swift model
- The Minimal Model
- Statically compiled //동적 언어 아니야!
- Small runtime
- Simple Interoperation
- Transparent interaction with C and Objective C
- Can deploy to previous versions of iOS and Mac OS X
- Predictable Behavior
- You control the code that runs
- Comprehensible compilation with inspectable results
- No non-deterministic JITs or garbage collection pauses
- Efficient Execution
- Native code instantly ready to run
- No artificial abstraction barriers //generic 같은 정보가 사라지지 않음을 얘기하는 듯?
- Predictable model enables bare-to-the-metal programming //low level code를 생성한다?
- High-Level Optimization
- Removing abstraction penalties : struct를 사용해도 overhead 0
- Global analysis of your app
- Using a struct has no runtime penalty
- Even Int and Float are structs
- Generic specialization : 다른 언어같이 extension 하지 않음 <- 컴파일 과정에서 generic 정보를 풀어헤치지 않음
- Swift can run generic code directly
- Optimizer can produce specialized versions of generic code at will
- Separate compilation of generics
- Faster compiles
- Flexibility to trade code size for speed
- Devirtualization
- Resolving dynamic method calls at compile-time
- If Swift can see where you constructed the object
- If Swift knows that a class doesn't have any subclasses
- If you've marked a method with the @final attribute
- Resolving dynamic method calls at compile-time
- Removing abstraction penalties : struct를 사용해도 overhead 0