Methods

목차

  1. Instance Method
  2. Type Method

Class, Structure, Enumeration 모두 생성 가능

 

1. Instance Method

Modifying Value Types from Within Instance Methods

  • 기본적으로, Value 타입의 property는 인스턴스 메소드 안에서 수정될 수 없음.  (여기서말하는 value 타입은 struct, enum)
  • mutating 키워드 : 메소드에서 property의 수정이 가능하도록 정의하기 위해 추가
struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"

 

2. Type Method

  • 타입 자체에서 호출되는 메소드
  • func 키워드 앞에 static 키워드 작성
  • Objective-C에서는 Class에서만 type-level method를 정의할 수 있었다. Swift에서는 모든 클래스, 구조체, 열거형에서 정의 가능
class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()
struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1

    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

 

 

 

 

 

Ref.

https://docs.swift.org/swift-book/LanguageGuide/Methods.html

https://kka7.tistory.com/117?category=919617

Properties

  1. Stored Properties
  2. Computed Properties
  3. Property Observers
  4. Global and Local Variables
  5. Type Properties

Properties

1. Stored Properties

  • A stored property is a constant or variable that is stored as part of an instance of a particular class or structure. 
  • Default property values
  • Assiging Constant properties during initalization (초기화 중에 할당되는 프로퍼티)

1-1) 상수 Structure 인스턴스의 stored property

  • If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6 // this will report an error, even though firstValue is a variable property
  • 결론)
    Value type의 인스턴스를 상수로 생성하면, 모든 property는 변경할 수 없다.
    반대로, Reference type의 인스턴스를 constant로 생성하면 변경 가능하다.

1-2) Lazy Stored Properties:  인스턴스 초기화 -> 값 계산 -> 값 세팅

  • lazy stored property: 최초 사용될 때까지 초기 값이 계산되지 않는 프로퍼티
  • var keyword를 사용해야 함 => lazy stored property는 인스턴스 초기화가 완료될 때 까지 초기값을 가져올 수 없으므로 
    (let: 초기화가 완료되기 전에 값을 가져야 함.)

 

2. Computed Properties

  • Class, structure, enumeration은 computed property 정의 가능
  • 실제로 값을 저장하는 것이 아님
  • getter와 optional setter를 제공하여 다른 property의 값을 간접적으로 세팅할 수 있다.
struct CompactRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

 

3. Property Observers

  • property observer는 property의 값의 변화에 관찰하고 반응한다!(observe and respond)
  • property의 값이 세팅되는 매 순간마다 호출됨. (같은 값으로 세팅되더라도)
  • lazy stored property를 제외한 모든 property(stored, computed property)에 추가 가능
  • overriding된 상속받은 property에도 property observer를 추가할 수 있다.
  • observer 종류
    - willSet is called just before the value is stored.
    - didSet is called immediately after the new value is stored.
  • in-out 으로 observer를 가지는 property가 parameter로 전달되어도, willSet과 didSet observer는 항상 호출됨.
    => copy-in copy-out memory model이기 때문에!
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

 

 

4. Global and Local Variables

  • Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties.
    Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.

  • Local constants and variables are never computed lazily.

 

5. Type Properties

  • 클래스 변수
  • static keyword로 정의
struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // cap the new audio level to the threshold level
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // store this as the new overall maximum input level
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"

 

 

 

 

Ref.

https://kka7.tistory.com/116?category=919617

https://docs.swift.org/swift-book/LanguageGuide/Properties.html

'iOS > Swift' 카테고리의 다른 글

[Swift 5.2] Methods - Apple Documentation  (0) 2020.03.05
[Swift] ExpressibleByStringLiteral, CustomStringConvertible  (0) 2020.02.18
[Swift4.1] map, flapMap, compactMap  (0) 2019.12.11
[Swift5.1] Overview  (0) 2019.12.04
[Swift] if let vs. guard let  (0) 2019.11.29

RW-Swift-5.1-Cheatsheet-1.0.pdf
3.21MB

Swift 5.1 관련하여 간단하게 개념만 집고가자!

 

  1. Declaring constants and variables
  2. Numeric type conversion
  3. String
  4. Tuple
  5. Optionals
  6. Collection Types: Array
  7. Collection Types: Dictionary
  8. Collection Types: Set

  9. Control Flow: Loops
  10. Control Flow: Conditionals
  11. Functions
  12. Closures
  13. Enumerations 
  14. Structs
  15. Classes
  16. Inheritance
  17. designated & Convenience Initializers

  18. Extensions
  19. Error Handling
  20. Coding Protocols
  21. Access Control
  22. 스위프트에서의 물음표와 느낌표

 

1. Declaring constants and variables

let double: Double = 2.0 
let inferredDouble = 2.0 

var mutableInt: Int = 1 


2. Numeric type conversion

let integerValue = 8 
let doubleValue = 8.0 

// error: type mismatch 
//let sum = integerValue + doubleValue 
let sum = Double(integerValue) + doubleValue 


3. String - TODO: string 비교

let helloWorld = "Hello, world" 

let helloWorldProgram = """ 
dfdf 
dfdf 
""" 

// emptyString 
let emptyString = ""        // Using string literal 
let anotherEmptyString = String() 

var mutableString = "Swift" 
mutableString += " is awesome!" 

print("This is my opinion: \(mutableString)") 


4. Tuple

- 여러 값을 group화 할 수 있는 자료형 => Tuples are useful for simple groups of related values

- decompose - let (code, reason) = httpError

- underscore (_) : ignoring parts of the tuple

- 인덱스 숫자로 접근하여 값을 가져올 수 있음

- element에 이름 추가 가능

// Group multiple values into a single compound value
// : a number and a human-readable description.
let httpError = (503, "Server Error") 

// decompose 
let (code, reason) = httpError 
let codeByIndex = httpError.0 
let reasonByIndex = httpError.1 

// _ : ignoring parts of the tuple 
let (_, justTheReason) = httpError 

// access using index numbers
print("The status code is \(http404Error.0)")

/-----------------------------------
// element 이름 추가 가능
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")


5. Optionals

- 해당 자료형 값 또는 nil 값을 갖는 변수

var catchphrase: String?                     // optional String

let count3: Int?  = catchphrase?.count   // optional Int (catchphrase가 nil일 수도 있으니까요)

- ! (forced unwrapping opration) : nil이 아니면 unwrapped optional, nil이면 crash

- optional binding: if let, guard let

- ?? : coalescing operator

- ? : chaing operator

- implicitly unwarpped optionals : 강제적으로 optional unwrap

// optional: String or nil 값을 갖는 변수 
var catchphrase: String?    // called optional String 
catchphrase = "Hey, everybody?" 

// Forced uwrapping operation(!) 
// catchphrase이 nil이 아닐 경우 count 값을 가짐, nil이면 crash  
let count1: Int = catchphrase!.count 

// Optional binding 
// catchphrase?count : optional Int 
// optional Int에 값이 포함되어 있으면 count라는 constant 생성  
if let count = catchphrase?.count { 
    print(count) 
} 

// Coalescing operator (??) - coalesce: 합치다 
//catchphrase가 nil이 아니면 count2는 cathphrase의 count을 넣음; nil이면 0 
let count2: Int = catchphrase?.count ?? 0 

// Chaining operator (?) 
// Optional Int: catchphrase의 count or nil 
let count3: Int? = catchphrase?.count 

// Implicitly unwrapped optionals (implicit: 절대적인) 
let forcedCatchphrase: String! = "Hey, everybody?" 
let implicitCatchphrase = forcedCatchphrase // 값을 넣을 때 ! mark 필요 없음 


6. Collection Types: Array

- contains

- append, insert

- remove

- arr[0]

// let collection : reassign과 element 바꾸는 것 안됨 
let immutableArray: [String] = ["Alice", "Bob"] 

// 타입 추론: mutableArray [String] 
var muttableArray = ["Eve", "Frank"] 
let isEveThere = immutableArray.contains("Eve") 
let name: String = immutableArray[0] 

// index가 out of range이면 crash 
mutableArray[1] = "Bart" 

// immutableArray[1] = "Bart" // Error: 바꿀 수 없음 
mutableArray.append("Ellen") 
mutableArray.insert("Gemma", at: 1) 
let removedPerson = mutableArray.remove(at: 1) 

mutableArray = ["Ilary", "David"] 
mutableArray[0] = "John" 


7. Collection Types: Dictionary

- key가 존재하지 않으면 nil return : optional 값 리턴

- dic["name], removeValue(forKey: )

let immutableDict: [String: String] = ["name", "Kirk", "rank": "captain"] 
var mutableDict = ["name": "Picard", "rank": "captain"] 

// key가 존재하지 않으면 nil return (String? - optional String) 
let name2: String? = immutableDic["name"] 

// update value for key 
mutableDic["name"] = "Janeway" 

// add new key and value 
mutableDic["ship"] = "Voyager" 

// delete by key, 해당 key가 nil이 아니면 
let rankWasRemoved: String? = mutableDic.removeValue(forKey: "rank") 


8. Collection Types: Set

- list와 비슷하지만 중복된 item은 ignore

- contains, insert, remove

// Set: 중복된 item은 ignore 
let immutableSet: Set = ["chocolate", "vanilla", "chocolate"] 
var mutableSet: Set = ["butterscotch", "strawberry"] 

immutableSet.contains("chocolate") 
mutableSet.insert("green tea") 

// 해당 item을 찾지 못하면 nil return  
let flavorWasRemoved: String? = mutableSet.remove("strawBerry") 

9. Control Flow: Loops

// Iterator list or set 
for item in listOrSet { 
    print(item) 
} 
// Iterator over dictionary 
for (key, value) in dictionary { 
    print("\(key) = \(value)") 
} 

for i in 0...10 { 
    print(i)    // 0 to 10 
} 

for i in 0..<10 { 
    print(i)    // 0 to 9 
} 

var x = 0 
while x < 10 { 
    x += 1 
    print(x) 
} 

repeat { 
    x -= 1 
    print(x) 
} while(x > 0) 


10. Control Flow: Conditionals

let number = 88 

if (number <= 10) { 

} else if (number > 10 && number < 100) { 

} else { 

} 

// Ternary operator (ternary: 3으로 이루어진) 
let height = 100 
let isTall = height > 200 ? true : false 

// guard: program의 제어를 변형하기 위해 
for n in 1...30 { 
    guard n % 2 == 0 else { 
        continue 
    } 
    print("\(n) is even") 
} 

let year = 2012 
switch year { 
case 2003, 2004: 
    print("Panther or Tiger") 
case 2010: 
    print("Lion") 
case 2012...2015: 
    print("Mountain Lion through El Captain") 
default: 
    fatalError("Not already classified") 
} 


11. Functions

_: argument label 제외

// void function 
func sayHello() { 
    print("Hello") 
} 
// function with parameters 
func sayHello(name: String) { 
    print("Hello \(name)!") 
} 
// function with default parameters 
func sayHello(name: String = "Lorenzo") { 
    print("Hello \(name)!") 
} 

func sayHello(name: STring = "Lozenzo", age: Int) { 
    print("\(name) is \(age) years old!") 
} 

// using just the non default value 
sayHello(age: 35) 

// function with parameters and return value 
func add(x: Int, y: Int) -> Int { 
    return x + y 
} 
let value = add(x: 8, y:10) 

// single expression만 포함할 경우 return 키워드 제거 가능 
func multiply(x: Int, y: Int) -> Int { 
    x + y 
} 

// arguments label 명시 
func add(x xVal: Int, y yVal: Int) -> Int { 
    return xVal + yVal 
} 

// TODO: argement label 제외  
func add(_ x: Int, y: Int) -> Int { 
    return x + y 
} 
let value = add(8, y: 10) 

// parameter로 다른 함수 접근 
func doMath(operation: (Int, Int) -> Int, a: Int, b: Int) -> Int { 
    return operation(a, b) 
} 


12. Closures:  TODO: 실행하기

// TODO: 
let adder: (Int, Int) -> Int = {(x, y) in x + y} 
// argument의 약어 사용  
let square: (Int) -> Int = {$0 * $0} 
// 함수에 closure 전달  
let addWithClosure = doMath(operation: adder, a: 2, b: 3) 

13. Enumerations 

- allCases : CaseIterable protocol을 채택하면 사용 가능

- rawValue

- associated value(연관값): enum case문에 추가 정보를 덧붙일 수 있음 

  case text(String)

- switch문으로 associated value를 캐치할 수 있다.

  case .text(let value):

enum Taste { 
    case sweet, sour, salty, bitter, umami 
} 
let vinegarTaste = Taste.sour 

// Iterating through an enum class 
enum Food: CaseIterable { 
    case pasta, pizza, hamburger 
} 
for food in Food.allCases { 
    print(food) 
} 

// enum with String raw values 
enum Currency: String { 
    case euro = "EUR" 
    case dollar = "USD" 
    case pound = "GBP" 
} 
let euroSymbol = Currency.euro.rawValue 
print("The currency symbol for Euro is \(euroSymbol)") 

// enum with associated values 
// associated value(연관값):  
// 열거형의 케이스(case)에 추가적인 정보를 덧 붙일 수 있는 독특한 방법 
enum Content { 
    case empty 
    case text(String) 
    case number(Int) 
} 
// matching enumeration values with a switch statement 
let content = Content.text("Hello") 
switch content { 
case .empty: 
    print("Value is empty") 
case .text(let value):  // String value 추출 
    print("Value is \(value)") 
case .number(_):        // Int value ignore 
    print("Value is a number") 
} 


14. Structs

- member initializer 자동 생성됨

struct User { 
    var name: String 
    var age: Int = 40 
} 
// member initilizer는 자동적으로 생성됨. 
let john = User(name: "John", age: 35) 
// member initilizer는 기본 파라미터 값을 사용.  
// TODO: User()이렇게 해도 돼? 
let dave = User(name: "Dave") 

print("\(john.name) is \(john.age) years old") 


15. Classes

- 반드시 하나의 designated initializer를 가져야 함

- computed property

- deinit: object의 리소스가 해제될 때 수행

class Person { 
    let name: String 
    // class initializer(designated initializer) 
    init(name: String) { 
        self.name = name 
    } 
    // using deinit to perform object's resources cleanup 
    deinit { 
        print("Perfom the deinitialization") 
    } 

    var numberOfLaughts: Int = 0 
    func laugh() { 
        numberOfLaughts += 1 
    } 
    // define a computed property 
    var isHappy: Bool { 
        return numberOfLaughs > 0 
    } 
} 

let david = Person(name: "David") 
david.laugh() 
let happy = david.isHappy 


16. Inheritance

- override computed property

- final: subclass를 방지하기 위해 사용

class Student: Person { 
    var numberOfExams: Int = 0 

    // override isHappy computed property providing additional logic 
    override var isHappy: Bool { 
        numberOfLaughs > 0 && numberOfExams > 2 
    } 
} 

let ray = Student(name: "Ray") 
ray.numberOfExams = 4 
ray.laugh() 
let happy = ray.isHappy 
// final: subclass를 방지하기 위해 사용 
final class Child: Person { } 


17. designated & Convenience Initializers

- designated initializer: class는 반드시 하나 가져야 함

- convenience initializer: 옵션

// class는 반드시 최소한의 하나의 designated initilizer를 가져야 함 
// convenience initilizer는 옵션 
// TODO:  designated initilizer 정의 
class ModeOfTransportation { 
    let name: String 
    // designated initilizer 정의 
    init(name: String) { 
        self.name = name 
    } 
    // convenience initilizer 정의 
    convenience init() { 
        // 내부 designated initilizer에게 위임 
        self.init(name: "Not classified") 
    } 
} 

class Vehicle: ModeOfTransportation { 
    let wheels: Int 
     
    init(name: String, wheels: Int) { 
        self.wheels = wheels 
        // superclass의 designated initilizer로 위임 
        super.init(name: name) 
    } 
    // override superclass convenience initilizer 
    override convenience init(name: String) { 
       // 내부 designated initilizer에게 위임 
        self.init(name: name, wheels: 4) 
    } 
} 

18. Extensions

- 존재하는 class, structure, enumeration, protocol 타입에 새로운 기능 추가

- TODO: Category와 extension의 차이점은?

 extension String { 
    // String type 확장 - String instance가 truthy or falsy인지 판단 
    var boolValue: Bool { 
        if self == "1" { 
            return true 
        }  
        return false 
    } 
} 

let isTrue = "0".boolValue 


19. Error Handling

- Error protocol 채택하여 enum으로 정의

- do catch 문으로 에러 캐치

- try? : 해당 값에 error가 던져지면 nil로 반환 (optional 반환)

  try! : 해당 값에 error가 던져지면 런타임 에러(앱이 죽는다는 얘기... 절대로 에러가 발생하지 않을 경우만 사용)

// error 상속받아 enum으로 기술 
enum BeverageMatchineError: Error { 
    case invalidSelection 
    case insufficientFunds 
    case outOfStock 
} 

func selectBeverage(_ selection: Int) throws -> String { 
    return "Waiting for beverage" 
} 

// do clause의 code에 의해서 error가 던져지면 처리  
let message: String 
do { 
    message = try selectBeverage(20) 
} catch BeverageMatchineError.invalidSelection { 
    print("Invalid selection") 
} catch BeverageMatchineError.insufficientFunds { 
    print("Insufficient funds") 
} catch BeverageMatchineError.outOfStock { 
    print("Out of stock") 
} catch { 
    print("Generic error") 
} 

// try? error가 던져지면, 그 expression의 값은 nil로 처리 
let nillableMessage = try? selectBeverage(10) 

// try! : 런타임에 error를 받으면 error를 받음. 
let throwableMessage = try! selectBeverage(10) 


20. Coding Protocols

- Codable: Decodable + Encodable protocol

- JSONDecoder: JSON String -> Data

- JSONEncoder: Data -> JSON String

import Foundatation 

// Codable: Decodable, Encodable protocol 
struct UserInfo: Codable { 
    let username: String 
    let loginCount: Int 
} 

// CustomStringConvertible 채택 
extension UserInfo: CustomStringConvertible { 
    var description: String { 
        return "\(username) has tried to login \(loginCount) time(s)" 
    } 
} 

// string literal 
let json = """ 
    {"username" : "David", "loginCount" : 2} 
""" 

// JSONDecoder를 사용하여 JSON serialize : JSON string -> Data 
let decoder = JSONDecoder() 

// String을 data(struct)로 표현  
let data = json.data(using: .utf8)! 
let userInfo = try! decoder.decode(UserInfo.self, from: data) 
print(userInfo) 

// JSONEncoder 사용하여 struct serialize :  
let encoder = JSONEncoder() 
let userInfoData = try! encoder.encode(userInfo) 
// Data -> JSON String으로 표현 
let jsonString = String(data: userInfoData, encoding: .utf8)! 
print(jsonString) 


21. Access Control

- public

- fileprivate

- private

- access관련이 없을 경우

// module(framework or application): 코드가 배포의 single unit 
// public: 다른 모듈에서 접근 가능 
public class AccessLevelsShowcase { 
    public var somePublicProperty = 0 
    // ? 
    var someInternalProperty = 0 

    // 소스 파일을 정의한 것으로부터 접근 가능  
    fileprivate func someFilePrivateMethod() { } 
    //  
    private func somePrivateMethod() { } 
}


22. 스위프트에서의 물음표와 느낌표

스위프트에서 이 두 가지 구두 문자 (? 와 !)는 사용된 모든 용법에서 공통된 의미를 가지고 있음

물음표 : 무언가 확실하지 않다는 뜻

느낌표 : 경고 표지판 같은 용도, 잠재적으로 위험 동작을 수행한다.

 

옵셔널의 언랩핑과 마찬가지로 몇 라인 코드를 더 작성해야한다 할지라도 try!대신에 try try?를 사용할 것을 권장합니다. 물론 try!와 옵셔널 강제 언랩핑이 필요한 경우가 분명 존재합니다.

 

 

 

Ref. 

Ray Wenderlich website

https://outofbedlam.github.io/swift/2016/03/22/try/

패턴이 해결하는 문제 이해

  • 튜플(Tuple) 은 그룹화된 값들의 세트이고, 편리하고, 쉽게 사용할 수 있다. 하지만, 사용하는데 한계가 있다.

var products = [
   ("Kayak", 275.0, 10), 
   ("Lifejacket", 48.95, 14), 
   ("Soccer Ball", 19.5, 32)
];
func calculateTax(product:(String, Double, Int)) -> Double {
   return product.1 * 0.2;
}
func calculateStockValue(tuples:[(String, Double, Int)]) -> Double {
  return tuples.reduce(0, combine: {(total, product) -> Double in
      return total + (product.1 * Double(product.2))
  });
}
print("Sales tax for Kayak: $\(calculateTax(products[0]))");
print("Total value of stock: $\(calculateStockValue(products))");

  • 그림 4-1은 함수들과 튜플 배열 사이의 강력한 결합(Tight Couplings)을 보여준다.

  • 강력한 결합도(Tight Couplings)’ 문제에 대한 이해
    • 강력하게 결합된 컴포넌트가 많은 애플리케이션에서 하나의 변경 사항이 연쇄적인 코드 변경을 필요로 한다.
    • 느슨하게 결합된 컴포넌트가 디자인 패턴을 사용하는 주요한 목표이다.(Loose Couplings)




오브젝트 템플릿 패턴의 이해

  • 오브젝트 템플릿 패턴은 클래스구조체를 이용하여 객체를 생성할 템플릿을 정의한다.
  • 애플리케이션 컴포넌트가 객체가 필요한 경우 스위프트 런타임에 요청한다.

  • 스위프트 런타임의 객체 생성 3단계
    => 이 3단계는 여러 개의 객체를 생성할 때마다 반복적으로 수행된다.

        1. 스위프트 런타임은 해당 템플릿 이름과 초기값들로 객체를 생성한다.
        2. 스위프트 런타임은 객체를 저장할 메모리를 할당하고 템플릿을 이용하여 객체를 생성한다.
          (템플릿에 구현된 객체 초기화 메소드로 초기값을 설정하거나 객체 생성을 요청한 포넌트가 제시한 값을 이용해서 초기화가 이루어진다. 스위프트 런타임은 객체 사용 준비를 위한 초기화를 실행한다.)
        3. 마지막으로 스위프트 런타임은 객체가 생성되었음을 컴포넌트에 알려준다.



      오브젝트 템플릿 패턴 구현

      • 튜플 기반 접근 방법과 흡사하도록 Product 클래스를 생성하기
      class Product {
         var name:String;
         var description:String;
         var price:Double;
         var stock:Int;
         init(name:String, description:String, price:Double, stock:Int) {
             self.name = name;
             self.description = description;
             self.price = price;
             self.stock = stock;
         }
      }
      
      • Product class 사용하기
      var products = [
         Product(name: "Kayak", description: "A boat for one person",
                   price: 275, stock: 10),
         Product(name: "Lifejacket", description: "Protective and fashionable",
                   price: 48.95, stock: 14),
         Product(name: "Soccer Ball", description: "FIFA-approved size and weight",
                   price: 19.5, stock: 32)];
      
      func calculateTax(product:Product) -> Double {
         return product.price * 0.2;
      }
      
      func calculateStockValue(productsArray:[Product]) -> Double {
         return productsArray.reduce(0, {(total, product) -> Double in
             return total + (product.price * Double(product.stock))
         });
      }
      • 스위프트 접근 제어(Access Control)의 이해
        • private: 같은 파일 내부에서만 보이고 구성요소 내에서만 사용할 수 있도록 제한
        • internal
          - 애플리케이션 레벨에서 전체 구성요소의 접근이 가능
          - 같은 프로젝트 내에서 import 구문이 필요없음
        • public
          - 외부에 제공되는 프레임워크 등 API 레벨의 접근 권한
          - Objective-C 쪽으로 코드베이스를 공유할 때 사용 


      오브젝트 템플릿 패턴의 장점

      • 튜플은 훌륭한 기능이지만, 클래스나 구조체를 선호하는 이유는 조정 가능하고 느슨한 결합도를 제공하기 때문.
      • 결합도 감소(decoupling)의 이득
        • 컴포넌트 간의 강력한 결합도를 깬다.
      • 캡슐화(encapsulation)의 이득
        • 데이터와 로직을 하나의 컴포넌트에 넣을 수 있다.
        • 하나의 컴포넌트에 기술되므로 코드 가독성이 올라간다.
      • Public 공개의 이득
        • 객체 사용자에게 제공할 공개 API 가능하게 한다.
        • private으로 구현체를 숨길 수 있다.




      오브젝트 템플릿 패턴의 위험

      • 정확한 종류(클래스 or 구조체)를 선택해야한다.
        • 스위프트에서의 클래스와 구조체의 가장 중요한 차이
          : 클래스는 Reference 객체, 구조체는 Value 객체.
        • 이 차이는 프로토타입 패턴에서 자세히 설명된다.(업데이트 예정)


      정리

      • 본 장에서는 스위프트 개발에 있어 “심장”과 같은 패턴을 설명했다.
      • “심장”은 객체 생성에 사용하는 템플릿의 정의이다.



      Ref. Pro Design Patterns in Swift by Adam Freeman.


      + Recent posts