iOS 스터디 Part3 Force-Unwrap
강제로 unwrapping을 하는 Force-Unwrap에 대해 알아보고자 한다.
iOS에서 unwrapping을 하는 3가지 방법이 있다.
Force-Unwrap
: ! 연산자Optional binding
: ?? 연산자Nil coalescing
: ?. 연산자
이번에는 강제로 unwrapping을 하는 Force-Unwrap
에 대해 알아보고자 한다.
언제 !를 사용해야할까?
Use "!" when you’re so certain that a value won’t be "nil" that you want your program to crash if it ever is.
→ nil이라면 프로그램이 충돌하기를 원할 때 쓰라고 권고하고 있다.
여기서도 이야기하는 바는 강제로 !를 쓰는 것보다 더 좋은 방법이 있다면 그 방법을 권고하고 있다. 하지만 때로는
Optional binding
,Nil coalescing
와 비교하여 하나의 문자로 의미를 표현할 수 있는 가독성 높은 연산자 역할을 하기도한다.
아래의 예시이다.
extension Sequence {
func compactMap<B>(_ transform: (Element) -> B?) -> [B] {
return lazy.map(transform).!lter { $0 != nil }.map { $0! }
}
}
$0이 nil이 아닌 것을 filter했기 때문에 당연히 $0은 nil이 아니라고 생각하고 $0!을 출력하는 간단한 예시이다.
let ages = [
"Tim": 53,"Angela":54,"Craig":44, "Jony": 47, "Chris": 37, "Michael": 34,
]
// Force-unwrap
ages.keys
.!lter { name in ages[name]! < 50 }
.sorted()
// not force-unwrap(권장)
ages
.!lter { (_, age) in age < 50 }
.map { (name, _) in name }
.sorted()
모두 키 값이 존재하기 때문에 nil이 아니라고 생각하고 !를 출력하는 예시를 보여준다.
Force-Unwrap 에러 메세지
!! 연산자를 사용한 정의 함수로 메세지를 커스텀할 수 있다.
infix operator !!
func !! <T>(wrapped: T?, failureText: @autoclosure () -> String) -> T { ifletx=wrapped{returnx}
fatalError(failureText())
}
let s = "foo"
let i = Int(s) !! "Expecting integer, got \\"\\(s)\\""
wrapped가 nil일 경우 fatalError
를 발생시켜 메세지를 출력한다.
빌드 환경에 따라 assert 선택
릴리즈 빌드에서는 크래시를 선택하는 방법보다는 유효한 default값을 지정하는 방법을 권고하고 있다.
!?연산자를 사용한 정의 함수로 unwrap 실패시 assert하고 릴리즈 빌드에서는 기본값으로 대체하도록 할 수 있다.
아래를 예시로 설명하면,
assert(wrapped != nil, failureText())
는 디버그 에서만 동작하고
릴리즈에서는 return 문이 동작하여 nil일 경우 기본값이 출력되도록 처리한다.
infix operator !?
func !?<T: ExpressibleByIntegerLiteral>
(wrapped: T?, failureText: @autoclosure () -> String) -> T
{
assert(wrapped != nil, failureText())
return wrapped ?? 0
}
lets="20"
let i = Int(s) !? "Expecting integer, got \\"\\(s)\\""
오버로딩하여 다양한 default값을 지정할 수 있다.
// 리스트
func !?<T: ExpressibleByArrayLiteral>
(wrapped: T?, failureText: @autoclosure () -> String) -> T
{
assert(wrapped != nil, failureText())
return wrapped ?? []
}
// 문자열
func !?<T: ExpressibleByStringLiteral>
(wrapped: T?, failureText: @autoclosure () -> String) -> T
{
assert(wrapped != nil, failureText())
return wrapped ?? ""
}
// 기본값과 오류 텍스트를 같이 사용하고 싶은 경우
func !?<T>(wrapped: T?,
nilDefault: @autoclosure () -> (value: T, text: String)) -> T
{
assert(wrapped != nil, nilDefault().text)
return wrapped ?? nilDefault().value
}
Int(s) !? (5, "Expected integer")
// void를 반환하는 메서드의 경우
func !?(wrapped: ()?, failureText: @autoclosure () -> String)
{
assert(wrapped != nil, failureText())
}
var output: String? = nil
output?.write("something") !? "Wasn't expecting chained nil here"
참조
Last updated