Swift 더 알아보기

jonghwan·2022년 10월 4일
0

멋쟁이사자처럼

목록 보기
20/28
post-thumbnail

타입

Swift의 타입은 세 가지 기본 그룹으로 구분되는데, 구조체, 열거형, 클래스이다.

Swift 구조체나 열거형은 대다수 언어보다 더 현저하게 강력하다.

프로퍼티, 이니셜라이저, 메서드 지원뿐만 아니라 확장하거나 프로토콜을 따를 수 있다.

// 구조체
struct MyStruct {
  // 프로퍼티
  // 이니셜라이저
  // 메서드
}


// 열거형
enum MyEnum {
  // 프로퍼티
  // 이니셜라이저
  // 메서드
}


// 클래스
class MyStruct: SuperClass {
  // 프로퍼티
  // 이니셜라이저
  // 메서드
}

구조체 열거형 둘 다 값 타입, 클래스는 참조 타입 !

각 진수에 따라 정수를 표현하는 방법

10진수 : 우리가 평소에 쓰던 숫자와 동일하게 작성

2진수 : 접두어 0b를 사용

8진수 : 접두고 0o를 사용

16진수 : 접두어 0x를 사용

let decimalIntger: Int = 28
let binaryInteger: Int = 0b11100     // 2진수로 10진수 28을 표현
let octalInteger: Int = 0o34         // 8진수로 10진수 28을 표현
let hexadecimalInteger: Int = 0x1c   // 16진수로 10진수 28을 표현

정수와 부동소수점 변환

정수와 부동 소수점 숫자 타입의 변환은 명시적으로 변환해야 한다.

이 코드에서 상수 three 는 타입 Double 의 새로운 값으로 생성하는데 사용되어 덧셈의 양쪽이 동일한 타입이다.

이 변환이 없으면 덧셈이 허용되지 않는다.

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double (three) + pointOneFourOneFiveNine

// pi equals 3.14159, and is inferred to be of type Double

부동 소수점을 정수로 변환하는 것 또한 명시적으로 변환해야 한다.

부동 소수점 값은 새로운 정수 값으로 초기화할 때 소수좀 아래를 버림한다.

이것은 4.75 는 4, 그리고 -3.9 는 -3 이 된다는 의미이다.

let integerPi = Int(pi)

// integerPi equals 3, and is inferrde to be of tpye Int

문자열

연산자를 통한 문자열 결합

+ 연산자를 이용해 결합

let hello: String = "Hello"
let ned: String = "ned"
var greeting: String = hello + " " + ned + "!"
print(greeting) // Hello ned!

greeting = hello
greeting += " "
greeting += ned
greeting += "!"
print(greeting) // Hello ned!

연산자를 통한 문자열 비교

== 연산자를 이용해 비교

isSameString = hello == "Hello"
print(isSameString) // true

isSameString = hello == "hello"
print(isSameString) // false

isSameString = ned == "ned"
print(isSameString) // true

isSameString = ned == hello
print(isSameString) // false

메서드를 통한 접두어 확인

hasPrefix 메소드를 이용해 확인

// 메서드를 통한 접두어 확인
var hasPrefix: Bool = false
hasPrefix = hello.hasPrefix("He")
print(hasPrefix)  // true

hasPrefix = hello.hasPrefix("HE")
print(hasPrefix)  // false

hasPrefix = greeting.hasPrefix("Hello")
print(hasPrefix)  // true

hasPrefix = ned.hasPrefix("ed")
print(hasPrefix)  // false

hasPrefix = hello.hasPrefix("Hello")
print(hasPrefix)  // true

메서드를 통한 접미어 확인

hasSuffix 메소드를 이용해 확인

// 메서드를 통한 접미어 확인
var hasSuffix: Bool = false
hasSuffix = hello.hasSuffix("He")
print(hasSuffix)  // false

hasSuffix = hello.hasSuffix("llo")
print(hasSuffix)  // true

hasSuffix = greeting.hasSuffix("ned")
print(hasSuffix)  // false

hasSuffix = greeting.hasSuffix("ned!")
print(hasSuffix)  // true

hasSuffix = ned.hasSuffix("ed")
print(hasSuffix)  // true

빈 문자열 확인

isEmpty 프로퍼티를 이용해 확인

// 프로퍼티를 통한 빈 문자열 확인
var isEmptyString: Bool = false
isEmptyString = greeting.isEmpty
print(isEmptyString)  // false

greeting = "안녕"
isEmptyString = greeting.isEmpty
print(isEmptyString)  // false

greeting = ""
isEmptyString = greeting.isEmpty
print(isEmptyString)  // true

문자열 길이 확인

count 프로퍼티를 이용해 확인

// 프로퍼티를 이용해 문자열 길이 확인
print(greeting.count) // 0

greeting = "안녕하세요"
print(greeting.count) // 5

greeting = "안녕!"
print(greeting.count) // 3

집합

Set

집합 (set) 은 콜렉션에 순서와 상관없이 같은 타입의 다른 값을 저장한다.

아이템의 순서가 중요하지 않거나 아이템이 반복되면 안될 때 배열 대신에 집합을 사용할 수 있다.

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// print "letters is of type Set<character> with 0 items."

letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is still of type Set<Character>

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres has been initialized with three initialitems

집합 접근과 수정

읽기 전용 count 프로퍼티로 집합의 아이템 갯수를 알 수 있습니다.

count 프로퍼티가 0 과 같은지를 부울 isEmpty 프로퍼티를 사용해 확인할 수 있다.

집합의 insert(_:) 메서드를 호출하여 집합에 새로운 아이템을 추가할 수 있다.

print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."

if favoriteGenres.isEmpty {
  print("As far as music goes, I'm not picky.")
} else {
  print("I have particular music preferences.")
}
// Prints "I have particular music preferences."

favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items

집합의 remove(_:) 메서드를 호출하여 집합의 아이템을 삭제할 수 있다.

remove(_:) 메서드는 집합에 아이템이 있을 경우 삭제하고 삭제된 값을 반환하거나 해당 아이템이 없을 경우 nil 을 반환한다.

또한 removeAll() 메서드를 사용하여 전체 아이템을 삭제할 수 있다.

contains(_:) 메서드를 사용하여 집합에 특정 아이템이 포함되어 있는지 알 수 있다.

if let removedGenre = favoriteGenres.remove("Rock") {
  print("\(removedGenre)? I'm over it.")
} else {
  print("I never much cared for that.")
}
// Prints "Rock? I'm over it."

if favoriteGenres.contains("Funk") {
  print("I get up on the good foot.")
} else {
  print("It's too funky in here.")
}
// Prints "It's too funky in here."

집합 반복

for-in 루프와 함께 집합에 값을 반복할 수 있다.

Swift의 Set 타입은 정의된 순서를 가지고 있지 않다.

특정 순서로 집합의 값을 반복하려면 집합의 요소를 < 연산자를 사용하여 정렬하여 반환하는 sorted() 메서드를 사용해야 한다.

for genre in favoriteGenres {
  print("\(genre)")
}
// Classical
// Jazz
// Hip hop


for genre in favoriteGenres.sorted() {
  print("\(genre)")
}
// Classical
// Hip hop
// Jazz

집합 연산 수행

두 집합을 합치거나 두 집합의 공통 값을 구하거나 두 집합이 모두 같은 값을 갖고 있거나 한쪽에만 존재하거나 아예 없거니와 같은 기본적인 집합 연산을 효율적으로 수행할 수 있다.

그림은 세 집합 a, b, c 를 나타내며 집합의 공통 요소는 겹쳐서 표현하고 있다.

a 는 b 의 모든 요소를 포함하므로 집합 a 는 b 의 초집합(superset)이라 한다.

반대로 b 의 모든 요소가 a 에 포함되어 있으므로 집합 b 는 집합 a 의 부분집합(subset)이라 한다.

집합 b 와 집합 c 는 공통 요소가 없으므로 분리집합(disjoint)이라 한다.

두 집합이 같은 값을 모두 포함하고 있는지 판단하려면 "같음" 연산자( == )를 사용한다.

isSubset(of:) 메서드를 사용하면 집합이 특정 집합에 모든 값이 포함되어 있는지 판단한다.

isSuperset(of:) 메서드를 사용하면 집합에 특정 집합에 모든 값을 포함 하는지 판단한다.

isStrictSubset(of:) 또는 isStrictSuperset(of:) 메서드를 사용하면 집합이 특정 집합과 같지 않고 부분집합인지 초집합인지 판단한다.

isDisjoint(with:) 메서드를 사용하면 두 집합이 공통값이 없는지 판단한다.

let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnilmals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

열거형

열거형은 값들의 집합으로 이루어진 타입이다.

파이를 묘사하는 enum을 정의해보자.

enum PieType {
    case Apple
    case Cherry
    case Pecan
}

let favoritePie = PieType.Apple

Swift는 enum 값들을 매칭하기에 좋은 강력한 switch 문을 가지고 있다.

switch문은 모든 case를 포함해야 한다.

switch 표현식의 경우 각각의 값은 명시적이든지 default: case든지 반드시 처리되어야 한다.

Swift C언어와 달리 switch 문의 case들이 다른 case로 빠지지 않는다. 일치되는 case의 코드만 실행된다.

// 파이를 묘사하는 열거형
enum PieType {
    case Apple
    case Cherry
    case Pecan
}

let favoritePie = PieType.Apple

let name: String
switch favoritePie {
case .Apple:
    name = "Apple"
case .Cherry:
    name = "Cherry"
case .Pecan:
    name = "Pecan"
}

Swift의 enumcase에 연관된 원시 값을 가질 수 있다.

타입이 명시된 enum을 가지고 rawValue를 사용하여 PieType의 인스턴스를 요청할 수 있다.

그리고 그 값을 enum 타입을 초기화할 수 있다.

이것은 enum의 실제 case에 상응하는 원시 값이 없을 수 있기 때문에 옵셔널을 반환한다.

따라서 이 경우가 옵셔널 바인딩의 좋은 예다.

// 파이를 묘사하는 열거형에 원시값 적용
enum PieType: Int {
    case Apple = 0
    case Cherry
    case Pecan
}

let pieRawValue = PieType.Pecan.rawValue
// pieRawValue는 2를 값으로 가진 Int 타입이다.

if let pieType = PieType(rawValue: pieRawValue) {
  // 'pieType'!이 유요한 값을 가지면
}

프로퍼티

프로퍼티 관찰자

프로퍼티 관찰자(Property observers)는 프로퍼티의 값이 변경되는지 관찰하고 응답한다.

프로퍼티 관찰자는 프로퍼티의 현재 값이 새로운 값과 같더라도 프로퍼티의 값이 설정될 때 호출된다.

아래의 위치에 프로퍼티 관찰자를 추가할 수 있다.

  • 상속된 프로퍼티의 경우 하위 클래스의 프로퍼티를 재정의하여 프로퍼티 관찰자를 추가한다.

  • 정의한 계산된 프로퍼티의 경우 관찰자를 생성하는 대신에 프로퍼티의 setter를 이용하여 값 변경을 관찰하고 응답한다.

정의한 프로퍼티, 상속한 저장된 프로퍼티, 상속한 계산된 프로퍼티

프로퍼티에 관찰자를 정의하는 방법은 2가지 선택사항을 가지며 둘다 정의할 수도 있다.

willSet

값이 저장되기 직전에 호출

didSet

새로운 값이 저장되자마자 호출

willSet 관찰자를 구현한다면 상수 파라미터로 새로운 프로퍼티 값이 전달된다.

  • willSet 구현의 일부로 이 파라미터에 특정 이름을 가질 수 있다.

  • 파라미터 명과 구현 내에 소괄호를 작성하지 않으면 파라미터는 newValue 의 기본 파라미터 명으로 만들어 질 수 있다.

유사하게 didSet 관찰자를 구현한다면 예전 프로퍼티 값을 포함한 상수 파라미터가 전달된다.

  • 파라미터 명을 사용하거나 oldValue 인 기본 파라미터 명을 사용할 수 있다.

  • didSet 관찰자 내의 프로퍼티에 값을 할당한다면 새로운 값으로 방금 설정한 값을 대체한다.

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

제너릭

제너릭 코드(Generic code)는 정의한 요구사항에 따라 모든 타입에서 동작할 수 있는 유연하고 재사용 가능한 함수와 타입을 작성할 수 있다.

중복을 피하고 명확하고 추상적인 방식으로 의도를 표현하는 코드를 작성할 수 있다.

제너릭은 Swift의 강력한 특징 중 하나이고 Swift 표준 라이브러리 대부분은 제너릭 코드로 되어 있다.

사실 모르고 있더라도 Language Guide 전체에서 제너릭을 사용한다.

  • 예를 들어 Swift의 Array 와 Dictionary 타입은 둘다 제너릭 콜렉션이다.

  • Int 값을 가진 배열, 또는 String 값을 가진 배열 또는 실제로 Swift에서 생성될 수 있는 다른 모든 타입에 대한 배열을 생성할 수 있다.

  • 마찬가지로 모든 지정된 타입의 값을 저장하기 위한 딕셔너리를 생성할 수 있고 해당 타입에 대한 제한은 없다.

제너릭이 해결하는 문제

다음의 두 Int 값을 바꾸는 swapTwoInts()라는 제너릭이 아닌 함수를 보자.

이 함수는 In-Out 파라미터 (In-Out Parameters) 에서 설명한 대로 a 와 b 의 값을 바꾸기 위해 in-out 파라미터를 사용하여 만든다.

swapTwoInts() 함수는 b 의 값을 a 로 그리고 a 의 값을 b 로 바꾼다.

2개의 Int 변수의 값을 바꾸기 위해 이 함수를 호출할 수 있다.

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

swapTwoInt() 함수는 유용하지만 Int 값만 사용이 가능하다.

2개의 String 값 또는 2개의 Double 값을 바꾸길 원하면 swapTwoString() 와 swapTowDoubles() 함수와 같이 더 많은 함수를 작성해야 한다.

swapTwoInts(), swapTwoStrings(), 그리고 swapTwoDoubles() 함수의 바디가 동일하다는 것을 알 수 있다.

차이점은 받아들이는 값의 타입만 (Int, String, 그리고 Double) 다르다.

모든 타입의 2개의 값을 바꾸는 단일 함수로 작성하면 더 유용하고 더 유연합니다.

제너릭 코드는 이러한 함수를 작성할 수 있다.

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

func swapTwoStrings(_ a: inout String, _ b: inout String) {
  let temporaryA = a
  a = b
  b = temporaryA
}

func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
  let temporaryA = a
  a = b
  b = temporaryA
}

제너릭 함수

제너릭 함수(Generic functions)는 모든 타입과 함께 동작할 수 있다.

다음은 swapTwoValues() 라는 위의 swapTwoInts() 함수의 제너릭 버전이다.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
  let temporaryA = a
  a = b
  b = temporaryA
}

swapTwoValues() 함수의 바디는 swapTwoInts() 함수의 바디와 동일하다.

그러나 swapTwoValues() 의 첫번째 줄은 swapTwoInts() 와 약간 다르다.

func swapTwoInts<T>(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
  let temporaryA = a
  a = b
  b = temporaryA
}

함수의 제너릭 버전은 Int, String, 또는 Double 와 같은 실제 타입 이름 대신에 이 경우 T 라는 임의의 타입 이름을 사용한다.

이 임의의 타입 이름은 T 가 무엇이어야 하는지 아무 말도 하지 않지만 T 가 무엇을 나타내든 a 와 b 는 모두 같은 타입 T 여야 한다고 말한다.

T 의 실제 타입은 swapTwoValues() 함수가 호출될 때마다 결정된다.

제너릭 함수와 제너릭이 아닌 함수 사이의 다른 차이점은 제너릭 함수의 이름 (swapTwoValues())에 바로 임의의 타입 이름 ( T )이 꺾쇠 괄호 내(< T >)에 위치한다는 것이다.

이 괄호는 T 는 swapTwoValues() 함수 정의 내에서 임의의 타입 이름이라고 Swift에게 말한다.

T 는 임의의 타입이므로 Swift는 T 라는 실제 타입을 찾지 않는다.

swapTwoValues() 함수는 이제 swapTwoInts 와 동일한 방식으로 호출될 수 있지만 두 값이 서로 동일한 타입이면 모든 타입의 두 값을 전달할 수 있다는 점이 다르다.

swapTwoValues() 가 호출될 때마다 T 로 사용한 타입은 함수에 전달된 값의 타입으로 부터 유추된다.

예제에서 T 는 각각 Int 와 String 으로 추론된다.

func swapTwoInts<T>(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"

타입 파라미터

swapTwoValues() 예제에서 임의의 타입 T 는 타입 파라미터 (type parameter) 의 예시다.

타입 파라미터는 임의의 타입을 지정하고 이름을 지정하며 꺾쇠 괄호 (예: < T >) 사이에 기록하고 함수의 이름 바로 뒤에 작성된다.

타입 파라미터를 지정하면 함수의 파라미터 (swapTwoValues() 함수의 a 와 b 와 같이) 의 타입을 정의하기 위해 사용하거나 함수의 반환 타입이나 함수의 바디내에 타입 주석으로 사용할 수 있다.

각각의 경우 타입 파라미터는 함수가 호출될 때마다 실제 타입으로 대체된다.

swapTwoValues() 에서 T 는 첫번째 함수가 호출될 때 Int 로 대체되고 두번째 호출될 때 String 으로 대체된다.

콤마로 구분된 꺾쇠 괄호 안에 여러개 타입 파라미터를 작성하여 하나 이상의 타입 파라미터를 제공할 수 있다.

func swapTwoInts<T>(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"

타입 파라미터 이름

대부분의 경우 타입 파라미터는 타입 파라미터와 제너릭 타입 간의 관계나 함수간의 관계를 나타내기 위해 Dictionary< Key, Value > 에서 Key 와 Value 그리고 Array< Element > 에서 Element 와 같이 설명이 포함된 이름이 있다.

그러나 의미있는 관계가 없을 때는 swapTwoValues() 함수에서 T 와 같이 T, U 그리고 V 와 같은 단일 문자를 사용하여 이름을 지정하는 것이 일반적이다.

프로토콜 지향 프로그래밍

Protocol-Oriented Programming in Swift

프로토콜 지향 예제

Talkable 프로토콜은 Person이라는 구조체 타입에만 채택이 되었으므로 여러 프로퍼티와 메서드를 구현하더라도 Person에만 구현하면되므로 큰 문제가 없다.

protocol Talkable {
  var topic: String { get set }
  func talk(to: Self)
}

struct Person: Talkable {
  var topic: String
  var name: String

  func talk(to: Person) {
    print("\(topic)에 대해 \(to.name)에게 이야기 합니다")
  }
}

그런데, Talkable이라는 프로토콜을 Person 뿐만 아니라 다른 타입에서도 채택하고 싶다면 그 타입에서도 Talkable 프로토콜이 요구하는 사항을 모두 구현해 주어야 한다.

protocol Talkable {
  var topic: String { get set }
  func talk(to: Self)
}

struct Person: Talkable {
  var topic: String
  var name: String

  func talk(to: Person) {
    print("\(topic)에 대해 \(to.name)에게 이야기 합니다")
  }
}

struct Monkey: Talkable {
  var topic: String

  func talk(to: Monkey) {
    print("우끼끼 꺄꺄 \(topic)")
  }
}

프로토콜이 요구하는 사항을 미리 모두 한꺼번에 구현해 둘 수 있다면 중복된 코드를 피할 수 있다.

이 코드에서는 Person과 Monkeydp Talkable의 요구사항인 talk(to:) 메서드를 구현하지 않았음에도 전혀 오류가 발생하지 않는다.

이렇게 하나의 프로토콜을 만들어주고, 초기 구현을 해둔다면 여러 타입에서 해당 기능을 사용하고 싶을 때 프로토콜을 채택하기만 하면 된다.

protocol Talkable {
  var topic: String { get set }
  func talk(to: Self)
}

extension Talkable {
  func talk(to: Self) {
    print("\(to)! \(topic)")
  }
}

struct Person: Talkable {
  var topic: String
  var name: String

  func talk(to: Person) {
    print("\(topic)에 대해 \(to.name)에게 이야기 합니다")
  }
}

struct Monkey: Talkable {
  var topic: String

  func talk(to: Monkey) {
    print("우끼끼 꺄꺄 \(topic)")
  }
}

만약에 프로토콜 초기 구현과 다른 동작을 해야한다면, 그저 그 타입에 프로토콜의 요구사항을 재정의해주면 된다.

protocol Talkable {
  var topic: String { get set }
  func talk(to: Self)
}

extension Talkable {
  func talk(to: Self) {
    print("\(to)! \(topic)")
  }
}

struct Person: Talkable {
  var topic: String
  var name: String

  func talk(to: Person) {
    print("\(topic)에 대해 \(to.name)에게 이야기 합니다")
  }
}

struct Monkey: Talkable {
  var topic: String

  func talk(to: Monkey) {
    print("\(to)! 우끼끼끼끼")
  }
}

let sunny = Monkey(topic: "바나나")
let jack = Monkey(topic: "나무")

sunny.talk(to: jack)

프로토콜 초기 구현을 잘 해둔다면 여러 프로토콜을 그저 채택만 하기만하면 그 타입에 기능이 추가된다.

protocol Flyable { func fly() }
extension Flyable {
  func flt() {
    print("푸드득 푸드득")
  }
}

protocol Runable { func run() }
extension Runable {
  func run() {
    print("후다닥 후다닥")
  }
}

protocol Swimable { func swim() }
extension Swimable {
  func swim() {
    print("어푸 어푸")
  }
}

protocol Talkable { func talk() }
extension Talkable {
  func talk() {
    print("재잘재잘 쪼잘쪼잘")
  }
}

struct Bird: Flyable, Talkable { }

let bird = Bird()
bird.fly()
bird.talk()

struct Person: Runable, Swimable, Talkable { }

let person = Person()
person.run()
person.talk()
person.swim()

객체지향 vs 프로토콜 지향

주의할 점

Swift는 Duck 타입 언어가 아닌 강한(Strong) 타입 언어다.

객체지향, 프로토콜 지향 어느 것도 만능은 아니다.

프로토콜 지향의 값 타입 기반은 유사성이 있지만 함수언어의 개념은 별개다.

클로저에서 함수언어의 특성이 나타난다.

우리가 활용할 객체가 Class 기반인지, Struct 기반인지 반드시 참고하고 사용할 필요가 있다.

0개의 댓글