나만의 공부 노트
4강 정리 본문
학습 내용
프로토콜
API가 Caller에게 원하는 메소드나 프로퍼티를 Caller가 구현하도록 하며, Caller는 자신이 원하는 class/ struct/ enum을 전달 할 수 있도록 합니다.
가장 중요한 것은 프로토콜은 하나의 타입이라는 것입니다.
프로토콜에서 구현되는 모든 메소드와 프로퍼티들은 프로토콜을 상속하는 경우 반드시 구현해야 합니다.
프로퍼티의 경우, { get } 또는 { get, set } 인지를 명시해줘야 합니다.
struct가 protocol을 채택할 수도 있기 때문에, 변경을 요하는 함수는 mutating 선언을 해주어야 합니다.
만약 오로지 class만 프로토콜을 채택할 수 있게 만들고 싶다면, 선언한 프로토콜명 뒤에 class를 명시해줍니다.
class의 경우 init은 반드시 required를 명시해 줘야 합니다.
위의 클래스를 상속받는 클래스는 마찬가지로 프로토콜 또한 준수해야하기 때문에, required 선언이 필요합니다.
required 키워드를 사용하면 상속받는 클래스가 해당 init을 상속받지 못하게 막습니다.


swift는 프로토콜을 강제적으로 구현해야하지만, objc에서는 프로토콜을 선택적으로 구현이 가능합니다.
따라서 delegate 함수들은 대부분 objc로 구현되어있고 선택적으로 구현해도 문제가 없는 이유입니다.
즉, 프로토콜 선언 시 프로토콜 이름 앞에 @objc 키워드와 함수 앞에 @objc optional 키워드를 붙이면 선택적으로 구현한다는 뜻입니다.(단, 선택적 프로토콜 사용시 프로토콜을 채택한 객체는 Class만 가능하며 NSObject를 무조건 상속받은 상태이여야 합니다.)
스위프트에서는 objc 문법을 사용할 때 @objc 키워드를 붙여주어야 하고, optional 키워드는 순수히 objective-c 문법입니다.
Hashable 프로토콜

Hashable 프로토콜은 Equatable 프로토콜을 준수하며, hashValue가 필요합니다.

다시 Equatable 프로토콜은 == 연산자를 구현해야합니다.(self는 값 타입이라면 자신의 값을, 참조 타입이라면 자신의 주소값을 나타내지만 Self는 자신의 타입을 나타냅니다.)
swift는 기본적으로 == 연산자가 내장되어 있는 것이 아닌 전부 위의 프로토콜을 채택하여 구현되어 있습니다.(매우 일관적입니다.)
* Swift 4.1에서 Hashable 프로토콜의 변경사항이 있습니다.
* var hashValue와 static func ==을 모두 구현해야 했었지만, 이제 자동으로 해당 프로퍼티와 메소드를 생성해줍니다.
* 저장 프로퍼티를 사용하여 hashValue를 만들어냅니다.(즉 저장 프로퍼티가 모두 같다면, 동일하다고 판단합니다.)
+ hashValue는 depreciate되었습니다.
+ 기존의 hashValue는 자동으로 저장 프로퍼티를 모두 이용하여 만들거나, 혹은 사용자가 직접 정의하여 생성하였지만, 이제 func hash를 통해 hashValue 값을 설정하는 방법으로 바뀌게 되었습니다.
+ 따라서 func hash(into hasher: inout Hasher)를 이용하여 해쉬값을 설정합니다.
+ 해당 함수 안에 hasher.combine(_ value: Hashable)을 실행하면 추가한 value들을 이용해 통해 자동적으로 hashvalue값을 만들어줍니다.
inout 키워드
기본적으로 함수의 인자를 전달받으면 let 변수로 복사가 됩니다.
하지만 인수의 타입 앞에 inout 키워드를 붙이면, 주소값을 전달받기 때문에 함수 내부에서 받아왔던 인자의 값을 변경할 수 있습니다.
단, 이러한 함수를 사용할 때 인자 앞에 &를 반드시 붙여서 사용해야하고, 이는 주소값이라는 것을 명확히 해주는 효과도 있습니다.
배열에서의 프로토콜



위의 계층 구조를 보면, 모든 것들이 Sequence 프로토콜을 기반으로 작성되었다는 사실을 알 수 있습니다.
Array 타입을 사용할 때 Sequence가 대부분의 기능을 제공합니다.
map, filter 뿐만 아니라, Sequence 안에서 특정 조건을 만족하는 첫 번째 요소를 찾는 기능까지, 모두 다 Sequence 프로토콜 안에 정의되어 있습니다.
Sequence가 가장 단순한 것이고, 나머지는 Sequence를 기반으로 작성된 것들입니다.
Sequence를 채택했다면, func makeIterator() -> some IteratorProtocol { } 만 구현해주면 됩니다.
IteratorProtocol은 단순히 next()만 구현해주면 됩니다.
Sequence에 extension으로 contains, forEach, joined, min, max, filter, map 등이 추가로 구현이 되어있어서(default implementation) 재정의하지 않아도 바로 사용이 가능합니다.
+ 다만 재정의할 경우, 위의 함수는 sequence의 함수들이였고 -> sequence를 상속받는 새로운 객체를 만들었으므로 override가 가능해집니다.
+ 기본적으로 extension으로 재정의하면 같은 레벨의 객체는 override 되지 않습니다.(extension의 정의 위에 객체의 정의가 뒤덮입니다. 즉 extension의 정의는 무시됩니다.)
Collection에는 subscripting(= []), index 등이 있습니다.
String
swift는 기본적으로 ascii를 바탕으로 character를 형성하지 않고, unicode를 바탕으로 character를 형성합니다.
따라서 character마다 unicode의 갯수가 다르기 때문에 index로 순차적으로 접근할 수 없습니다.
String.Index를 만들어서 접근해야합니다.
또는 Array(String)을 이용하면 Array<Character>의 형태로 배열이 생성되므로 정수형 index로 접근이 가능하기도 합니다.
NSAttributedString
String과 Dictionary로 구성되어 있고, Dictionary에 각각의 character마다 폰트 색상, 크기와 같은 속성들이 들어있습니다.

여기서 Any는 objc의 API입니다.
따라서 swift가 타입추론을 할 수 없으므로 타입을 명확히 명시해주어야합니다.
Paul교수님께선 위의 경우 enum을 이용해 구조를 만들어야하지만, objc 당시엔 enum이라는 데이터 구조가 없어서 위와 같은 형태를 띄게 되었다고 합니다.(하위 호환성을 위해 Any를 사용하고 있는데, 언젠가는 수정될 것이라 생각합니다.)
NSString
NSString은 클래스, String은 구조체로 서로 본질적으로 다릅니다.
따라서 unicode사용과 index를 매기는 법도 달라서 사용할 때 주의해야 합니다.
위의 NSAttributedString은 기본적으로 NSString과 NSRange를 사용하고 있습니다.
NSString과 String은 bridging 기법을 지원해서, NSString이 필요한 자리에 String을 넣어도 알아서 잘 변환해줍니다.
일급 객체(First Class Citizen)
1. 변수나 데이타에 할당 할 수 있어야 합니다.
2. 객체의 인자로 넘길 수 있어야 합니다.
3. 객체의 리턴값으로 리턴 할수 있어야 합니다.