티스토리 뷰

반응형


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

Swift 5.0 변경사항들을 썼는데..

이 글은 zzzz 보니까 1월 6일부터 쓰기 시작했는데..중간에 멈췄네요.


Flatten nested optionals resulting from 'try?'는 Swift 5.0 변경 사항 중 하나인데요, SE를 읽고 공부해봤어요. 


Flatten nested optionals resulting from 'try?'



Swift의 try? 구문은 중첩된(nested) Optional을 쉽게 만듭니다. 중첩된 Optional은 사용자가 추론하기 어려우며 일반적인 경우, 그것(중첩된 Optional)을 만드는 것을 피하려고 합니다.



이게 뭔소리여....하겠지만 예제를 보면 바로 이해가 가실겁니다.


try?를 사용 할 때, 중첩된 Optional타입으로 끝나는것은 현재 매우 쉽습니다. 중첩된 Optional을 구성하는 것이 유효하기는 하나, 대부분의 경우 개발자가 의도한 것이 아닙니다.

Swift에는 실수로 중첩된 Optional을 만드는 것을 방지하는 다양한 매커니즘이 있습니다.


// Note how 'as?' produces the same type regardless of whether the value

// being cast is optional or not.

let x = nonOptionalValue() as? MyType    // x is of type 'MyType?'

let y = optionalValue() as? MyType       // y is of type 'MyType?'


// Note how optional chaining produces the same type whether or not the

// call produces an optional value.

let a = optThing?.pizza()             // a is of type 'Pizza?'

let b = optThing?.optionalPizza()     // b is of type 'Pizza?'


자, 여기까진 당연하죠?? 익숙하죠?

근데 여기에 try?를 사용하면?


let q = try? harbor.boat()           // q is of type 'Boat?'

let r = try? harbor.optionalBoat()   // r is of type 'Boat??'



자...마지막 줄이 중첩된 Optional이라고 할 수 있죠. Boat??타입이 리턴되니까요.

왜냐? try?에서 한번 감싸고 optionalBoat니까 ㅠ



// The result of 'foo?.makeBar()' is 'Bar?' because of the optional

// chaining on 'foo'. The 'try?' adds an additional layer of

// optionality. So the type of 'x' is 'Bar??'

let x = try? foo?.makeBar()



그럼 이건?? x는 Bar??타입이 되겠죠? 왜냐? foo가 옵셔널이고 try?로 또 옵셔널 처리 해주니까 ㅇㅇ


자 그럼 우리에게 익숙한 상황.



// JSONSerialization.jsonObject(with:) returns an 'Any'. We use 'as?' to

// verify that the result is of the expected type, but the result is that 'dict'

// is now of type '[String: Any]??' because 'try?' added an additional layer.

let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any]



흠...지금은 잘 사용 안하는 코드...라고 믿고싶은데요...여러분 Codable씁시다.

암튼 dict의 타입은 [String: Any]??가 됩니다. 

역시나 중첩된 Optional이죠.

하나도 빡치는데 두개?

ㅎㅎ..

자..네...물론 중첩된 Optional 쓰시는 분들도 있겠죠!!! 

근데 근데 대부분의 경우에 거의 원하는 결과가 아니잖아요?

두번 풀어줘야 하자너........ㅂㄷㅂㄷ

암튼 이걸 풀어봅시다..




// Pattern 1: Double if-let or guard-let

if  let optionalX = try? self.optionalThing(),

    let x = optionalX {

    // Use 'x' here

}

// Pattern 2: Introducing parentheses to let 'as?' flatten for us

if let x = (try? somethingAsAny()) as? JournalEntry {

    // use 'x' here

}


// Pattern 3: Pattern matching

if case let x?? = try? optionalThing() {

    // use 'x' here

}


으악 내눈

코드리뷰하면 보는 사람이 100%..응??하게 만드는 코드들

if case let x?? = try? optionalThing() 는 최강이라고 생각..




이 중첩된 Optional은 사용하기가 더 어려워질 뿐만아니라,

어떠한 이점도 주지 못합니다.(they don't really give us any benefit in return.)

try?를 사용하는 것은 에러케이스와 nil-result케이스 간에 어떠한 차이점도 두지 않으므로 위 3가지 패턴은 값추출에만 중점을 두지, 오류는 무시합니다.

개발자가 특별히 오류를 감지해야 하는 경우에는 do/try/catch를 사용해야합니다.


Proposed solution

Swift 5에서, try? someExpr()는 foo?.someExpr(:의 동작을 미러링 합니다. 


1. 만약 someExpr()이 옵셔널이 아닌 값을 생성한다면, try? someExpr은 옵셔널로 감싸집니다.

2. 만약 someExpr()이 옵셔널인 값을 생성한다면, 추가적인 optional추가는 없습니다. 


즉!!!!! Swift 5에서는 1번이나 2번이나 똑같은 결과를 생성하게됩니다.

Swift 5이전에는 2번이 중첩된 Optional타입이 되게 되었지만......Swift 5에서부터는 아닙니다.



// Swift 4: 'Int??'

// Swift 5: 'Int?'

let result = try? database?.countOfRows(matching: predicate)


// Swift 4: 'String??'

// Swift 5: 'String?'

let myString = try? String(data: someData, encoding: .utf8)



// Swift 4: '[String: Any]??'

// Swift 5: '[String: Any]?'

let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any]




sub-expression이 non-optional을 생성할 때는 당연히!! 전체 타입이 변경되지 않습니다. 


// Swift 4: 'String?'

// Swift 5: 'String?'

let fileContents = try? String(contentsOf: someURL)






하지만 만약 sub-expression이 이미 중첩된 Optional을 생성하면 결과도 똑같이 중첩됩니다. 


func doubleOptionalInt() throws -> Int?? {

    return 3

}

// Swift 4: 'Int???'

// Swift 5: 'Int??'

let x = try? doubleOptionalInt()



위 코드에서, doubleOptionalInt의 리턴값이 이미!!! 이미 Int??로 중첩된 Optional이죠?? 이미 Int??인데 어쩔것임..하지만 Swift 4에서는 try? 때문에 ???였다면 Swift 5에서는 ??로만 리턴.


Source compatibility

위에서 보셨다시피, 이것은 try?에 대한 근원적인 변화(source-breaking change)입니다. 

optional자체를 명시적으로 flatten하지 않는 경우, sub-expression에서 작동하는 표현식이죠.

하지만 ABI-breaking change는 아닙니다. 


앜ㅋㅋ벌써 Swift 5.1 브랜치가 있네요.



5.1에는 과연 어떤것들이 변할지?




출처 : https://github.com/apple/swift-evolution/blob/master/proposals/0230-flatten-optional-try.md

반응형

'Swift' 카테고리의 다른 글

Swift 5.0 Released!  (2) 2019.03.26
Swift로 PS할 때 유용한 메소드들  (0) 2019.03.02
Swift 5.0 변경사항  (8) 2019.02.17
Swift Snapshot써보기  (1) 2019.01.07
Swift 5.0 Release Process  (3) 2019.01.04