[Swift] 프로토콜 - 4 (Protocol)
프로토콜 타입 콜렉션
프로토콜 타입을 콜렉션 타입에 저장할 타입으로 사용이 가능하다
protocol Age {
var age: Int { get }
}
class Human: Age {
var age: Int
init(age: Int) {
self.age = age
}
}
class Animal: Age {
var age: Int
init(age: Int) {
self.age = age
}
}
var arr: [Age] = [Human(age: 20), Animal(age: 10)]
for e in arr {
print(e.age)
}
각 인스턴스의 실제 타입은 다르지만 모두 Age 프로토콜을 준수하기 때문에
Age의 요구사항인 age 프로퍼티를 사용할 수 있음
이렇게 사용할 경우에는 프로토콜에 정의되지 않은 실제 인스턴스의 고유 프로퍼티 및 메서드는 사용할 수 없다
프로토콜 상속
프로토콜은 하나 이상의 다른 프로토콜을 상속할 수 있다
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// protocol definition goes here
}
상속되는 요구사항 외에 요구사항을 추가할 수 있다
protocol Introduce {
var info: String { get }
}
protocol Commentator: Introduce {
var comment: String { get }
}
이런 프로토콜을 채택하면, 상속 받는 프로토콜의 요구사항도 모두 구현해야 한다
extension Int: Commentator {
var info: String { "\(self)" }
var comment: String { "my number is \(info)" }
}
print(8.comment)
// my number is 8
클래스 전용 프로토콜
프로토콜을 정의할 때 AnyObject를 상속 받으면 클래스 타입만 해당 프로토콜을 채택할 수 있도록 제한할 수 있다
protocol ClassProtocol: AnyObject {}
class SomeClass: ClassProtocol {}
// Non-class type 'SomeStruct' cannot conform to class protocol 'ClassProtocol'
struct SomeStruct: ClassProtocol {}
프로토콜 구성
여러 프로토콜을 단일 요구사항으로 결합할 수 있다
프로토콜 구성은 프로토콜들을 결합하여 하나의 임시 로컬 프로토컬을 정의한 것 처럼 동작한다
새로운 프로토콜 타입을 정의하는 것이 아니다
프로토콜 구성은 앰퍼샌드(&)를 사용하여 필요한 만큼 프로토콜을 결합할 수 있다
결합한 프로토콜의 요구사항만 사용이 가능하다
protocol Name {
var name: String { get }
}
protocol Age {
var age: Int { get }
}
class Human: Name, Age {
var name: String
var age: Int
init(_ name: String, _ age: Int) {
self.name = name
self.age = age
}
}
class Animal: Name {
var name: String
init(_ name: String) { self.name = name }
}
func printInfo(model: Name & Age ) {
print(model.name)
print(model.age)
}
printInfo(model: Human("sweetfood",20))
// Argument type 'Animal' does not conform to expected type 'Age'
printInfo(model: Animal("lucy"))
Human은 Name, Age를 모두 채택하여 printInfo의 매개변수로 사용이 가능하지만
Animal은 사용할 수 없다
프로토콜 일치 확인
Type casting의 is, as 연산자를 사용하여
프로토콜을 확인하고 특정 프로토콜로 캐스팅 할 수 있다.
- is : 프로토콜을 준수하면 true, 그렇지 않으면 false를 반환한다
- as? : 프로토콜을 준수하면 해당 프로토콜로 캐스팅 하고 그렇지 않으면 nil 반환
- as! : 강제로 해당 프로토콜로 캐스팅한다. 성공하지 못하면 추후 런타임 에러 발생
protocol HasArea {
var area: Double { get }
}
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi radius radius }
init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area
선택적 프로토콜 요구조건 (Optional Protocol Requirements)
선택적 요구사항을 정의할 수 있다
선택적 요구사항은 프로토콜을 채택한 타입에서 필수로 구현하지 않아도 됨을 의미
선택적 요구사항 정의시 @objc optional 키워드를 사용하여 정의한다
@objc 키워드를 사용하면 클래스 타입에서만 채택할 수 있기 때문에 선택적 프로토콜 요구조건은 클래스에서만
채택할 수 있다
선택적 요구사항이 있는 프로토콜을 타입으로 사용하여 프로퍼티나 메서드를 호출 시 ( 델리게이트 패턴 같은)
자동으로 옵셔널로 취급하여 사용한다.
선택적이기 때문에 구현을 했을 수도, 안했을수도 있기 때문
예를들어
@objc protocol SomeProtocol {
@objc optional someMethod()
}
SomeProtocol 타입으로 사용하는 프로퍼티에서 someMethod() 메서드를 호출하면
someMethod?()로 사용해야한다
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
// 3
// 6
// 9
// 12
Counter 타입의 increment() 메서드 안을 보면
if let amount = dataSource?.increment?(forCount: count) {
...
} else if let amount = dataSource?.fixedIncrement {
...
}
increment?(forCount: count)로 사용, fixedIncrement를 옵셔널 바인딩으로 사용하는 것을 볼 수 있다.
댓글남기기