티스토리 뷰

Swift

Swift ) Protocols (2)

Zedd0202 2017. 10. 18. 22:01
반응형

 안녕하세요 :) Zedd입니다.

오늘을 프로토콜 두번째 시간!~~

바로 시작할게요.

그 전에

: < Protocols (1) >

을 읽고오시는 것을 추천드립니다!




Method Requirements


저번 시간에 "프로퍼티 요구사항"까지 배웠었죠?

오늘은 "메소드 요구사항"을 배워볼거에요 :)


프로토콜은 특정 인스턴스 메소드 및 타입 메소드가 타입을 준수하여 구현되도록 요구할 수 있습니다. 

메소드는 일반 인스턴스 및 타입 메소드와 완전히 동일하지만, 중괄호와 메소드 본문없이 프로토콜 정의의 일부로 작성됩니다. 

Variadic(가변?) 파라미터는 일반 메소드와 동일한 규칙에 따라 허용됩니다.

그러나 프로토콜 정의 내의 메소드 매개변수에는 기본값을 지정 할 수 없습니다. 



자, 프로토콜에서 "메소드"는 어떻게 정의하느냐.

위에서 프로토콜에서는~~중괄호랑 메소드 본문은 작성하지 말고 "정의"만 하라고 그러죠?

한번 해봅시다. 



protocol SomeProtocol {

    func someMethod()

}

이렇게만 하면 돼요!!

중괄호도 없고, 메소드 본문도 작성 안해도 되고!! 오직 "정의"만 프로토콜에서 해주면 됩니다.

나를 "채택"하는 클래스나 구조체, 열거형들은 이 메소드를 "구현"을 꼭 해줘야한답니다. 

위에서 그냥 일반 메소드와 완전히 동일하게 작성 할 수 있다고 그랬죠? 

다른 형태들도 해봅시다. 


protocol SomeProtocol {

    func someMethod()

    func anotherMethod(name: String, age:Int)->Int

    func protocolMethod()->String

}

이런것도 다~~된다는 거에요 :)




다음으로 넘어갈게요 XD


타입 프로퍼티를 프로토콜에 정의한 경우, 타입메소드 요구사항에 항상 "static"키워드를 접두사로 붙어야 합니다. 

클래스에서 구현할 때, 타입 메소드 요구사항 앞에 "class" 또는 "static"키워드가 접두사로 붙는 경우에도 마찬가지 입니다. 



이 말 잘 이해가 안가시죠!?
타입 프로퍼티를,......잘 안다고....생각하겠습니다. static이라는 키워드를 써서 타입 프로퍼티임을 나타내줬죠? 
메소드도 마찬가지에요. 
"static"이라는 키워드를 사용하여 이 메소드가 타입 메소드임을 나타낸답니다. 

protocol SomeProtocol {

    static func someTypeMethod()

    static func anotherTypeMethod()

    

}


예를 들어 이런 프로토콜이 있다고 생각해볼게요. 
그러면 이 프로토콜을 "클래스"에서 채택해보겠습니다. 


class Zedd : SomeProtocol{

    static func someTypeMethod() {

    }

    class func anotherTypeMethod() {

    }

}

Zedd라는 클래스에서 SomeProtocol을 "채택"했어요.

그리고 SomeProtocol이 요구한 메소드들을 구현해야겠죠. 

someTypeMethod()는 static이라는 키워드와 함께 구현했고, anotherTypeMethod()는 class라는 키워드로 구현했네요.


우리 < Type Properties(타입 프로퍼티) >글에서 클래스 안에서 "class"라는 키워드가 붙으면, 서브클래스에서 재정의(override)할 수 있다고 그랬죠?

저 anotherTypeMethod()는 Zedd를 상속받은 어떤 클래스에서 재정의 되겠네요!


class Zedd : SomeProtocol{

    static func someTypeMethod() {

    }

    class func anotherTypeMethod() {

    }

}

class Child : Zedd{

    override static func anotherTypeMethod(){

    }

}


이렇게요 :)

이제 조금 이해가 가시죠!?


다음으로 넘어갈게요 :)


이제 예제를 하나 볼건데 단일(single) 인스턴스 메소드를 요구하는 프로토콜을 볼거에요 .



protocol RandomNumberGenerator {

    func random() -> Double

}

자. random이라는 메소드를 "정의"했어요. 

RandomNumberGenerator라는 프로토콜은 이 RandomNumberGenerator를 "채택"하고 "준수"하는 클래스, 구조체, 열거형에 random이라는 메소드를 "요구"하며, 호출 될 때마다 Double값을 리턴합니다.

한번 RandomNumberGenerator을 채택하고 구현해볼까요?


class LinearCongruentialGenerator: RandomNumberGenerator {

    var lastRandom = 42.0

    let m = 139968.0

    let a = 3877.0

    let c = 29573.0

    func random() -> Double {

        lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))

        return lastRandom / m

    }

}

LinearCongruentialGenerator이라는 클래스가 RandomNumberGenerator를 채택했네요.

여러 저장 인스턴스 프로퍼티들을 선언했고, 기본값을 줬으니 init은 필요 없겠네요 ㅎㅎ

그리고 RandomNumberGenerator가 요구한 random()이라는 메소드도 구현해줘야겠죠?


이런식으로 프로토콜을 사용하는거랍니다 XD



Mutating Method Requirements


가끔은 인스턴스에 속한 메소드를 수정(또는 변경)할 때가 있습니다. 

예를들어 Value Type(=> 구조체 및 열거형)의 메소드의 경우 fund 키워드 앞에 "mutating"키워드를 두어 해당 메소드가 속한 인스턴스와 해당 인스턴스의 모든 프로퍼티를 수정할 수 있음을 나타냅니다.


ㅎㅎㅎ바로 이 것 때문에 어제 제가 글을 썼죠..<Method>글을 읽고오시길 바랍니다 :) mutating을 안다고 가정하고 설명할거에요!!!!XD..


프로토콜을 채택하는 모든 타입의 인스턴스를 변경하기 위한 프로토콜 인스턴스 메소드 요구사항을 정의하려면, 메소드에 mutating키워드를 프로토콜 정의의 일부로 표시합니다. 

이로 인해 구조체와 열거형에서 프로토콜을 채택하고, 해당 메소드 요구사항을 충족시킬 수 있습니다. 

protocol SomeProtocol{

    mutating func SomeMethod(_ num : Int)

}

struct SomeStruct : SomeProtocol{


    var x = 0

    mutating func SomeMethod(_ num :Int) {

        x += num

    }

}

자. 이렇게요!! 구조체와 열거형은 Value Type이기 때문에 원래는 메소드 안에서 프로퍼티의 값을 수정 못합니다.

하지만 mutating은 그것을 가능하게 해준다고 그랬죠!!

만약에 프로토콜을 채택하는 곳이 열거형과 구조체인데, 프로퍼티 변경이 필요하다!!! 그러면

프로토콜측에서 애초에 mutating으로 메소드를 만들기로 규약을 정한거죠.

구조체나 열거형은 해당 프로토콜을 채택하면,  mutating 키워드와 함께 메소드를 작성해주면 된답니다. 




하지만, 프로토콜 인스턴스 메소드 요구사항을 mutating으로 하면, 클래스에 해당 메소드를 구현할 때, mutating키워드를 쓸 필요없습니다.

mutating키워드는 구조체와 열거형에서만 사용됩니다. 

이게 무슨소리냐..하면

애초에 "클래스"는 구조체와 열거형과 달리 Reference Type이죠? 

그러니까 메소드 내에서 프로터티들의 값을 변경하는 것이 애초부터 가능하다는 것입니다.

그러니까 만약 클래스가 위의 SomeProtocol을 채택하더라도, SomeProtocol이 mutating으로 메소드를 만들었어도!!!!

class는 "mutating"이라는 키워드가 없어도 됩니다. 

protocol SomeProtocol{

    mutating func SomeMethod(_ num : Int)

}

class SomeClass : SomeProtocol{

    var x = 0

    func SomeMethod(_ num: Int) {

        x += num

    }

}

이렇게요 :)
애플에는 열거형 예제가 나와있네요. 근데 넘 쉽다능 
열거형도 구조체와 같은 값타입이기 때문에, mutating을 통한 프로퍼티 값 변경이 가능하다~~이소리에요.
<>글에서 나온 예제랑 똑같으니 넘어가겠습니댜


Initializer Requirements


요구사항이 참 많네요 :) 프로퍼티 요구사항, 메소드 요구사항에 이어서 이니셜라이저 요구사항입니다 :-)
프로토토콜은 프로토콜을 준수하려는 타입(conforming type)에게 특정한 이니셜라이저를 구현하도록 요구할 수 있습니다.
프로톨에서, 이러한 이니셜라이저는 일반 이니셜라이저와 똑같이 작성하지만 중괄호나 본문은 작성하지 않습니다.

protocol SomeProtocol {

    init(someParameter: Int)

}

이렇게만 쓰면 이니셜라이저 요구사항 끝~


Class Implementations of Protocol Initializer Requirements


해당 프로토콜을 준수하는 클래스에서 프로토콜에서 요구하는 이니셜라이저 요구사항을 구현할 수 있습니다.

designated init 또는 convenience init으로말이죠.

두 경우 모두 "required"라는 modifier를 표시해야합니다. 


protocol SomeProtocol {

    init(someParameter: Int)

}

class SomeClass: SomeProtocol {

    required init(someParameter: Int) {

        // initializer implementation goes here

    }

}

위 경우는 "클래스"라서 그렇구요 :)

구조체인 경우는 "required"가 필요없답니다. 



protocol SomeProtocol {

    init(someParameter: Int)

}

struct SomeStruct  : SomeProtocol{

    init(someParameter: Int) {

    }

}

아무튼 왜 "클래스"에서는 required를 init앞에 왜 써줘야 하느냐!!

required를 사용하면 해당 프로토콜을 준수하는 클래스의 모든 하위클래스들 역시 이니셜라이저 요구사항을 구현함을 보장(ensure)받을 수 있습니다. 



만약 클래스가 final 수정자로 표시되어있다면, 프로토콜 이니셜라이저 구현을 "required" 수정자를 표시할 필요 없습니다. 

왜냐하면 final클래스는 서브클래스화 할 수 없기때문입니다. 


final을 아직 모르시는 분들도 계실거에요 :) 글 쓸 주제가 점점 늘어나네요 XD..

final을 간단히 설명드리자면, 재정의(override)를 막아주게 해주는 아이?에요.

이 final은 메소드, 프로퍼티, 클래스 등등에 붙을 수 있답니다.

final var ~, final func ~, final class func~~

이렇게 앞에 final이라고 표시해주면 됩니다.

클래스 전체를 final로 하고싶으면 final class~ 겠죠.

이 final클래스는 override가 안되는 거니, 서브클래스화 할 수 없습니다. final이라고 붙은 클래스는 상속할 수 없다는 거죠!!


final이 살짝 이해가 가시나요?ㅎㅎ


자, 그럼 다음으로 넘어가봅시댜


만약 서브클래스가 슈퍼클래스의 designated 이니셜라이저를 재정의(override)하고, 또한 프로토콜에서 요구하는 이니셜라이저와 일치하는 경우에는 이니셜라이저를 required override로 표시하세요.




protocol SomeProtocol {

    init()

}


class SomeSuperClass {

    init() {

        // initializer implementation goes here

    }

}


class SomeSubClass: SomeSuperClass, SomeProtocol {

    // "required" from SomeProtocol conformance; "override" from SomeSuperClass

    required override init() {

        // initializer implementation goes here

    }

}

이렇게말이죠. 



Failable Initializer Requirements

프토토콜은 해당 프로토콜을 준수하는 타입들에게 failable 이니셜라이저를 정의하도록 요구할 수 있습니다. 

failable 이니셜라이저 요구사항은  해당 프로토콜을 준수하는 타입에서 failable 또는 nonfailable이니셜러이저로 만족시킬 수 있습니다. 

 non failable이니셜라이저 요구사항은  nonfailable이니셜라이저 또는 암시적(implicitly)으로 언래핑된 failable 이니셜라이저로 만족시킬 수 있습니다. 





이거 참 이니셜라이저 글을 얼른 써야겠네요 :) Failable 이건 이니셜라이저 글에서 꼭!! 설명하도록 할게요 XD..

오늘은 여기까지!! 오늘도 도움이 되었길 바라며!! 다음글에서 만나요 :)

반응형

'Swift' 카테고리의 다른 글

Swift ) Patterns  (0) 2017.10.31
Swift ) Type Casting  (2) 2017.10.23
Swift ) Method  (7) 2017.10.17
Swift ) Protocols (1)  (4) 2017.10.16
Swift ) Properties - Type Properties  (4) 2017.10.13