(출처: iOS 13 Programming Fundamentals with Swift, Swift, Xcode, and Cocoa Basics – Matt Neuburg)
protocol을 채택한 객체가 맞는지 Bool을 return하는 방식으로 판별할 수 있습니다.
protocol을 채택한 객체를 변수나 함수의 parameter로 받아서 사용하는 경우 type casting을 해주어야 합니다. 그렇지 않으면 객체의 고유한 속성(protocol에 없는)을 사용할 수 없습니다.
protocol Protocol_A { }
class Class_B: Protocol_A {
func b() { }
}
class Class_C: Protocol_A { }
//특정 객체인지 여부를 판별
func isB(object: Protocol_A) -> Bool{
return object is Class_B
}
//객체의 고유 속성을 사용하기 위해 casting 필요
func funcOfB (object: Protocol_A) {
object.b() //compile error
(object as? Class_B)?.b()
}
Properties의 경우
- var만 사용할 수 있습니다.(let은 사용 불가)
- {get} 또는 {get set} 으로 읽기 쓰기 제한을 할 수 있습니다. {get}의 경우 writable로 사용할 수 있지만 {get set}을 read-only로 사용할 수는 없습니다.
- static을 붙여서 static/class 프로퍼티를 선언할 수 있습니다. (class adopter는 이를 class 프로퍼티로 사용가능합니다.)
Method의 경우
- 함수의 body부분을 구현하지 않습니다. param과 return 타입은 지정할 수 있습니다. 함수를 구현하고 싶다면 extension protocol을 사용하면 됩니다.
- init과 subscript 사용할 수 있습니다. 참고로, {get}, {get set}을 사용한다는 점만 제외하면 protocol에서 subscript를 사용하는 것은 object type에서 subscript를 사용하는 것과 같습니다.
- 앞에 static을 붙여서 class method로 사용할 수 있습니다.
- struct, enum에서 mutating을 사용할 수 있도록 하기 위해서 mutating을 붙여줘야 합니다. (protocol을 채택한 객체에서 실제로 사용할 때는 mutating을 생략할 수 있습니다.)
protocol A {
func a()
}
protocol B {
func b()
}
//프로토콜 C가 다른 프로토콜 A, B를 채택합니다.
protocol C: A, B {
func c()
}
class SomeClass: C {
func c()
//프로토콜 C 뿐만 아니라,
//C가 채택한 프로토콜 A, B의 요구사항도 충족시켜야합니다.
func a()
func b()
}
var a: protocolA & protocolB & protocolC
var b: classA & protocolA
protocol A : classA { }
class B {
var b: A //classA를 동시에 포함합니다.
//즉, class A & protocol A 과 동일합니다.
}
protocol A : AnyObject { }
//또 다른 표현 법으로 AnyObject 대신 class를 사용가능
//그러나 swift 5 이전 표기법(나중에deprecated 됩니다)
protocol A : class { }
//Self는 이 프로토콜을 채택하게 될 객체를 의미합니다.
//주의: Self의 S는 대문자입니다!
protocol A Where Self: AnyObject { }
위 4번 항목에서 다룬 class를 채택한 protocol을 class protocol이라 합니다.
class protocol을 사용하는 주된 목적은 우리가 흔히 들어 알고 있는 delegate를 사용하기 위해서 입니다.
위의 예에서 class protocol을 type으로하는 property를 선언할 경우 그 property에는 해당 class를 상속받고 동시에 그 protocol을 채택한 인스턴스 객체가 할당되어야 한다고 설명했습니다. 아래의 예를 보겠습니다.
protocol DelegateProtocol: AnyOject {
func updateColor() -> UIColor
{
class CustomUIView: UIView {
weak var delegate: DelegateProtocol?
var color: UIColor = delegate?.updateColor()?? .white
}
class ViewController: UIViewController, DelegateProtocol {
let myView: CustomUIView!
func viewDidLoad() {
myView.delegate = self
}
func updateColor() -> UIColor {
}
}
위에서 var delegate의 타입은 class protocol 입니다. 그리고 이 property에는 UIViewController라는 class와 DelegateProtocol을 채택한 ViewController가 self라는 명령어로 할당되었습니다.
여기서 App이 실행되게 되면 var delegate가 호출되는 시점에 ViewController가 추가로 인스턴스화 되면서 참조카운트가 1이 올라가게 됩니다. 결과적으로 ViewController에 대한 참조 카운트가 2가 되는 것입니다.
강한순환참조가 발생되어 메모리의 누수가 발생되지 않도록 하기 위해 weak을 사용하였습니다.
delegate가 작동되는 원리는 인스턴스화 된 ViewController가 var delegate에 할당되면서 delgate에 "."입력하면 CustomView class에서 ViewController의 속성들에 접근을 할 수 있게 됩니다. 이로써 두화면의 속성들을 서로 연결하여 기능을 구현할 수 있게 됩니다.
사실 단순히 두 class 인스턴스를 연결하여 사용하고 싶다면 protocol을 사용하지 않아도 무방합니다. 아래 코드를 보겠습니다.
class CustomUIView: UIView {
weak var delegate: UIViewController?
var color: UIColor = delegate?.updateColor()?? .white
}
class ViewController: UIViewController {
let myView: CustomUIView!
func viewDidLoad() {
myView.delegate = self
}
func updateColor() -> UIColor {
}
}
class viewController: UIViewController {
init() {
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemeted")
}
}