"치명적인 오류 : 선택적 값을 언 래핑하는 동안 예기치 않게 nil을 찾았습니다."는 무엇을 의미합니까?
내 Swift 프로그램이 EXC_BAD_INSTRUCTION
다음과 유사한 오류 중 하나 와 충돌 합니다. 이 오류는 무엇을 의미하며 어떻게 수정합니까?
치명적인 오류 : 선택적 값을 언 래핑하는 동안 예기치 않게 nil이 발견되었습니다.
또는
치명적인 오류 : 선택적 값을 암시 적으로 언 래핑하는 동안 예기치 않게 nil이 발견되었습니다.
이 게시물은 "예기치 않게 발견되지 않은"문제에 대한 답변을 수집하여 흩어져서 찾기 어렵지 않도록합니다. 자신의 답변을 추가하거나 기존 위키 답변을 편집 하십시오.
답변
이 답변은 커뮤니티 위키 입니다. 더 나아질 수 있다고 생각되면 자유롭게 편집하십시오 !
배경 : 선택 사항은 무엇입니까?
스위프트에서 Optional<Wrapped>
입니다 옵션 유형 : 원래 ( "포장") 유형 또는 전혀 값 (특수 값에서 모든 값을 포함 할 수 있습니다 nil
). 선택적 값은 사용하기 전에 풀어야 합니다 .
옵션은입니다 제네릭 형식 수단 것을, Optional<Int>
그리고 Optional<String>
서로 다른 종류 - 유형의 내부가 <>
랩 타입이라고합니다. 후드에서 선택 사양은이다 열거 두 건으로이 .some(Wrapped)
와 .none
어디 .none
에 해당합니다 nil
.
선택 사항은 명명 된 type Optional<T>
을 사용하거나 (가장 일반적으로) ?
접미사가 있는 속기 로 선언 될 수 있습니다 .
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int> // equivalent to `Int?`
anOptionalInt = nil // now this variable contains nil instead of an integer
선택 사항은 코드를 작성하는 동안 가정을 표현하는 간단하면서도 강력한 도구입니다. 컴파일러는이 정보를 사용하여 실수를 방지 할 수 있습니다. 에서 스위프트 프로그래밍 언어 :
Swift는 유형이 안전한 언어입니다. 즉,이 언어는 코드에서 사용할 수있는 값 유형을 명확하게하는 데 도움이됩니다. 코드의 일부에이 필요한 경우
String
형식 안전성을Int
통해 실수로 코드 를 전달할 수 없습니다 . 마찬가지로, 타입 안전성은 선택 사항String
이 아닌 코드가 필요한 코드에 실수로 선택 사항 을 전달하는 것을 방지합니다String
. 유형 안전성은 개발 프로세스에서 가능한 한 빨리 오류를 포착하고 수정하는 데 도움이됩니다.
일부 다른 프로그래밍 언어도 제네릭이 옵션 유형 : 예를 들어, 어쩌면 하스켈에서 옵션 녹, 그리고 선택 (17) C ++한다.
옵션 유형이 없는 프로그래밍 언어에서는 특정 "sentinel"값 이 유효한 값이 없음을 나타내는 데 자주 사용됩니다. 예를 들어 Objective-C에서 nil
( null 포인터 )는 객체가 없음을 나타냅니다. 와 같은 기본 유형의 int
경우 널 포인터를 사용할 수 없으므로 별도의 변수 (예 : value: Int
and isValid: Bool
) 또는 지정된 센티널 값 (예 : -1
또는 INT_MIN
)이 필요합니다. 이러한 접근 방식은 isValid
센티넬 값 을 확인 하거나 확인하는 것을 잊기 쉽기 때문에 오류가 발생하기 쉽습니다 . 또한 특정 값이 센티널로 선택되면 더 이상 유효한 값 으로 처리 할 수 없습니다 .
Swift와 같은 옵션 유형 Optional
은 특수하고 별도의 nil
값 을 도입하고 (따라서 센티넬 값을 지정할 필요가 없음) 강력한 유형 시스템을 활용하여 컴파일러가 필요할 때 nil을 확인하는 것을 기억하도록 도와 줌으로써 이러한 문제를 해결 합니다.
왜 " 치명적 오류 : 선택 값을 언 래핑하는 동안 예기치 않게 nil을 찾았습니다 "가 표시됩니까?
옵 셔널의 값에 접근하기 위해 (만일 있다면), 그것을 풀어야 합니다. 선택적 값은 안전하거나 강제로 풀릴 수 있습니다. 선택 사항을 강제로 풀고 값 이 없으면 위의 메시지와 함께 프로그램이 중단됩니다.
Xcode는 코드 줄을 강조 표시하여 충돌을 보여줍니다. 이 라인에서 문제가 발생합니다.
이 충돌은 두 가지 다른 종류의 강제 풀기에서 발생할 수 있습니다.
1. 명시 적 강제 풀기
이것은 !
선택 사항 에서 운영자 와 함께 수행됩니다 . 예를 들면 :
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
치명적인 오류 : 선택적 값을 언 래핑하는 동안 예기치 않게 nil이 발견되었습니다.
대로 anOptionalString
입니다 nil
여기에, 당신은 당신이 그것을 unwrap를 강제 라인에 충돌을 얻을 것이다.
2. 암시 적으로 언 래핑 된 옵션
이들은 유형 뒤에가 !
아니라 으로 정의됩니다 ?
.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
이러한 옵션은 값을 포함한다고 가정합니다. 따라서 암시 적으로 언 래핑 된 옵션에 액세스 할 때마다 자동으로 래핑 해제됩니다. 값이 없으면 충돌이 발생합니다.
print(optionalDouble) // <- CRASH
치명적인 오류 : 선택적 값 을 암시 적으로 언 래핑하는 동안 예기치 않게 nil이 발견되었습니다.
어떤 변수가 크래시를 일으켰 ⌥는지 알아 내기 위해, 당신은 선택적인 유형을 찾을 수있는 정의를 보여주기 위해 클릭하는 동안 누르고 있을 수 있습니다.
특히 IBOutlet은 일반적으로 암시 적으로 언 래핑 된 선택 사항입니다. 이는 xib 또는 스토리 보드가 초기화 후 런타임에 콘센트를 연결하기 때문 입니다. 따라서로드되기 전에 콘센트에 액세스하고 있지 않은지 확인해야합니다. 또한 스토리 보드 / xib 파일에서 연결이 올바른지 확인해야합니다. 그렇지 않으면 값이 nil
런타임에 있으므로 암시 적으로 풀릴 때 충돌이 발생합니다. . 연결을 수정할 때 콘센트를 정의하는 코드 줄을 삭제 한 다음 다시 연결하십시오.
언제 옵션을 강제로 풀어야합니까?
명시 적 강제 풀기
일반적으로 !
연산자를 사용 하여 옵션을 명시 적으로 강제 풀지 않아야합니다 . 사용 !
이 허용되는 경우가있을 수 있지만 옵션에 값이 포함되어 있다고 100 % 확신하는 경우에만 사용해야합니다.
선택 사항에 값이 포함되어 있다는 사실 을 알고 있듯이 강제 해제를 사용할 수있는 경우 가있을 수 있지만 대신 해당 선택 사항을 안전하게 풀 수없는 단일 위치 는 없습니다 .
암시 적으로 언 래핑 된 옵션
이러한 변수는 나중에 코드에서 할당을 연기 할 수 있도록 설계되었습니다. 액세스하기 전에 가치가 있는지 확인하는 것은 귀하의 책임입니다. 그러나 강제 언 래핑을 포함하기 때문에 nil 할당이 유효하더라도 값이 nil이 아니라고 가정 하기 때문에 본질적으로 안전하지 않습니다.
최후의 수단 으로 암시 적으로 언 래핑 된 옵션 만 사용해야합니다 . 당신이 사용할 수있는 경우 게으른 변수를 하거나 제공하는 기본값을 변수에 대한 - 당신은 너무 대신 암시 적으로 풀어 옵션을 사용해야한다.
그러나 암시 적으로 언 래핑 된 옵션이 유익한 몇 가지 시나리오가 있으며 아래 나열된대로 안전하게 언 래핑하는 다양한 방법을 사용할 수 있지만 항상 신중하게 사용해야합니다.
선택 사항을 안전하게 처리하려면 어떻게해야합니까?
옵션에 값이 포함되어 있는지 확인하는 가장 간단한 방법은 값을와 비교하는 것 nil
입니다.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
그러나 옵션으로 작업 할 때 99.9 %의 경우 실제로 포함 된 값에 액세스하고 싶을 것입니다. 이를 위해 Optional Binding을 사용할 수 있습니다 .
선택적 바인딩
선택적 바인딩을 사용하면 옵션에 값이 포함되어 있는지 확인할 수 있으며 래핑되지 않은 값을 새 변수 또는 상수에 할당 할 수 있습니다. 이 구문을 사용 if let x = anOptional {...}
하거나 if var x = anOptional {...}
당신이 그것을 결합 후 새 변수의 값을 수정해야하는 경우, 따라.
예를 들면 :
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
이것이하는 일은 먼저 옵션에 값이 포함되어 있는지 확인하는 것입니다. 이 경우 수행 한 후 '미개봉'값은 새 변수 (에 할당 number
이 아닌 선택 사항 인 것처럼 다음 자유롭게 사용할 수 있습니다 -). 선택 사항 에 값이 포함되어 있지 않으면 예상 한대로 else 절이 호출됩니다.
선택적 바인딩의 장점은 여러 옵션을 동시에 풀 수 있다는 것입니다. 쉼표로 문을 구분할 수 있습니다. 모든 옵션이 풀리면 명령문이 성공합니다.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
또 다른 깔끔한 트릭은 쉼표를 사용하여 값을 풀고 나서 값에 대한 특정 조건을 확인할 수도 있다는 것입니다.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
if 문 내에서 선택적 바인딩을 사용하는 유일한 문제는 문 범위 내에서만 래핑되지 않은 값에 액세스 할 수 있다는 것입니다. 문 범위 밖에서 값에 액세스해야하는 경우에는 guard 문을 사용할 수 있습니다 .
가드 문은 당신이 성공을위한 조건을 정의 할 수 있습니다 - 그 조건이 충족되는 경우 현재 범위는 계속 실행됩니다. 구문으로 정의됩니다 guard condition else {...}
.
따라서 선택적 바인딩과 함께 사용하려면 다음을 수행하십시오.
guard let number = anOptionalInt else {
return
}
(가드 본문 내 에서 현재 실행중인 코드의 범위를 종료하려면 제어 전송 문 중 하나 를 사용해야합니다 .)
anOptionalInt
값 이 포함 된 경우 래핑 해제되고 새 number
상수에 할당됩니다 . 가드 이후 의 코드 는 계속 실행됩니다. 값이 포함되어 있지 않으면 가드가 대괄호 안에있는 코드를 실행하여 제어권이 이전되어 바로 다음 코드가 실행되지 않습니다.
가드 문에 대한 진짜 깔끔한 점은 언 래핑 된 값이 이제 문 다음에 나오는 코드에서 사용할 수 있다는 것입니다 ( 선택 사항에 값이있는 경우 에만 향후 코드를 실행할 수 있음을 알고 있기 때문입니다 ). 여러 if 문을 중첩하여 생성 된 '파멸의 피라미드' 를 제거하는 데 유용 합니다.
예를 들면 :
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Guards는 동시에 여러 선택 사항을 풀고 where
절을 사용하는 것과 같이 if 문이 지원하는 것과 동일한 깔끔한 트릭을 지원합니다 .
if 또는 guard 문을 완전히 사용하는지 여부는 향후 코드 가 선택 사항에 값을 포함 해야하는지 여부에 따라 다릅니다 .
Nil Coalescing 연산자
무기 호 합체 운영자 의 멋진 속기 버전입니다 삼항 조건 연산자 주로 비 선택적 항목에 선택적 항목을 변환하도록 설계되었습니다. 구문이 있습니다 a ?? b
. 여기서는 a
선택적 유형이고 (일반적으로 선택적이 아니지만) b
동일한 유형 a
입니다.
기본적으로 " a
값 이 포함되어 있으면 풀기. 그렇지 않으면 b
대신 반환 ”. 예를 들어 다음과 같이 사용할 수 있습니다.
let number = anOptionalInt ?? 0
이것은 값을 포함하는 경우 값을 포함하거나 그렇지 않은 경우 값을 포함하는 유형 의 number
상수를 정의합니다 .Int
anOptionalInt
0
다음의 약어입니다.
let number = anOptionalInt != nil ? anOptionalInt! : 0
선택적 체인
선택적 체인 을 사용 하여 메서드를 호출하거나 선택적 속성에 액세스 할 수 있습니다. 이것은 변수 이름을 사용할 ?
때 접미사로 간단히 수행됩니다 .
예를 들어 foo
선택적 Foo
인스턴스 유형의 변수가 있다고 가정 합니다.
var foo : Foo?
foo
아무것도 반환하지 않는 메서드를 호출하려면 다음 과 같이하면됩니다.
foo?.doSomethingInteresting()
경우 foo
값을 포함,이 방법은 호출됩니다. 그렇지 않으면 나쁜 일이 일어나지 않습니다. 코드는 단순히 계속 실행됩니다.
(이것은 nil
Objective-C에서 메시지를 보내는 것과 유사한 동작 )
따라서 속성을 설정하고 메서드를 호출하는데도 사용할 수 있습니다. 예를 들면 :
foo?.bar = Bar()
경우 다시 말하지만, 아무것도 나쁜 여기에 일어날 것 foo
입니다 nil
. 코드는 단순히 계속 실행됩니다.
선택적 체이닝으로 할 수있는 또 다른 트릭은 속성 설정 또는 메서드 호출이 성공했는지 확인하는 것입니다. 반환 값을와 비교하여이를 수행 할 수 있습니다 nil
.
( 아무것도 반환하지 않는 메서드 Void?
보다는 선택적 값이 반환 되기 때문 Void
입니다.)
예를 들면 :
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
그러나 속성에 액세스하거나 값을 반환하는 메서드를 호출하려고 할 때 상황이 조금 더 까다로워집니다. foo
은 선택 사항 이므로 반환되는 모든 항목도 선택 사항입니다. 이를 처리하기 위해 위의 메서드 중 하나를 사용하여 반환 된 선택 사항을 풀 foo
거나 메서드에 액세스하거나 값을 반환하는 메서드를 호출하기 전에 자체적으로 풀을 수 있습니다.
또한 이름에서 알 수 있듯이 이러한 진술을 함께 '연결'할 수 있습니다. 즉 , foo
속성이있는 선택적 속성 baz
이있는 qux
경우 다음을 작성할 수 있습니다.
let optionalQux = foo?.baz?.qux
때문에 다시, foo
그리고 baz
선택 사항 값에서 반환 qux
에 관계없이 항상 여부의 선택이 될 것 qux
자체는 선택 사항입니다.
map
과 flatMap
옵션에서 자주 사용되지 않는 기능 은 map
및 flatMap
기능 을 사용하는 기능입니다. 이를 통해 선택적 변수에 선택적이 아닌 변환을 적용 할 수 있습니다. 옵션에 값이있는 경우 주어진 변환을 적용 할 수 있습니다. 값이 없으면으로 유지 nil
됩니다.
예를 들어, 선택적 문자열이 있다고 가정 해 보겠습니다.
let anOptionalString:String?
여기에 map
함수를 적용함으로써 - stringByAppendingString
다른 문자열에 연결하기 위해 함수를 사용할 수 있습니다 .
stringByAppendingString
선택적이 아닌 문자열 인수를 취하기 때문에 선택적 문자열을 직접 입력 할 수 없습니다. 그러나를 사용하면 값이있는 경우 map
허용 stringByAppendingString
을 사용할 수 있습니다 anOptionalString
.
예를 들면 :
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
그러나 anOptionalString
값이 없으면을 map
반환 nil
합니다. 예를 들면 :
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
클로저 본문 내에서 다른 옵션 map
을 반환 할 수 있다는 점을 제외하면 과 유사하게 작동합니다 . 이것은 비 선택적 입력이 필요한 프로세스에 선택 사항을 입력 할 수 있지만 선택 사항 자체를 출력 할 수 있음을 의미합니다.
try!
Swift의 오류 처리 시스템은 Do-Try-Catch 와 함께 안전하게 사용할 수 있습니다 .
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
경우 someThrowingFunc()
오류가 발생합니다, 오류가 안전하게에 잡힐 것 catch
블록.
error
당신은에서 볼 일정한 catch
블록은 우리가 선언되지 않은 -가 자동으로 생성 된 것 catch
.
error
자신 을 선언 할 수도 있습니다 . 예를 들어 다음과 같이 유용한 형식으로 캐스트 할 수 있다는 장점이 있습니다.
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
try
이 방법을 사용 하는 것이 함수를 던지면서 발생하는 오류를 시도, 포착 및 처리하는 적절한 방법입니다.
try?
오류를 흡수하는 것도 있습니다 .
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
그러나 Swift의 오류 처리 시스템은 try!
다음 과 같이 "강제 시도"하는 방법도 제공합니다 .
let result = try! someThrowingFunc()
이 게시물에서 설명하는 개념은 여기에도 적용됩니다. 오류가 발생하면 응용 프로그램이 충돌합니다.
당신은 try!
그 결과가 당신의 상황에서 절대 실패하지 않는다는 것을 증명할 수있는 경우 에만 사용해야 하며 이것은 매우 드뭅니다.
대부분의 경우 전체 Do-Try-Catch 시스템 try?
을 사용하며 드물게 오류 처리가 중요하지 않은 경우에는 선택 사항 인을 사용합니다.
자원
TL; DR 답변
로 매우 몇 가지 예외를 제외하고 ,이 규칙은 황금이다 :
사용을 피하십시오 !
?
암시 적으로 언 래핑 된 옵 셔널 (IUO)이 아닌 옵 셔널 변수 ( ) 선언 ( !
)
즉, 차라리 다음을 사용하십시오.
var nameOfDaughter: String?
대신에:
var nameOfDaughter: String!
포장을 벗긴 옵션 변수를 사용 if let
하거나guard let
다음과 같이 변수를 풀으십시오.
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: \(nameOfDaughter)")
}
또는 다음과 같이 :
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
이 답변은 전체 이해를 위해 간결하게 작성되었습니다.
자원
이 질문은 온다 모든 시간 SO에 있습니다. 이것은 새로운 Swift 개발자가 어려움을 겪는 첫 번째 일 중 하나입니다.
배경:
Swift는 값을 포함하거나 포함하지 않을 수있는 값을 처리하기 위해 "옵션"개념을 사용합니다. C와 같은 다른 언어에서는 변수에 0 값을 저장하여 값이 없음을 나타낼 수 있습니다. 그러나 0이 유효한 값이면 어떻게 될까요? 그런 다음 -1을 사용할 수 있습니다. -1이 유효한 값이면 어떻게됩니까? 등등.
Swift 옵션을 사용하면 유효한 값을 포함하거나 값이없는 모든 유형의 변수를 설정할 수 있습니다.
의미하는 변수를 선언 할 때 (유형 x 또는 값 없음) 유형 뒤에 물음표를 넣습니다.
선택 사항은 실제로 주어진 유형의 변수를 포함하거나 아무것도 포함하지 않는 컨테이너입니다.
내부 값을 가져 오려면 선택 사항을 "언 래핑"해야합니다.
"!" 연산자는 "강제 풀기"연산자입니다. "저를 믿으세요. 제가하는 일을 알고 있습니다.이 코드가 실행될 때 변수에 nil이 포함되지 않을 것임을 보증합니다." 당신이 틀렸다면 충돌합니다.
당신이 정말로하지 않는 한 않는 당신이 무엇을하고 있는지 알고는하지 않도록 "!" 강제 풀기 연산자. 그것은 아마도 초보 Swift 프로그래머에게 가장 큰 충돌 원인 일 것입니다.
옵션 처리 방법 :
더 안전한 옵션을 다루는 다른 방법이 많이 있습니다. 다음은 일부입니다 (전체 목록이 아님).
"옵션 바인딩"또는 "if let"을 사용하여 "이 옵션에 값이 포함되어 있으면 해당 값을 새로운 비 옵션 변수에 저장하십시오. 옵션에 값이 포함되지 않은 경우이 if 문의 본문을 건너 뛰십시오. ".
다음은 선택적 바인딩의 예입니다 foo
.
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
선택적 입찰을 사용할 때 정의하는 변수는 if 문의 본문에만 존재합니다 ( '범위 내'에만 있음).
또는 변수가 nil 인 경우 함수를 종료 할 수있는 guard 문을 사용할 수 있습니다.
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Guard 문이 Swift 2에 추가되었습니다. Guard를 사용하면 코드를 통해 "골든 경로"를 유지하고 "if let"선택적 바인딩을 사용하여 발생하는 중첩 된 if의 수준이 계속 증가하는 것을 방지 할 수 있습니다.
"nil 통합 연산자"라는 구조도 있습니다. "optional_var ?? replacement_val"형식을 사용합니다. 선택 사항에 포함 된 데이터와 동일한 유형의 선택 사항이 아닌 변수를 리턴합니다. 선택 사항에 nil이 포함 된 경우 "??"뒤에 표현식 값을 반환합니다. 상징.
따라서 다음과 같은 코드를 사용할 수 있습니다.
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
try / catch 또는 guard 오류 처리를 사용할 수도 있지만 일반적으로 위의 다른 기술 중 하나가 더 깔끔합니다.
편집하다:
옵 셔널을 사용하는 약간 더 미묘한 또 다른 문제는 "암묵적으로 언 래핑 된 옵 셔널입니다. foo를 선언 할 때 다음과 같이 말할 수 있습니다.
var foo: String!
이 경우 foo는 여전히 선택 사항이지만 참조하기 위해 래핑을 풀 필요가 없습니다. 즉, foo를 참조하려고 할 때마다 nil이면 충돌합니다.
그래서이 코드 :
var foo: String!
let upperFoo = foo.capitalizedString
foo를 강제 해제하지 않더라도 foo의 capitalizedString 속성에 대한 참조에서 충돌이 발생합니다. 인쇄물은 괜찮아 보이지만 그렇지 않습니다.
따라서 암시 적으로 언 래핑 된 옵 셔널에 대해 정말 조심해야합니다. (선택 사항에 대한 확실한 이해가있을 때까지 완전히 피할 수도 있습니다.)
결론 : Swift를 처음 배울 때 "!" 캐릭터는 언어의 일부가 아닙니다. 그것은 당신을 곤경에 빠뜨릴 가능성이 있습니다.
위의 답변은 옵션으로 안전하게 플레이하는 방법을 명확하게 설명하기 때문에. 옵션이 실제로 무엇인지 신속하게 설명하겠습니다.
선택적 변수를 선언하는 또 다른 방법은
var i : Optional<Int>
그리고 Optional type은 두 가지 경우가있는 열거 형입니다.
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
따라서 변수 'i'에 nil을 할당합니다. var i = Optional<Int>.none
값을 할당하거나 할 수 있습니다
. 값을 전달합니다.
var i = Optional<Int>.some(28)
Swift에 따르면 'nil'은 가치가 없음을 의미합니다. 그리고 초기화 된 인스턴스를 만들려면 nil
우리는 프로토콜을 준수해야하며, ExpressibleByNilLiteral
추측한다면 훌륭합니다 . 다른 유형을 Optionals
준수 ExpressibleByNilLiteral
하고 준수하는 것은 권장되지 않습니다.
ExpressibleByNilLiteral
init(nilLiteral:)
nil로 instace를 초기화하는 단일 메소드가 있습니다. 일반적으로이 메서드를 호출하지 않으며 신속한 문서에 따르면 nil
리터럴 로 Optional 형식을 초기화 할 때마다 컴파일러가 호출하므로이 초기화 프로그램을 직접 호출하지 않는 것이 좋습니다 .
나 자신조차도 Optionals : D Happy Swfting All 주위에 내 머리를 감싸 야 합니다.
먼저 Optional 값이 무엇인지 알아야합니다. 자세한 내용 은 The Swift Programming Language 로 이동할 수 있습니다 .
둘째, 옵션 값에 두 가지 상태가 있음을 알아야합니다. 하나는 전체 값이고 다른 하나는 nil 값입니다. 따라서 선택적 값을 구현하기 전에 어떤 상태인지 확인해야합니다.
당신은 사용할 수 있습니다 if let ...
또는 guard let ... else
등등.
다른 방법으로 구현 전에 변수 상태를 확인하지 않으려면 var buildingName = buildingName ?? "buildingName"
대신 사용할 수도 있습니다 .
다음과 같이 segue 준비 방법에서 Outlets 값을 설정하려고 할 때이 오류가 한 번 발생했습니다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// This line pops up the error
destination.nameLabel.text = item.name
}
}
}
그런 다음 컨트롤러가 아직로드되거나 초기화되지 않았기 때문에 대상 컨트롤러 콘센트의 값을 설정할 수 없다는 것을 알았습니다.
그래서 나는 이것을 이렇게 해결했습니다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// Created this method in the destination Controller to update its outlets after it's being initialized and loaded
destination.updateView(itemData: item)
}
}
}
목적지 컨트롤러 :
// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""
// Outlets
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nameLabel.text = name
}
func updateView(itemDate: ObjectModel) {
name = itemDate.name
}
이 답변이 내가 표시된 답변이 옵션 및 작동 방식을 이해하는 데 훌륭한 리소스이지만 문제 자체를 직접 해결하지는 않은 것을 알았던 것과 동일한 문제가있는 모든 사람에게 도움이되기를 바랍니다.
기본적으로 Swift가 nil이 아닌 값만 허용하는 곳에서 nil 값을 사용하려고했습니다. 컴파일러에게 nil 값이 절대 없을 것이라고 믿도록 지시하여 앱이 컴파일되도록합니다.
이러한 종류의 치명적인 오류로 이어지는 몇 가지 시나리오가 있습니다.
강제 풀기 :
let user = someVariable!
경우
someVariable
무기 호입니다, 당신은 충돌을 얻을 것이다. 강제 언랩을 수행하면 컴파일러에서 nil 검사 책임을 사용자에게 옮겼습니다. 기본적으로 강제 언랩을 수행하면 컴파일러에 nil 값이 없다는 것을 보장합니다. 그리고 어떻게 든 nil 값이로 끝나면 어떻게되는지 추측하십시오someVariable
.해결책? 선택적 바인딩 (일명 if-let)을 사용하고 여기에서 변수 처리를 수행합니다.
if user = someVariable { // do your stuff }
강제 (다운) 캐스트 :
let myRectangle = someShape as! Rectangle
여기서 강제 캐스팅을 통해 항상
Rectangle
인스턴스가 있으므로 컴파일러에게 더 이상 걱정할 필요가 없습니다 . 그리고 그것이 유지되는 한 걱정할 필요가 없습니다. 문제는 프로젝트의 동료가 사각형이 아닌 값을 순환하기 시작할 때 시작됩니다.해결책? 선택적 바인딩 (일명 if-let)을 사용하고 여기에서 변수 처리를 수행합니다.
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
암시 적으로 언 래핑 된 옵션. 다음과 같은 클래스 정의가 있다고 가정합니다.
class User { var name: String! init() { name = "(unnamed)" } func nicerName() { return "Mr/Ms " + name } }
이제 아무도
name
속성을으로 설정 하여 속성을 엉망으로 만들지 않으면nil
예상대로 작동하지만 키User
가없는 JSON에서 초기화 된 경우name
속성을 사용하려고하면 치명적인 오류가 발생합니다.해결책? 그것들을 사용하지 마십시오 :) 속성이 사용될 때까지 항상 0이 아닌 값을 가질 것이라고 102 % 확신하지 않는 한. 대부분의 경우 선택 사항 또는 비 선택 사항으로 변환하는 것이 작동합니다. 비 선택적으로 만들면 컴파일러가 놓친 코드 경로를 알려 주어 해당 속성에 값을 제공함으로써 도움을줍니다.
연결되지 않았거나 아직 연결되지 않은 콘센트. 이것은 시나리오 # 3의 특별한 경우입니다. 기본적으로 사용하려는 XIB로드 클래스가 있습니다.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
이제 XIB 편집기에서 콘센트를 연결하지 못한 경우 콘센트를 사용하려는 즉시 앱이 충돌합니다. 해결책? 모든 콘센트가 연결되어 있는지 확인하십시오. 또는
?
그들에 연산자를 사용하십시오 :emailTextField?.text = "[email protected]"
. 또는 콘센트를 선택 사항으로 선언하십시오.이 경우 컴파일러는 코드 전체에서이를 풀도록 강제합니다.Objective-C에서 가져오고 null 허용 여부 주석이없는 값입니다. 다음 Objective-C 클래스가 있다고 가정 해 보겠습니다.
@interface MyUser: NSObject @property NSString *name; @end
이제 null 허용 여부 주석이 지정되지 않은 경우 (명시 적으로 또는
NS_ASSUME_NONNULL_BEGIN
/ 를 통해NS_ASSUME_NONNULL_END
)name
속성은 Swift에서String!
(IUO-암시 적으로 래핑 해제 된 선택 사항) 로 가져 옵니다. 일부 신속한 코드가 값을 사용하려는 즉시name
nil 이면 충돌합니다 .해결책? Objective-C 코드에 null 허용 여부 주석을 추가합니다. 그러나 Objective-C 컴파일러는 null 허용 여부와 관련하여 약간 관대합니다. 명시 적으로 표시 한 경우에도 nil 값으로 끝날 수 있습니다
nonnull
.
이것은 더 중요한 주석이며, nil
값 을 디버깅 할 때 암시 적으로 언 래핑 된 옵 셔널이 기만적 일 수있는 이유 입니다.
다음 코드를 생각해보십시오. 오류 / 경고없이 컴파일됩니다.
c1.address.city = c3.address.city
그러나 런타임에 다음과 같은 오류가 발생합니다. 치명적인 오류 : 선택적 값을 언 래핑하는 동안 예기치 않게 nil을 찾았습니다.
어떤 물체인지 말해 줄 수 있습니까 nil
?
당신은 할 수 없습니다!
전체 코드는 다음과 같습니다.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c3 = BadContact()
c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct BadContact {
var address : Address!
}
struct Address {
var city : String
}
긴 이야기를 짧게 사용 var address : Address!
하면 변수가 다른 독자로부터 나올 수있는 가능성을 숨기는 것입니다 nil
. 그리고 그것이 충돌 할 때 당신은 "이게 뭐야?! 내 address
선택 사항이 아닌데 왜 내가 충돌하는거야?!.
따라서 다음과 같이 작성하는 것이 좋습니다.
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
이제 그게 어떤 물체인지 말해 줄 수 있습니까 nil
?
이번에는 코드가 더 명확 해졌습니다. address
강제로 풀린 매개 변수 일 가능성이 있다고 합리화하고 생각할 수 있습니다 .
전체 코드는 다음과 같습니다.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c2 = GoodContact()
c1.address.city = c2.address!.city
c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
if let city = c2.address?.city { // safest approach. But that's not what I'm talking about here.
c1.address.city = city
}
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct GoodContact {
var address : Address?
}
struct Address {
var city : String
}
오류 EXC_BAD_INSTRUCTION
및 fatal error: unexpectedly found nil while implicitly unwrapping an Optional value
당신이를 선언 할 때 가장 나타납니다 @IBOutlet
,하지만 연결되지 않은 스토리 보드 .
다른 답변에서 언급 된 선택 사항의 작동 방식에 대해서도 배워야 하지만 이것이 대부분 나에게 나타나는 유일한 시간입니다.
CollectionView에서이 오류가 발생하면 CustomCell 파일과 Custom xib도 만들어보십시오.
mainVC의 ViewDidLoad ()에이 코드를 추가합니다.
let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
메인 스토리 보드에서 뷰 컨트롤러에 대한 사용자 지정 클래스 이름을 지정하는 것을 잊었 기 때문에 테이블 뷰 컨트롤러에서 뷰 컨트롤러로 segue를 만드는 동안이 오류가 발생했습니다.
다른 모든 것이 괜찮아 보이는지 확인할 가치가있는 간단한 것