티스토리 뷰

iOS-Development

Swift : 타입

Gobans 2022. 3. 10. 21:15

 

프로토콜

protocol Name {property method...}

 

 메소드, 프로퍼티, 어떠한 일이나 기능을하는 것들의 청사진입니다. 클래스, 구조체, 열거형 등에 채택되어 구현에 대한 요구사항을 제공합니다. 프로토콜의 요구사항을 만족하는 타입을 이 프로토콜에 *conform 한다고 말합니다.

(*따르다, 순응하다)

 

 프로토콜은 특정한 이름과 타입으로 인스턴스, 타입 프로퍼티를 요구합니다. 저장 프로퍼티인지 계산 프로퍼티인지는 필요하지 않습니다. 프로토콜은 프로퍼티들이 gettable 또는 gettable and settable 인지 명시해줘야 합니다.

(*gettable: 읽기, settable: 쓰기)

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}

 

이렇게 정의된 프로토콜은 다음 예제와 같이 사용됩니다.

 

protocol FullyNamed {
    var fullName: String { get }
}

struct Person: FullyNamed {
    var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName is "John Appleseed"

 

Fully Named 프로토콜을 만들고 구조체 Person을 정의할 때 ' : ' 콜론 옆에 프로토콜의 이름을 적습니다. 

 

또한 프로토콜은 또 다른 프로토콜을 따를 수 있습니다.

 

protocol Messagable {
  var message: String? { get }
}

protocol Sendable: Messagable {
  // ...
}

 


 

Extension과 Protocol

extension과 protocol을 함께 사용하면 프로토콜의 요구사항을 미리 구현해둘 수 있습니다. 이를 프로토콜 초기구현이라고 합니다.

(*extension은 계산 프로퍼티와 계산 타입프로퍼티 새로운 이니셜라이져 등을 존재하는 클래스, 구조체, 열거형, 프로토콜에 새롭게 추가합니다)

 

extension Some Type { // new functionality to add to SomeType goes here }

 

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

// 익스텐션을 사용한 프로토콜 초기 구현
extension Talkable {
    func talk(to: Self) {
        print("\(to)! \(topic)")
    }
}

 

이렇게 사용한다면 새로운 인스턴스가 Talkable 프로토콜을 conform 한다면 인스턴스내에서 func talk를 구현하지 않아도 사용할 수 있습니다. 

 


클래스 (class) 와 구조체 (struct)

class Name {property method...}
 struct Name {property method...}

 

 클래스와 구조체는 *인스턴스의 값을 저장하거나 기능을 제공하고 이를 캡슐화 할 수 있는 타입입니다.

 

(*An instance of a class is traditionally known as an object. However, Swift structures and classes are much closer in functionality than in other languages)

 


 

 클래스와 구조체가 공통적으로 할 수 있는 것

 

  • 값을 저장할 프로퍼티를 선언할 수 있습니다.
  • 함수적 기능을 하는 메서드를 선언 할 수 있습니다.
  • 서브스크립트를 선언하여 서브스크립트 문법으로 내부 값들에 접근할 수 있습니다.
  • 생성자를 사용해 초기 상태를 설정할 수 있습니다.
  • extension을 사용하여 기능을 확장할 수 있습니다.
  • Protocol을 채택하여 기능을 설정할 수 있습니다.

 


 

 클래스만 가능한 것

 

  • 상속이 가능합니다.
  • 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있습니다.
  • deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있습니다.
  • *참조 카운트를 통해 다른 인스턴스에 하나 이상의 참조를 가능하게 합니다.

(*ARC가 인스턴스의 참조 갯수를 파악하여 추적하고 관리함. ARC: Auto Reference Counting)

 


 

메모리 할당

클래스는 ARC를 통해 인스턴트들을 관리합니다. 클래스는 Heap 메모리에 할당됩니다.

(*stack에 reference인 주소값을 할당하고, 실질적인 데이터는 heap에 할당합니다.)

 

구조체는 Stack 메모리에 할당됩니다. 그러나 *가변 길이 Collection들은 Heap 메모리에 할당됩니다.

(*Array, Dictionary, Set, String 이는 참조카운트를 가짐)

 


 

클래스와 구조체 사용전략

 구조체 내에 *참조카운트가 1개를 초과할 경우 *오버헤드가 발생하기 때문에 이 경우에는 클래스가 유리함.

 (*구조체 안에 클래스, 가변 길이 Collection 등)

(*오버헤드: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리)

 

 Stack 메모리의 처리가 Heap 보다 빠르기 때문에 단순 값, 구조체로 구현 가능한 것들은 구조체가 유리함.

 

 


열거형

enum SomeEnumeration { case Name1, case Name2 ... }

 관련된 값들을 그룹화 하여 정의하고 값들을 *type-safe 하게 처리할 수 있도록 하는 타입

(*타입 값을 명시하여 명확하게 하는것. enum 내에서의 값들은 동일한 타입이여야 한다.)

 

enum 내의 case 에 관련 값들의 타입을 명시하여 사용할 수도 있습니다. 아래는 switch와 함께 사용한 예시입니다.

 

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."

 


제네릭

 

func Name<T>( _: T , _: T ... ) { some action }

(*T 는 실제 타입이 아닌 placeholder이다. 실제 타입은 함수가 호출되는 순간 결정된다. )

 

 제네릭은 메소드 구현 시 기능은 같은데 자료형이 다를 때 쓰는 일반화 방법입니다. 자료형을 placeholder로 추상화 하여 실제 타입을 명시하지 않고 포괄적으로 함수 인자를 받습니다.

 

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

(*swift 내의 함수 인자는 상수이다. 함수 내부에서 인자값을 변경하기 위해서는 inout을 써서 값을 변경하고 함수 호출이 종료된 후에도 지속되도록 해야한다.)

 

 

 두개의 placeholder는 다음과 같이 사용합니다.

func someFunction<T, U>(someT: T, someU: U) {
    // function body goes here
}

 


제약

 

func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

 

 이 코드는 컴파일 되지 않습니다. 왜냐하면 Swift의 모든 타입이 == 연산자를 사용할 수 있는 것이 아니기 때문입니다. 예를들어 제가 임의의 클래스나 구조체를 만들었을 때 그것을 == 연산 할 수 있는지 Swift가 판별할 수 없습니다.

 

 그래서 이때는 Equatable 이라는 프로토콜을 제약하여 타입이 conform 하도록 만들어야 합니다.

(*Equtable은 equal 연산을 보장하는 프로토콜)

 

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

 

 

 


 

Reference

https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html Structures and Classes

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

https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html# Enumerations

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

https://icksw.tistory.com/256 Class와 Struct의 차이점

https://corykim0829.github.io/swift/Understanding-Swift-Performance/# [Swift] 스위프트 성능 이해하기 (1) - struct와 class의 성능 차이

 

 

 

'iOS-Development' 카테고리의 다른 글

Lottie  (0) 2022.05.08
CoreData  (0) 2022.04.24
UserDefaults  (0) 2022.04.19
SwiftUI와 Storyboard  (0) 2022.04.03
Swift : 개요  (0) 2022.03.08
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함