본문 바로가기
iOS/SWIFT

[Swift] Chapter 20. 프로토콜

by 원만사 2021. 10. 11.
반응형

20.1 프로포콜이란


프로토콜은 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다. 어떤 프로토콜의 요구사항을 모두 따르는 타입은 '해당 프로토콜을 준수한다'고 표현한다. 타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 청사진의 기능을 모두 구현해야 한다. 즉, 프로토콜은 정의를 하고 제시를 할 뿐이지 스스로 기능을 구현하지는 않는다.

 

20.2 프로토콜 채택


protocol 키워드를 사용하여 정의한다.

protocol 프로토콜 이름 {
	프로토콜 정의
}

 

구조체, 클래스, 열거형 등에서 프로토콜을 채택하려면 타입 이름 뒤에 콜론(:)을 붙여준 후 채택할 프로토콜 이름을 쉼표(,)로 구분하여 명시해준다.

 

/*
	코드 20-1. 타입의 프로토콜 채택
*/

struct SomeStruct: AProtocol, AnotherProtocol {
    // 구조체 정의
}

class SomeClass: Aprotocol, AnotherPtocol {
    // 클래스 정의
}

enum SomeEnum: Aprotocol, AnotherProtocol {
    // 열거형 정의
}

만약 클래스가 다른 클래스를 상속받는다면 상속받을 클래스 이름 다음에 채택할 프로토콜을 나열해준다.

 

/*
	코드 20-2. SuperClass를 상속받는 클래스의 프로토콜 채택
*/

class SomeClass: SuperClass, Aprotocol, AnotherProtocol {
    // 클래스 정의
}

 

20.3 프로토콜 요구사항


프로토콜은 타입이 특정 기능을 실행하기 위해 필요한 기능을 요구한다. 프로토콜이 자신을 채택한 타입에 요구하는 사항은 프로퍼티나 메서드와 같은 기능들이다.

 

20.3.1 프로퍼티 요구

프로토콜은 자신을 채택한 타입이 어떤 프로퍼티를 구현해야 하는지 요구할 수 있지만 그 프로퍼티의 종류(연산 프로퍼티인지, 저장 프로퍼티인지 등)는 따로 신경쓰지 않는다. 프로토콜이 요구하는 프로퍼티의 이름과 타입만 맞도록 구현해주면 된다. 다만 프로퍼티를 읽기 전용으로 할지 혹은 읽고 쓰기가 모두 가능하게 할지는 프로토콜이 정해야 한다.

프로토콜이 읽고 쓰시가 가능한 프로퍼티를 요구한다면 읽기만 가능한 상수 저장 프로퍼티 또는 읽기 전용 연산 프로퍼티를 구현할 수 없다. 프로토콜이 읽기 가능한 프로퍼티를 요구한다면 상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티를 포함해서 어떤 식으로든 프로퍼티를 구현할 수 있다.

프로토콜의 프로퍼티 요구사항은 항상 var 키워드를 사용한 변수 프로퍼티로 정의한다. 읽기와 쓰기가 모두 가능한 프로퍼티는 프로퍼티의 정의 뒤에 { get set }이라고 명시하며, 읽기 전용 프로퍼티는 프로퍼티의 정의 뒤에 { get }이라고 명시해준다.

 

/*
	코드 20-3. 프로퍼티 요구
*/

protocol SomeProtocol {
    var settableProperty: String { get set }
    var notNeedToBeSettableProperty: String { get }
}

protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
    static var anotherTypeProperty: Int { get }
}

[코드 20-3]의 SomeProtocol에 정의된 settableProperty는 읽기와 쓰기 모두를 요구했고, notNeedToBeSettableProperty는 읽기만 가능하다면 어떻게 구현되어도 상관없다는 요구사항이다.

타입 프로퍼티를 요구하려면 static 키워드를 사용한다. 클래스의 class 타입 프로퍼티와 static 타입 프로퍼티를 따로 구분하지 않고 모두 static 키워드를 사용한다.

 

/*
	코드 20-4. Sendable 프로토콜과 Sendable 프로토콜을 준수하는 Message와 Mail 클래스
*/

protocol Sendable {
    var from: String { get }
    var to: String { get }
}

class Message: Sendable {
    var sender: String
    var from: String {
        return self.sender
    }
    
    var to: String
    
    init(sender: String, receiver: String) {
        self.sender = sender
        self.to = receiver
    }
}

class Mail: Sendable {
    var from: String
    var to: String
    
    init(sender: String, receiver: String) {
        self.from = sender
        self.to = receiver
    }
}

[코드 20-4]의 Sendable 프로토콜은 무언가의 전송을 가능하게 하기 위한 프로퍼티인 from과 to를 요구한다. 그래서 Sendable 프로토콜을 채택하여 준수하는 Message와 Mail 클래스는 모두 from과 to 프로퍼티를 갖는다. 다만 Message 클래스의 from 프로퍼티는 읽기 전용 연산 프로퍼티라는 점이 Mail 클래스의 from 프로퍼티와 다를 뿐이다. Sendable 프로토콜에서 요구한 프로퍼티는 읽기 가능한 프로퍼티였지만 실제로 프로토콜을 채택한 클래스에서 구현할 때는 읽고 쓰기가 가능한 프로퍼티로 구현해도 전혀 문제가 없다.

20.3.2 메서드 요구

프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수도 있다. 프로토콜이 요구할 메서드는 프로토콜 정의에서 작성한다. 다만 메서드의 실제 구현부인 중괄호 부분은 제외하고 메서드 이름, 매개변수, 반환 타입 등만 작성하여 가변 매개변수도 허용한다.

프로토콜의 메서드 요구에서는 매개변수 기본값을 지정할 수 없다. 타입 메서드를 요구할 때는 타입 프로퍼티 요구와 마찬가지로 앞에 static 키워드를 명시한다. 클래스에서 실제 구현할 때는 static 키워드나 class 키워드 어느 쪽을 사용해도 무방하다.

 

/*
	코드 20-5. Receiveable, Sendable 프로토콜과 두 프로토콜을 준수하는 Message와 Mail 클래스
*/

// 무언가를 수신받을 수 있는 기능
protocol Receiveable {
    func received(data: Any, from: Sendable)
}

// 무언가를 발신할 수 있는 기능
protocol Sendable {
    var from: Sendable { get }
    var to: Receiveable? { get }
    
    func send(data: Any)
    
    static func isSendableInstance(_ instance: Any) -> Bool
}

// 수신, 발신이 가능한 Message 클래스
class Message: Sendable, Receiveable {
    // 발신은 발신 가능한 객체, 즉 Sendable 프로토콜을 준수하는 타입의 인스턴스여야 한다.
    var from: Sendable {
        return self
    }
    
    // 상대방은 수신 가능한 객체, 즉 Receiveable 프로토콜을 준수하는 타입의 인스턴스여야 한다.
    var to: Receiveable?
    
    // 메시지를 발신
    func send(data: Any) {
        guard let receiver: Receiveable = self.to else {
            print("Message has no receiver")
            return
        }
        
        // 수신 가능한 인스턴스의 received 메서드를 호출
        receiver.received(data: data, from: self.from)
    }
    
    // 메시지를 수신
    func received(data: Any, from: Sendable) {
        print("Message received \(data) from \(from)")
    }
    
    // class 메서드이므로 상속이 가능하다.
    class func isSendableInstance(_ instance: Any) -> Bool {
        if let sendableInstance: Sendable = instance as? Sendable {
            return sendableInstance.to != nil
        }
        return false
    }
}

// 수신, 발신이 가능한 Mail 클래스
class Mail: Sendable, Receiveable {
    var from: Sendable {
        return self
    }
    
    var to: Receiveable?
    
    func send(data: Any) {
        guard let receiver: Receiveable = self.to else {
            print("Mail has no receiver")
            return
        }
        
        receiver.received(data: data, from: self.from)
    }
    
    func received(data: Any, from: Sendable) {
        print("Mail received \(data) from \(from)")
    }
    
    // static 메서드이므로 상속이 불가능하다.
    static func isSendableInstance(_ instance: Any) -> Bool {
        if let sendableInstance: Sendable = instance as? Sendable {
            return sendableInstance.to != nil
        }
        return false
    }
}


// 두 Message 인스턴스를 생성한다.
let myPhoneMessage: Message = Message()
let yourPhoneMessage: Message = Message()

// 아직 수신받을 인스턴스가 없다
myPhoneMessage.send(data: "Hello") // Message has no receiver

// Message 인스턴스는 발신과 수신이 모두 가능하므로 메시지를 주고 받을 수 있다
myPhoneMessage.to = yourPhoneMessage
myPhoneMessage.send(data: "Hello") // Message received Hello from Message

// 두 Mail 인스턴스를 생성한다.
let myMail: Mail = Mail()
let yourMail: Mail = Mail()

myMail.send(data: "Hi") // Mail has no receiver

// Mail과 Message 모두 Sendable과 Receiveable 프로토콜을 준수하므로
// 서로 주고 받을 수 있다
myMail.to = yourMail
myMail.send(data: "Hi") // Mail received Hi from Mail

myMail.to = myPhoneMessage
myMail.send(data: "Bye") // Message received Bye from Mail

// String은 Sendable 프로토콜을 준수하지 않는다.
Message.isSendableInstance("Hello") // false

// Mail과 Message는 Sendable 프로토콜을 준수한다
Message.isSendableInstance(myPhoneMessage) // true

// yourPhoneMessage는 to 프로퍼티가 설정되지 않아서 보낼 수 없는 상태이다
Message.isSendableInstance(yourPhoneMessage) // false
Mail.isSendableInstance(myPhoneMessage) // true
Mail.isSendableInstance(myMail) // true

 

NOTE_타입으로서의 프로토콜
프로토콜은 요구만 하고 스스로 기능을 구현하지는 않는다. 그렇지만 프로토콜은 코드에서 완전한 하나의 타입으로 사용되기에 여러 위치에서 프로토콜을 타입으로 사용할 수 있다.
- 함수, 메서드, 이니셜라이저에서 매개변수 타입이나 반환 타입으로 사용될 수 있다.
- 프로퍼티, 변수, 상수 등의 타입으로 사용될 수 있다.
- 배열, 딕셔너리 등 컨테이너 요소의 타입으로 사용될 수 있다.

 

20.3.3 가변 메서드 요구

값 타입의 인스턴스 메서드에서 자신 내부의 값을 변경하고자 할 때는 메서드의 func 키워드 앞에 mutating 키워드를 적어 메서드에서 인스턴스 내부의 값을 변경한다는 것을 확실히 해준다.

프로토콜이 어떤 타입이든 간에 인스턴스 내부의 값을 변경해야 하는 메서드를 요구하려면 프로토콜의 메서드 정의 앞에 mutating 키워드를 명시해야 한다. 값 타입인 구조체와 열거형의 메서드 앞에는 mutating을 써줘야 하지만 클래스 구현에서는 mutating 키워드를 써주지 않아도 된다.

 

/*
	코드 20-6. Resettable 프로토콜의 가변 메서드 요구
*/

protocol Resettable {
    mutating func reset()
}

class Person: Resettable {
    var name: String?
    var age: Int?
    
    func reset() {
        self.name = nil
        self.age = nil
    }
}

struct Point: Resettable {
    var x: Int = 0
    var y: Int = 0
    
    mutating func reset() {
        self.x = 0
        self.y = 0
    }
}

enum Direction: Resettable {
    case east, west, south, north, unknown
    
    mutating func reset() {
        self = Direction.unknown
    }
}

만약 Resettable 프로토콜에서 가변 메서드를 요구하지 않는다면, 값 타입의 인스턴스 내부 값을 변경하는 mutating 메서드는 구현이 불가능하다.

 

20.3.4 이니셜라이저 요구

프로토콜은 특정한 이니셜라이저를 요구할 수도 있다. 프로토콜에서 이니셜라이저를 요구하려면 메서드 요구와 마찬가지로 이니셜라이저를 정의하지만 구현은 하지 않는다.

/*
	코드 20-7. 프로토콜의 이니셜라이저 요구와 구조체의 이니셜라이저 요구 구현
*/

protocol Named {
    var name: String { get }
    
    init(name: String)
}

struct Pet: Named {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

구조체는 상속할 수 없기 때문에 이니셜라이저 요구에 대해 크게 신경쓸 필요가 없지만 클래스의 경우라면 조금 다르다.

 

/*
	코드 20-8. 클래스의 이니셜라이저 요구 구현
*/

class Person: Named {
    var name: String
    
    required init(name: String) {
        self.name = name
    }
}

 

클래스 타입에서 프로토콜의 이니셜라이저 요구에 부합하는 이니셜라이저를 구현할 때는 이니셜라이저가 지정 이니셜라이저인지 편의 이니셜라이저인지는 중요하지 않다. 그러나 이니셜라이저 요구에 부합하는 이니셜라이저를 구현할 때는 required 식별자를 붙인 요구 이니셜라이저로 구현해야 한다. [코드 20-8]의 Person 클래스를 상속받는 모든 클래스는 Named 프로토콜을 준수해야 하며, 이는 곧 상속받는 클래스에 해당 이니셜라이저를 모두 구현해야 한다는 뜻이다. 그렇기 때문에 Named에서 요구하는 init(name:) 이니셜라이저를 required 식별자를 붙인 요청 이니셜라이저로 구현해야 한다.

만약에 클래스 자체가 상속받을 수 없는 final 클래스라면 required 식별자를 붙여줄 필요가 없다. 상속할 수 없는 클래스의 요청 이니셜라이저 구현은 무의미하기 때문이다.

 

/*
	코드 20-9. 상속 불가능한 클래스의 이니셜라이저 요구 구현
*/

final class Person: Named {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

 

만약 특정 클래스에 프로토콜이 요구하는 이니셜라이저가 이미 구현되어 있는 상황에서 그 클래스를 상속받은 클래스가 있다면, required와 override 식별자를 모두 명시하여 프로토콜에서 요구하는 이니셜라이저를 구현해주어야 한다.

 

/*
	코드 20-10. 상속받은 클래스의 이니셜라이저 요구 구현 및 재정의
*/

class School {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

class MiddleSchool: School, Named {
    required override init(name: String) {
        super.init(name: name)
    }
}

[코드 20-10]에서 School 클래스는 Named 프로토콜을 채택하지 않았지만 Named 프로토콜이 요구하는 이니셜라이저가 이미 있는 상태이다. 그런데 MiddleSchool 클래스는 School 클래스를 상속받았으며, Named 프로토콜을 채택했다. 그렇기 때문에 override와 required 식별자를 모두 표기해야 한다. 순서는 상관없다.

프로토콜은 일반 이니셜라이저 외에도 실패 가능한 이니셜라이저를 요구할 수도 있다. 실패 가능한 이니셜라이저를 요구하는 프로토콜을 준수하는 타입은 해당 이니셜라이저를 구현할 때 실패 가능한 이니셜라이저로 구현해도, 일반적인 이니셜라이저로 구현해도 무방하다.

 

/*
	코드 20-11. 실패 가능한 이니셜라이저 요구를 포함하는 Named 프로토콜과 
							Named 프로토콜을 준수하는 다양한 타입들
*/

protocol Named {
    var name: String { get }
    
    init?(name: String)
}

struct Animal: Named {
    var name: String
    
    init!(name: String) {
        self.name = name
    }
}

struct Pet: Named {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

class Person: Named {
    var name: String
    
    required init(name: String) {
        self.name = name
    }
}

class School: Named {
    var name: String
    
    required init?(name: String) {
        self.name = name
    }
}

 

20.4 프로토콜의 상속과 클래스 전용 프로토콜


프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있다.

/*
	코드 20-12. 프로토콜의 상속
*/

protocol Readable {
    func read()
}

protocol Writeable {
    func write()
}

protocol ReadSpeakable: Readable {
    func speak()
}

protocol ReadWriteSpeakable: Readable, Writeable {
    func speak()
}

class SomeClass: ReadWriteSpeakable {
    func read() {
        print("Read")
    }
    
    func write() {
        print("Write")
    }
    
    func speak() {
        print("Speak")
    }
}

프로토콜의 상속 리스트에 class 키워드를 추가해 프로토콜이 클래스 타입에만 채택될 수 있도록 제한할 수도 있다. 클래스 전용 프로토콜로 제한을 주기 위해서는 프로토콜의 상속 리스트의 맨 처음에 class 키워드가 위치해야 한다.

 

/*
	코드 20-13. 클래스 전용 프로토콜의 정의
*/

protocol ClassOnlyProtocol: class, Readable, Writeable {
    // 추가 요구사항
}

class SomeClass: ClassOnlyProtocol {
    func read() { }
    func write() { }
}

// 오류! ClassOnlyProtocol 프로토콜은 클래스 타입에만 채택 가능하다
//struct SomeStruct: ClassOnlyProtocol {
//    func read() { }
//    func write() { }
//}

 

20.5 프로토콜 조합과 프로토콜 준수 확인


하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 하나의 매개변수에 여러 프로토콜을 한 번에 조합하여 요구할 수 있다. 프로토콜을 조합하여 요구할 때는 SomeProtocol & AnotherProtocol과 같이 표현한다. 또, 하나의 매개변수가 프로토콜을 둘 이상 요구할 수도 있다. 이때도 마찬가지로 앰퍼샌드(&)를 여러 프로토콜 이름 사이에 써주면 된다.

더불어 특정 클래스의 인스턴스 역할을 할 수 있는지 함께 확인할 수 있다. 구조체나 열거형 타입은 조합할 수 없다. 그리고 조합 중 클래스 타입은 한 타입만 조합할 수 있다.

 

/*
	코드 20-14. 프로토콜 조합 및 프로토콜, 클래스 조합
*/

protocol Named {
    var name: String { get }
}

protocol Aged {
    var age: Int { get }
}

struct Person: Named, Aged {
    var name: String
    var age: Int
}

class Car: Named {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}


class Truck: Car, Aged {
    var age: Int
    
    init(name: String, age: Int) {
        self.age = age
        super.init(name: name)
    }
}

func celebrateBirthday(to celebrator: Named & Aged) {
    print("Happy birthday \(celebrator.name)!! Now your are \(celebrator.age)")
}

let yagom: Person = Person(name: "yagom", age: 99)
celebrateBirthday(to: yagom)

let myCar: Car = Car(name: "Boong Boong")
//celebrateBirthday(to: myCar) // Aged를 충족시키지 못해 오류발생

// 클래스 & 프로토콜 조합에서 클래스 타입은 한 타입만 조합할 수 있다. 오류발생
//var someVariable: Car & Truck & Aged

// Car 클래스의 인스턴스 역할도 수행할 수 있고,
// Aged 프로토콜을 준수하는 인스턴스만 할당할 수 있다
var someVariable: Car & Aged

// Truck 인스턴스는 Car 클래스 역할도 할 수 있고 Aged 프로토콜도 준수하므로 할당할 수 있다.
someVariable = Truck(name: "Truck", age: 5)

// Car의 인스턴스인 myCar는 Aged 프로토콜을 준수하지 않으므로 할당할 수 없다
// 오류 발생
//someVariable = myCar

 

타입캐스팅에 사용하던 is와 as 연산자를 통해 대상이 프로토콜을 준수하는지 확인할 수도 있고, 특정 프로토콜로 캐스팅할 수 있다. 프로토콜을 준수하는지 확인하거나 다른 프로토콜로 캐스팅하는 방법은 타입캐스팅과 똑같다.

  • is 연산자를 통해 해당 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있다.
  • as? 다운캐스팅 연산자를 통해 다른 프로토콜로 다운캐스팅을 시도해볼 수 있다.
  • as! 다운캐스팅 연산자를 통해 다른 프로토콜로 강제 다운캐스팅을 할 수 있다.

 

/*
	코드 20-15. 프로토콜 확인 및 캐스팅
*/

print(yagom is Named) // true
print(yagom is Aged)  // true

print(myCar is Named) // true
print(myCar is Aged)  // false

if let castedInstance: Named = yagom as? Named {
    print("\(castedInstance) is Named")
} // Person is Named

if let castedInstance: Aged = yagom as? Aged {
    print("\(castedInstance) is Aged")
} // Person is Aged

if let castedInstance: Named = myCar as? Named {
    print("\(castedInstance) is Named")
} // Car is Named

if let castedInstance: Aged = myCar as? Aged {
    print("\(castedInstance) is Aged")
} // 출력 없음.. 캐스팅 실패

 

20.6 프로토콜의 선택적 요구


프로토콜의 요구사항 중 일부를 선택적 요구사항으로 지정할 수 있다. 다만 먼저 고려해야 할 사항이 있다. 선택적 요구사항을 정의하고 싶은 프로토콜은 objc 속성이 부여된 프로토콜이어야 한다.

objc 속성이 부여되는 프로토콜은 Objective-C 클래스를 상속받은 클래스에서만 채택할 수 있다. 즉, 열거형이나 구조체 등에서는 objc 속성이 부여된 프로토콜은 아예 채택할 수 없다.

 

NOTE_Foundation 프레임워크
objc 속성을 사용하려면 애플의 Foundation 프레임워크 모듈을 임포트해야 한다.

 

선택적 요구를 하면 프로토콜을 준수하는 타입에 해당 요구사항을 필수로 구현할 필요가 없다. 선택적 요구사항은 optional 식별자를 요구사항의 정의 앞에 붙여주면 된다. 만약 메서드나 프로퍼티를 선택적 요구사항으로 요구하게 되면 그 요구사항의 타입은 자동적으로 옵셔널이 된다. 메서드의 매개변수나 반환 타입이 옵셔널이 되는 것이 아니라 메서드(함수) 자체의 타입이 옵셔널이 된다

선택적 요구사항은 그 프로토콜을 준수하는 타입에 구현되어 있지 않을 수 있기 때문에 옵셔널 체이닝을 통해 호출할 수 있다. 프로퍼티뿐만 아니라 메서드도 마찬가지다.

 

/*
	코드 20-16. 프로토콜의 선택적 요구
*/

import Foundation

@objc protocol Moveable {
    func walk()
    @objc optional func fly()
}

// 걷기만 할 수 있는 호랑이
class Tiger: NSObject, Moveable {
    func walk() {
        print("Tiger walks")
    }
}

// 걷고 날 수 있는 새
class Bird: NSObject, Moveable {
    func walk() {
        print("Bird walks")
    }
    
    func fly() {
        print("Bird flys")
    }
}

let tiger: Tiger = Tiger()
let bird: Bird = Bird()

tiger.walk() // Tiger walks
bird.walk()  // Bird walks

bird.fly() // Bird flys

var movableInstance: Moveable = tiger
movableInstance.fly?() // 응답 없음

movableInstance = bird
movableInstance.fly?() // Bird flys

objc 속성의 프로토콜을 사용하기 위해 Tiger와 Bird는 각각 Objective-C의 클래스인 NSObject를 상속받았다. Mobeable 프로토콜 변수에 각 인스턴스가 할당되었을 때는 인스턴스의 타입에 실제로 fly() 메서드가 구현되어 있는지 알 수 없으므로 옵셔널 체인을 이용하여 fly() 메서드 호출을 시도해본다. 옵셔널 체인을 사용할 때는 메서드 이름 뒤에 물음표를 붙여 표현한다.

 

20.7 프로토콜 변수와 상수


프로토콜 이름을 타입으로 갖는 변수 또는 상수에는 그 프로토콜을 준수하는 타입의 어떤 인스턴스라도 할당할 수 있다.

프로토콜은 프로토콜 이름만으로 자기 스스로 인스턴스를 생성하고 초기화할 수는 없다. 그렇지만 프로토콜 변수나 상수를 생성하여 특정 프로토콜을 준수하는 타입의 인스턴스를 할당할 수는 있다.

 

20.8 위임을 위한 프로토콜


위임(Delegation)은 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴이다. 책무를 위임하기 위해 정의한 프로토콜을 준수하는 타입은 자신에게 위임될 일정 책무를 할 수 있다는 것을 보장한다. 위임은 사용자의 특정 행동에 반응하기 위해 사용되기도 하며, 비동기 처리에도 많이 사용한다.

위임 패턴(Delegation Pattern)은 애플의 프레임워크에서 사용하는 주요한 패턴 중 하나이다.

 

 

 

스위프트 프로그래밍: Swift 5 - 교보문고

객체지향, 함수형, 프로토콜 지향 패러다임까지 한 번에! | 스위프트를 제대로 이해하고 싶은 개발자를 위한 책스위프트는 iOS와 macOS용 애플리케이션 개발에 주로 사용하는 프로그래밍 언어입니

www.kyobobook.co.kr

 

반응형

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

[Swift] Chapter 22. 제네릭  (0) 2021.10.13
[Swift] Chapter 21. 익스텐션  (0) 2021.10.11
[Swift] Chapter 19. 타입캐스팅  (0) 2021.10.11
[Swift] Chapter 18. 상속  (0) 2021.10.10
[Swift] Chapter 17. 서브스크립트  (0) 2021.10.10

댓글