스위프트의 다운 캐스팅 옵션 : as? 입력 또는 as! 유형?


95

Swift에서 다음을 감안할 때 :

var optionalString: String?
let dict = NSDictionary()

다음 두 진술의 실질적인 차이점은 무엇입니까?

optionalString = dict.objectForKey("SomeKey") as? String

vs

optionalString = dict.objectForKey("SomeKey") as! String?

답변:


142

실제적인 차이점은 다음과 같습니다.

var optionalString = dict["SomeKey"] as? String

optionalString유형의 변수가됩니다 String?. 기본 유형이 String이것이 아닌 다른 유형이면 무해하게 nil선택 사항에 할당 합니다.

var optionalString = dict["SomeKey"] as! String?

이것은 내가 말한다 알고 이 일이있다 String?. 이것 역시 optionalString유형 String?되지만 기본 유형이 다른 경우 충돌합니다.

그런 다음 첫 번째 스타일 if let을와 함께 사용 하여 선택 사항을 안전하게 풀 수 있습니다.

if let string = dict["SomeKey"] as? String {
    // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
    // identified the type as String, and the value is now unwrapped and ready to use.  In
    // this case "string" has the type "String".
    print(string)
}

첫 번째 방법이 항상 더 낫지 않습니까? 둘 다 String 유형의 선택 사항을 반환합니까? 두 번째 방법이 첫 번째 방법과 동일한 작업을 수행하는 것처럼 보이지만 다운 캐스트가 실패하면 충돌 할 수 있습니다. 그렇다면 왜 그것을 사용합니까?
Sikander

6
예 @Sikander, 첫 번째는 항상 더 좋습니다. 두 번째는 사용하지 않을 것입니다.
vacawama

14

as? Types-다운 캐스팅 공정이 선택 사항임을 의미합니다. 프로세스는 성공하거나 실패 할 수 있습니다 (다운 캐스팅이 실패하면 시스템이 nil을 반환합니다).

as! Type?-여기서 다운 캐스팅 과정이 성공적이어야합니다 !. 끝 물음표는 최종 결과가 nil 일 수 있는지 여부를 나타냅니다.

"!"에 대한 추가 정보 그리고 "?"

2 가지 사례를 보자

  1. 치다:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell

    여기서 우리는 식별자 "Cell"이있는 셀을 UITableViewCell로 다운 캐스팅 한 결과가 성공했는지 여부를 알 수 없습니다. 실패하면 nil을 반환합니다 (그래서 여기서 충돌을 피합니다). 여기서 우리는 아래와 같이 할 수 있습니다.

    if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
        // If we reached here it means the down casting was successful
    }
    else {
        // unsuccessful down casting
    }

    그러니 이것을 기억합시다.- ?가치가 nil인지 아닌지 확실하지 않다는 뜻 이라면 (물음표는 우리가 알지 못할 때 나타납니다).

  2. 다음과 대조 :

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 

    여기서 우리는 컴파일러에게 다운 캐스팅이 성공해야한다고 알려줍니다. 실패하면 시스템이 충돌합니다. 따라서 !값이 nil이 아니라고 확신 할 때 제공 합니다.


11

vacawama가 말한 것을 명확히하기 위해 여기에 예가 있습니다 ...

스위프트 3.0 :

import UIKit

let str_value:    Any   = String("abc")!
let strOpt_value: Any?  = String("abc")!
let strOpt_nil:   Any?  = (nil as String?)
let int_value:    Any   = Int(1)
let intOpt_value: Any?  = Int(1)
let intOpt_nil:   Any?  = (nil as Int?)

// as String
//str_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//int_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// as? String
  str_value     as? String // == "abc"
  strOpt_value  as? String // == "abc"
  strOpt_nil    as? String // == nil
  int_value     as? String // == nil
  intOpt_value  as? String // == nil
  intOpt_nil    as? String // == nil

// as! String
  str_value     as! String // == "abc"
  strOpt_value  as! String // == "abc"
//strOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
//int_value     as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_value  as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.

// as String?
//str_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//strOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//int_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//intOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// as? String?
//str_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as? String? // == "abc"
  strOpt_nil    as? String? // == nil
//int_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  intOpt_value  as? String? // == nil
  intOpt_nil    as? String? // == nil

// as! String?
//str_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as! String? // == "abc"
  strOpt_nil    as! String? // == nil
//int_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//intOpt_value  as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  intOpt_nil    as! String? // == nil

// let _ = ... as String
//if let _ = str_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String
if let _ = str_value    as? String { true } // true
if let _ = strOpt_value as? String { true } // true
if let _ = strOpt_nil   as? String { true } // false
if let _ = int_value    as? String { true } // false
if let _ = intOpt_value as? String { true } // false
if let _ = intOpt_nil   as? String { true } // false

// let _ = ... as! String
//if let _ = str_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = int_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'

// let _ = ... as String?
//if let _ = str_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String?
//if let _ = str_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as? String? { true } // true
  if let _ = strOpt_nil   as? String? { true } // true
//if let _ = int_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = intOpt_value as? String? { true } // false
  if let _ = intOpt_nil   as? String? { true } // true

// let _ = ... as! String?
//if let _ = str_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as! String? { true } // true
  if let _ = strOpt_nil   as! String? { true } // false
//if let _ = int_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  if let _ = intOpt_nil   as! String? { true } // false

스위프트 2.0 :

import UIKit

let str:    AnyObject   = String("abc")
let strOpt: AnyObject?  = String("abc")
let strNil: AnyObject?  = (nil as String?)
let int:    AnyObject   = Int(1)
let intOpt: AnyObject?  = Int(1)
let intNil: AnyObject?  = (nil as Int?)

str    as? String // == "abc"
strOpt as? String // == "abc"
strNil as? String // == nil
int    as? String // == nil
intOpt as? String // == nil
intNil as? String // == nil

str    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
strOpt as! String? // == "abc"
strNil as! String? // == nil
int    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString'
intNil as! String? // == nil

예를 들어 +1하지만 동일한 예를 사용하여 설명해 주시겠습니까? 대신? let cell = tableView.dequeueReusableCellWithIdentifier ( "Cell") as! UITableViewCell..i 추측? as가 필요한 이유는 충분했습니다!
아니시 Parajuli 웃

let cell = tableView.dequeueReusableCellWithIdentifier ( "Cell")로? UITableViewCell. -여기서 우리는 식별자가 "Cell"인 셀을 UITableViewCell로 다운 캐스팅 한 결과가 nill인지 여부를 알 수 없습니다. nill이면 nill을 반환합니다 (그래서 여기서 충돌을 피합니다).
지 쉬누 발라

흥미 롭습니다 intNil as! String? // ==nil. 크래시를 일으키지 않습니다 !!! ???, 옵션 <Int> .None은 Optional <String>
.None과 다릅니다 .None

당신은 왜 낙심 할 as?String? 왜 다운 캐스트하지 String?않습니까? 왜 낙심하지 않습니다 as!String?
Honey

Swift 3에서이 플레이 그라운드를 시도하고 있지만 Any대신 사용해야 합니다AnyObject
Honey

9
  • as 업 캐스팅 및 브리지 유형에 대한 유형 캐스팅에 사용
  • as? 안전한 캐스팅에 사용, 실패하면 nil 반환
  • as! 강제 캐스팅에 사용, 실패하면 충돌

노트 :

  • as! 원시 유형을 선택 사항으로 캐스팅 할 수 없습니다.

예 :

let rawString: AnyObject = "I love swift"
let optionalString: AnyObject? = "we love swift"
let nilString: AnyObject? = (nil as String?)

let rawInt: AnyObject = Int(3)
let optionalInt: AnyObject? = Int(3)
let nilInt: AnyObject? = (nil as Int?)

var age: Int? = nil
var height: Int? = 180

추가하여 ? 데이터 유형 바로 뒤에 컴파일러에게 변수에 숫자가 포함될 수 있는지 여부를 알립니다. 산뜻한! Optional 상수를 정의하는 것은 실제로 의미가 없습니다. 값을 한 번만 설정할 수 있으므로 값이 nil인지 여부를 말할 수 있습니다.

"?"를 사용해야하는 경우 그리고 언제 "!"

UIKit 기반의 간단한 앱이 있다고 가정 해 보겠습니다. 뷰 컨트롤러에 코드가 있고 그 위에 새로운 뷰 컨트롤러를 표시하려고합니다. 내비게이션 컨트롤러를 사용하여 화면에 새보기를 푸시하기로 결정해야합니다.

우리가 알고 있듯이 모든 ViewController 인스턴스에는 속성 탐색 컨트롤러가 있습니다. 탐색 컨트롤러 기반 앱을 빌드하는 경우 앱 마스터보기 컨트롤러의이 속성이 자동으로 설정되며이를 사용하여보기 컨트롤러를 푸시하거나 팝할 수 있습니다. 단일 앱 프로젝트 템플릿을 사용하는 경우 자동으로 생성되는 탐색 컨트롤러가 없으므로 앱의 기본 뷰 컨트롤러는 navigationController 속성에 아무것도 저장하지 않습니다.

이것이 바로 Optional 데이터 유형의 경우라고 이미 짐작 하셨을 것입니다. UIViewController를 확인하면 속성이 다음과 같이 정의되어 있음을 알 수 있습니다.

var navigationController: UINavigationController? { get }

이제 사용 사례로 돌아가 보겠습니다. 뷰 컨트롤러에 항상 내비게이션 컨트롤러가 있다는 사실을 알고 있다면 계속 진행하여 강제로 풀 수 있습니다.

controller.navigationController!.pushViewController(myViewController, animated: true)

당신은! 속성 이름 뒤에는 컴파일러 에게이 속성이 선택 사항인지 신경 쓰지 않는다고 말합니다.이 코드가 실행될 때 항상 값 저장소가 있으므로이 옵션을 일반 데이터 유형처럼 취급합니다. 좋지 않나요? 뷰 컨트롤러에 내비게이션 컨트롤러가 없다면 어떻게 될까요? navigationController에 저장된 값이 항상 있다는 제안이 잘못 되었습니까? 앱이 충돌합니다. 그렇게 간단하고 추합니다.

따라서! 이것이 안전하다고 101 % 확신하는 경우에만.

내비게이션 컨트롤러가 항상 있다는 확신이 없다면 어떨까요? 그런 다음 사용할 수 있습니까? 대신! :

controller.navigationController?.pushViewController(myViewController, animated: true)

무엇입니까? 속성 이름 뒤에는 컴파일러 에게이 속성에 nil 또는 값이 포함되어 있는지 여부를 알 수 없으므로 값이 있으면 사용하고 그렇지 않으면 전체 표현식 nil을 고려합니다. 효과적으로? 탐색 컨트롤러가있는 경우에만 해당 속성을 사용할 수 있습니다. 어떤 종류의 수표 나 어떤 종류의 주물이든 아니오. 이 구문은 내비게이션 컨트롤러가 있든 없든 상관하지 않고있을 때만 무언가를하고 싶을 때 완벽합니다.

Fantageek 에게 큰 감사


8

그들은 Swift에서 두 가지 다른 형태의 다운 캐스팅 입니다.

as?조건부 형식 으로 알려진 ( ) 는 다운 캐스트하려는 유형의 선택적 값을 반환합니다.

다운 캐스트가 성공할지 확실하지 않을 때 사용할 수 있습니다. 이 형식의 연산자는 항상 선택적 값을 반환하며 다운 캐스트가 불가능한 경우 값은 nil이됩니다. 이를 통해 성공적인 다운 캐스트를 확인할 수 있습니다.


as!Forced Form 으로 알려진 ( ) 은 다운 캐스트를 시도하고 결과를 단일 복합 동작으로 강제 해제합니다.

다운 캐스트가 항상 성공할 것이라고 확신 할 때만 사용해야합니다 . 이 형식의 연산자는 잘못된 클래스 유형으로 다운 캐스트하려고하면 런타임 오류 를 트리거합니다 .

자세한 내용 은 Apple 문서의 Type Casting 섹션을 확인하십시오 .


4

이 코드 예제는 누군가가 원칙을 이해하는 데 도움이 될 것입니다.

var dict = [Int:Any]()
dict[1] = 15

let x = dict[1] as? String
print(x) // nil because dict[1] is an Int

dict[2] = "Yo"

let z = dict[2] as! String?
print(z) // optional("Yo")
let zz = dict[1] as! String // crashes because a forced downcast fails


let m = dict[3] as! String?
print(m) // nil. the forced downcast succeeds, but dict[3] has no value

또한, z2 = dict [2] as! 문자열 // "Yo"(선택 사항 아님)
Jay



-1

저는 Swift 초보자이며 '선택 사항'에 대해 이해하는대로 설명하려고이 예제를 작성합니다. 내가 틀렸다면 나를 바로 잡으십시오.

감사.


class Optional {

    var lName:AnyObject! = "1"

    var lastName:String!
}

let obj = Optional()

print(obj.lName)

print(obj.lName!)

obj.lastName = obj.lName as? String

print(obj.lastName)

(1) : obj.lastName = obj.lName as! String

vs

(2) : obj.lastName = obj.lName as? String

정답 : (1) 여기서 프로그래머는 “obj.lName”문자열 유형 객체 를 포함 하고 있다고 확신 합니다. 따라서 그 값을 “obj.lastName”.

이제 프로그래머가 옳다면 "obj.lName"문자열 유형 객체를 의미하면 문제가 없습니다. "obj.lastName"은 동일한 값으로 설정됩니다.

그러나 프로그래머가 틀렸다면 "obj.lName"문자열 유형 객체가 아니라는 것을 의미 합니다. 즉 "NSNumber"등과 같은 다른 유형 객체를 포함합니다. 그런 다음 CRASH (런타임 오류).

(2) 프로그래머는 “obj.lName”문자열 유형 객체 또는 다른 유형 객체 가 포함되어 있는지 확신하지 못합니다 . 따라서 “obj.lastName”문자열 유형이면 해당 값을로 설정하십시오 .

이제 프로그래머가 옳다면 “obj.lName”문자열 유형 객체를 의미하면 문제가 없습니다. “obj.lastName”동일한 값으로 설정됩니다.

그러나 프로그래머가 틀렸다면 obj.lName이 문자열 유형 객체가 아니라는 것을 의미합니다. 즉, 다른 유형 객체 "NSNumber"등 이 포함되어 있습니다 . 그런 다음 “obj.lastName”nil 값으로 설정됩니다. 그래서 크래시가 없습니다 (Happy :)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.