티스토리 뷰

Swift

Swift 4.1 Released! -2

Zedd0202 2018. 4. 14. 14:16
반응형

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

Swift 4.1에서 달라진 점이


이만큼이 있었죠?
하나하나 보도록 할게요!
글을 읽으시기전에 <Swift 4.1 Released! -1>을 읽고오시는 걸..추천..
Post는 <What’s new in Swift 4.1>글 참고할거에요 :)


 Conditional conformances[SE-0143]

Conditional conformances특정 조건이 충족 될 때만 타입이 프로토콜을 준수 할 수 있습니다.


protocol Purchaseable {

    func buy()

}


자, Purchaseable라는 프로토콜을 하나 만들고, buy라는 메소드를 하나 요구합니다.

이제 Purchaseable를 채택하는 타입들은 모두 buy라는 메소드를 반드시 구현해야만 합니다.


struct Book: Purchaseable {

    func buy() {

        print("You bought a book")

    }

}


이렇게요.


자, 이까지는 프로토콜 글에서 다 배운거죠?

여기서 한걸음 더 나아가 보겠습니다..

만약 사용자가 장바구니에 책을 가ㅏ~~~득 담고 그걸 모두 사고싶으면 어떻게 해야할까요? 위 코드에서?

음...책을 배열안에 넣어서, loop를 돌면서 buy라는 메소드를 호출해야겠네요.


하지만 더 나은 접근법은,

Array에 Extension을 작성하여 Purchaseable프로토콜을 준수하게 한 다음 각 요소에 대해 buy() 메서드를 호출하는 것입니다.


extension Array: Purchaseable where Element: Purchaseable {

    func buy() {

        for item in self {

            item.buy()

        }

    }

}


바로 이렇게 말이죠.


ㅇ엥 이게 원래ㅑ 안됐나?


 


네..4.0에서는 Extension of type 'Array' with constraints cannot have an inheritance clause라는 에러가 나는 모습을 볼 수 있습니다.

4.1에서는..! element(=요소)가 Purchaseable를 준수하는 경우에만!!!!!!!!!요소가!!!!!!요소가 Purchaseable를 “준수하는 경우에만”!!!!!!!!!!! 이 Array라는 구조체를 Purchaseable를 준수하도록 만들 수 있는 것입니다!!!!!!!!!!!!!!!!!!!!!!!


Conditional conformances : 특정 조건이 충족 될 때만 타입이 프로토콜을 준수 할 수 있습니다.

이게 무슨소리인지 이제 아시겠나요?


우리는 조건을 요소가 Purchaseable를 준수하는걸로 줬죠. 이 조건이 만족된다면, Array라는 타입은 Purchaseable를 준수하게 됩니다.

제약조건으로 Purchaseable를 줬다고 무조건 Purchaseable를 준수하게 되는 것이 아니라, 요소가 Purchaseable를 준수할때만, 그제서야 Array는 Purchaseable를 준수하게 되는것이죠.


이 Conditional conformances은 Swift코드의 많은 부분을 더 쉽고 안전하게 만드는데요,



var left: [String?] = ["Andrew", "Lizzie", "Sophie"]

var right: [String?] = ["Charlotte", "Paul", "John"]

left == right



위 코드가 Swift4.0에서는 어떤 결과를 가져올 것 같으신가요!?




네..! 





컴파일이 안되는 것을 볼 수 있습니다.

왜냐?

우리 < Equatable >글에서 Equatable을 배웠었죠? 저렇게 같냐 아니냐를 비교할 수 있으려면, Equatable 프로토콜을 준수하고 있어야 합니다.

하지만..

String과 [String]은 Equatable을 준수하지만 [String?]은 아니였죠.


Swift4.1에서, Conditional conformances의 도입은 이제 조건을 만족하면 프로토콜 준수를 타입에 줄 수 있는 거였죠? 이 경우 배열의 요소가 모두 equatable하기 때문에 Swift 4.1에서는 컴파일이 되게 됩니다.

물론 false가 나오겠죠?


 Conditional conformances의 도입으로 인해, Codable프로토콜을 사용시 더 안전하게 작업 할 수 있게 되었습니다..!


struct Person {

    var name = "Taylor"

}


var people = [Person()]

var encoder = JSONEncoder()

//try encoder.encode(people)


자.. 위 코드를 봅시다. people은 Person이라는 구조체의 인스턴스 배열이네요.

근데, people을 마지막 줄에서 encode하려고 합니다.


하지만..!!!!!!!!!!!!! JSONEncoder는... Encodable한 값만을 encode할 수 있습니다.

즉, Person이라는 구조체가 Encodable또는 Codable(Codable안에 Encodable이 있으니까)을 준수하지 않기 때문에 에러가 나게 됩니다..!!


Swift 4.0에서는 이 에러가...언제 나냐면...바로 “런타임”때 나게 됩니다. 


일단 실행 하고 ㅇㅇ 이제 encode하려고 보니까 people이 Encodable프로토콜을 준수하지 않는거죠. 그러면 @_@;;하고 에러를 내게 됩니다. 

하지만...!!!!!!!!!!!!!


Swift 4.1에서는 Conditional conformance을 사용하여 이를 방지합니다. 

Optional, Array, Dictionary, Set은 이제 얘네의 모든 요소가 Codable을 준수하면, Optional, Array, Dictionary, Set은 Codable을 준수하게 됩니다.


그러므로, Swift 4.1에서는 위 코드가 “컴파일 타임”에 에러를 내게 됩니다..!



이렇게요 :)


● Support Recursive constraints on associated types[SE-0157]

음..뭐라는거지.. associated types에 재귀 제약 지원...?......
associated types은 <associated types>글에서 정리했으니 참고하시길 바랍니다 :)
사실 associated types모르면 이해안감 ㅎ

프로토콜에서 associated types을 사용하는 방식에 대한 제한을 해제합니다. 
결과적으로 우리는 associated types에 대한 재귀적 제약 조건을 만들 수 있습니다. 즉, 정의 된 프로토콜에 의해 제약되는 associated types입니다.

자...ㅎ 무슨소리인지 글만보면 1도 모르겠네요.  <associated types>글에서 예제를 가져 와봤습니다. 

  1. protocol ZeddProtocol{

        associatedtype MyType: Equatable

        var name: MyType { get }

    }



저의 associated types에 이렇게 “제약조건”을 추가할 수 있었습니다.


하지만......associated types에 내 자신(ZeddProtocol)을 제약조건으로 추가 하게되면



이렇게 하면...에러.....왜냐면, ZeddProtocol을 자체적으로 사용하기 때문이죠.

하지만..!!!!!!!! Swift 4.1에서는 가능해짐 ㅎ

예제를 봅시다. 



Employee라는 프로토콜을 하나 만들어볼건데요, 모든 Employee에게는 Manager가 있으며 각 Manager는 이 회사의 Employee이어야 합니다.


  1. protocol Employee {

        associatedtype Manager: Employee

        var manager: Manager? { get set }

    }


(왜 manager라는 프로퍼티 요구사항은 Optional이냐? CEO는 manager가 필요없어서..)

자 아무튼 위 코드는 Swift 4.0에서는 오류가 나겠죠. 

왜냐? 지금 Employee안에서 자체적으로 associated types에 Employee프로토콜을 준수하도록 제약조건을 걸어놨기 때문이죠.

하지만! 4.1에서는 가능합니다. 


protocol Employee {

    associatedtype Manager: Employee

    var manager: Manager? { get set }

}


class BoardMember: Employee {

    var manager: BoardMember?

}




이것이 가능해지는 것이죠. 


protocol ZeddProtocol{


    associatedtype MyType: ZeddProtocol


    var name: MyType { get }

}//OK




● Synthesized Equatable and Hashable[SE-0185]

Equatable과 Hashable의 합성..?

..일단 Hashable을 정확히 공부하고 오겠습니다.


- <Hashable>읽으러가기


네..! Hashable진심 별거 없었음ㅎ

그럼 계속할게요.


자..여러분 < Equatable >글에서 Equatable에 대해서 배웠습니다.
글 중에서, 해당 인스턴스에 끼리 비교를 가능하게 하려면 ==을 정의해줘야 했습니다.

class A : Equatable{

    var aNum : Int

    init(_ aNum :Int) {

        self.aNum = aNum

    }

    public static func ==(lhs: A, rhs: A) -> Bool{

        return lhs.aNum == rhs.aNum

    }

}


바로 이렇게요. 그리고 ==안에서 현재 타입의 프로퍼티가 같은지 비교해줘야 했습니다.

지금은 aNum이라는 프로퍼티밖에 없지만..


  1. struct Person {

        var firstName: String

        var lastName: String

        var age: Int

        var city: String

    }



만약 내 타입이 이만큼 프로퍼티를 가지고 있었다고 생각해볼게요.

그리고 이제 내 타입, 즉 Person도 "비교"를 하고싶어졌어요. 어떤 Person 인스턴스랑 다른 Person인스턴스가 같은지 다른지 비교를 하고싶어진거죠.

그러면....



  1. struct Person: Equatable {

        var firstName: String

        var lastName: String

        var age: Int

        var city: String

        

        static func ==(lhs: Person, rhs: Person) -> Bool {

            return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && lhs.age == rhs.age && lhs.city == rhs.city

        }

    }


먼저, Person이 Equatable을 채택하도록 하고, 이제 ==을 정의해줘야겠죠...하지만.....

어떤 Person 인스턴스와 또 다른 Person인스턴스가 "같다"라는 의미는

Person타입 안에 있는 모든 프로퍼티의 "값이 같다"라는 의미죠?


그래서...저렇게... &&를 통해...전부...같으면.....같구나...true...하나라도 다르면 false를 return하도록 했습니다.



솔직히 코드가 상당히 예쁘지 않달까??그렇지 않나요?

아니...그냥 나는 얘랑 얘랑 같지 않은지만..비교하고싶은데..저렇게 더러운 코드를 꼭 짜야만 하니깐...ㅎ



네 눈치를 채셨을진 모르지만...Swift 4.1에서는.....!!!!! 위와같은 더러운 코드를 작성하지 않아도 됩니다..!!!! XD


==> 니가 Equatable채택하잖아? 그럼 내가 ==메소드를 자동으로 생성해줄게 ㅇㅇ입니다.

이제 Equatable을 유형에 대한 프로토콜로 추가하면 Swift가 나머지 작업을 수행합니다.



  1. var zedd = Person(firstName: "zizon", lastName: "zedd", age: 28, city: "Russia")

    var alan = Person(firstName: "alan", lastName: "walker", age: 20, city: "england")


    zedd == alan//false




하지만...

아ㅎ 나는 어떤 타입의 인스턴스가 같은지 안같은지 비교 할 때, 그 타입의 프로퍼티 말고, 어떤 특정한 프로퍼티로만 비교하고싶어 ㅇㅇㅇ;; 만약 이름이 다르더라도 나는 어떤 ID가 같으면 같은거로 보고싶은데...



==> 이러면 직접 ==를 구현하시면 됩니다!!!

그러면 자동으로 생성된 ==를 사용하지 않고, 우리가 만든 ==를 사용하게 됩니다 :) 짱이죠??


  1. struct Person: Equatable {


        var firstName: String

        var lastName: String

        var age: Int

        var city: String

        static func ==(lhs: Person, rhs: Person) -> Bool {

            return lhs.age == rhs.age

        }

    }


    var zedd = Person(firstName: "zizon", lastName: "zedd", age: 28, city: "Russia")

    var alan = Person(firstName: "alan", lastName: "walker", age: 28, city: "england")


    zedd == alan//true



그리고 Swift 4.1에서는 Hashable 프로토콜에 대한 합성(synthesized) 지원을 도입했습니다. 즉, 자동으로 타입을 준수하기위한 hashValue 프로퍼티를 생성합니다.

Hashable은 모든 객체에 고유 한 (또는 적어도 대부분 고유 한) 해시를 반환해야하기 때문에 구현하기가 항상 짜증났습니다. 

  1. var hashValue: Int {

            return firstName.hashValue ^ lastName.hashValue &* 16777619

        }


Equatable의 경우와 마찬가지로 필요한 특정 항목이 있으면 자신의 메서드를 작성할 수도 있지만 대부분 Swift 4.1에서는 더 이상 필요하지 않습니다.

참고 : 유형에 대한 적합성을 추가하여 이러한 프로토콜을 선택해야하며 합성 된 코드를 사용하려면 해당 유형의 모든 속성이 각각 Equatable 또는 Hashable을 준수해야합니다.


 Introduce Sequence.compactMap(_:)[SE-0187]


이거는 <flatMap -> compactMap>글에 썼으니까 넘어갈게용



● Make Standard Library Index Types Hashable[SE-0188]


이 변경사항은...Key Path에 관한것 같은데요. KeyPath를 잘 안써서.......

Key Path 표현식에는 이제 subscripts가  Hashable 인 경우에만 모음 및 기타 하위 식 타입의 개별 위치를 참조하는 subscripts가 포함될 수 있습니다. 최대한의 유틸리티를 제공하기 위해 표준 라이브러리 인덱스 유형에는 모두 Hashable 준수가 추가되어야합니다.


이게 뭔소린지 보니까..


let numbers = [10, 20, 30, 40, 50]

let firstValue = \[Int].[0]

print(numbers[keyPath: firstValue])     // 10


let string = "Helloooo!"

let firstChar = \String.[string.startIndex]

// error: subscript index of type 'String.Index' in a key path must be Hashable



원래 Swift 4.1전에는 이렇게 error가 났다고 해요. 왜냐면, 



  • Int (already Hashable)
  • Dictionary.Index
  • Set.Index
  • String.Index


이런 인덱스들이 Hashable을 준수 안하고 있어서...(Int는 이미 준수하고 있었지만)

하지만 Swift 4.1에서는 기본적으로? 자동으로 준수를 해놓았으니, 위 코드는 Swift 4.1에서는 잘 돌아간다~~ 이거죠.




● Eliminate IndexDistance from Collection[SE-0191]


Collection에서 associated type인 IndexDistance를 제거하고 대신 Int로 수정했습니다.

여기에서 Diff를 보니까




Collection에서 Int타입이 아닌 IndexDistance타입이어서 이런 귀찮은 캐스팅 작업을 많이 해줘야 했었나봐요.

하지만..! 이제는 Int로 고쳐졌다는 이말..!!

자세한 내용은 <SE-0191 Eliminate IndexDistance from Collection>를 참고해주세요. 




자..!!!! 드디어..!!!! 드디어 다 정리했네요. 이 글만 한 일주일 붙잡고 있은듯

아무튼 Swift 4.1.. 잘 사용하시길 바랍니다 :)

반응형

'Swift' 카테고리의 다른 글

Swift ) Class only Protocol. class? AnyObject?  (4) 2018.04.18
Swift ) FloatingPoint  (0) 2018.04.14
Swift ) Hashable  (1) 2018.04.10
Swift ) Key decoding strategy  (0) 2018.04.08
Swift 4.1 Released! - 1  (0) 2018.04.07