Downcasting optionals ใน Swift: as? พิมพ์หรือเป็น! ประเภท?


96

ให้สิ่งต่อไปนี้ใน Swift:

var optionalString: String?
let dict = NSDictionary()

อะไรคือความแตกต่างในทางปฏิบัติระหว่างสองข้อความต่อไปนี้:

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

เทียบกับ

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

คำตอบ:


144

ความแตกต่างในทางปฏิบัติคือ:

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

optionalStringString?จะเป็นตัวแปรประเภท หากประเภทพื้นฐานเป็นสิ่งอื่นที่ไม่ใช่สิ่ง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
ใช่ @ ซิคานเดอร์คนแรกดีกว่าเสมอ ฉันจะไม่ใช้ครั้งที่สอง
vacawama

14

as? Types- หมายถึงกระบวนการหล่อขึ้นรูปเป็นทางเลือก กระบวนการนี้สามารถทำได้สำเร็จหรือไม่ (ระบบจะคืนค่าศูนย์หากการแคสต์ล้มเหลว) วิธีใดก็ตามที่จะไม่ผิดพลาดหากการแคสต์ล้มเหลว

as! Type?- ขั้นตอนการลงหล่อควรจะสำเร็จที่นี่ ( !ระบุว่า) เครื่องหมายคำถามสิ้นสุดระบุว่าผลลัพธ์สุดท้ายสามารถเป็นศูนย์ได้หรือไม่

ข้อมูลเพิ่มเติมเกี่ยวกับ "!" และ "?"

ให้เราใช้เวลา 2 กรณี

  1. พิจารณา:

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

    ที่นี่เราไม่รู้ว่าผลของการลดลงของเซลล์ที่มีตัวระบุ "เซลล์" เป็น UITableViewCell นั้นประสบความสำเร็จหรือไม่ หากไม่สำเร็จมันจะคืนค่าศูนย์ (ดังนั้นเราจึงหลีกเลี่ยงความผิดพลาดที่นี่) ที่นี่เราสามารถทำได้ตามที่ระบุด้านล่าง

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

    ให้เราจำไว้แบบนี้ - ถ้า?หมายความว่าเราไม่แน่ใจว่าค่าเป็นศูนย์หรือไม่ (เครื่องหมายคำถามเกิดขึ้นเมื่อเราไม่รู้สิ่งต่างๆ)

  2. ตรงกันข้ามกับ:

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

    ที่นี่เราบอกคอมไพเลอร์ว่าการแคสต์ควรจะสำเร็จ หากล้มเหลวระบบจะพัง ดังนั้นเราจึงให้!เมื่อเราแน่ใจว่าค่านั้นไม่ใช่ศูนย์


11

เพื่อชี้แจงสิ่งที่ vacawama กล่าวนี่คือตัวอย่าง ...

Swift 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

Swift 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 สำหรับตัวอย่างของคุณ แต่คุณสามารถอธิบายฉันด้วยตัวอย่างเดียวกับที่ใช้เป็น! แทนที่ด้วย? ในขณะที่ downcasting ให้ cell = tableView.dequeueReusableCellWithIdentifier ("Cell") เป็น! UITableViewCell..i เดาว่า? ก็เพียงพอแล้วทำไมต้องเป็น!
Anish Parajuli 웃

ให้ cell = tableView.dequeueReusableCellWithIdentifier ("Cell") เป็น? UITableViewCell - ที่นี่เราไม่รู้ว่าผลลัพธ์ของการแคสต์เซลล์ที่มีตัวระบุ "เซลล์" เป็น UITableViewCell เป็นค่าว่างหรือไม่ ถ้าไม่มีมันก็จะคืนค่า nill (ดังนั้นเราจึงหลีกเลี่ยงความผิดพลาดที่นี่)
jishnu bala

น่าสนใจintNil as! String? // ==nilไม่ทำให้เกิดความผิดพลาด !!! ??? เป็นตัวเลือก <Int> ไม่มีอะไรแตกต่างจากตัวเลือก <String> ไม่มี
onmyway133

ทำไมคุณตกต่ำas?ถึงString? ทำไมคุณไม่มองข้ามไปString?? ทำไมคุณไม่มองข้ามas!ไปString?
น้ำผึ้ง

ความพยายามที่จะทำสนามเด็กเล่นในสวิฟท์ 3 แต่คุณต้องใช้AnyแทนAnyObject
น้ำผึ้ง

9
  • as ใช้สำหรับ upcasting และ type casting เป็น bridged type
  • as? ใช้สำหรับการหล่ออย่างปลอดภัยส่งคืนศูนย์หากล้มเหลว
  • 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

โดยการเพิ่ม? ทันทีหลังจากชนิดข้อมูลคุณบอกคอมไพเลอร์ว่าตัวแปรอาจมีตัวเลขหรือไม่ เรียบร้อย! สังเกตว่าการกำหนดค่าคงที่เป็นทางเลือกนั้นไม่สมเหตุสมผลนักคุณสามารถกำหนดค่าได้เพียงครั้งเดียวดังนั้นคุณจะสามารถบอกได้ว่าค่าของพวกเขาจะเป็นศูนย์หรือไม่

เราควรใช้ "?" เมื่อใด และเมื่อ "!"

สมมติว่าเรามีแอพง่ายๆที่ใช้ UIKit เรามีโค้ดบางส่วนในตัวควบคุมมุมมองของเราและต้องการนำเสนอตัวควบคุมมุมมองใหม่ที่ด้านบน และเราจำเป็นต้องตัดสินใจที่จะผลักดันมุมมองใหม่บนหน้าจอโดยใช้ตัวควบคุมการนำทาง

ดังที่เราทราบทุกอินสแตนซ์ ViewController มีตัวควบคุมการนำทางคุณสมบัติ หากคุณกำลังสร้างแอปที่ใช้ตัวควบคุมการนำทางคุณสมบัติของตัวควบคุมมุมมองหลักของแอปจะถูกตั้งค่าโดยอัตโนมัติและคุณสามารถใช้เพื่อผลักดันหรือตัวควบคุมมุมมองป๊อปอัป หากคุณใช้เทมเพลตโปรเจ็กต์ของแอปเดียวจะไม่มีตัวควบคุมการนำทางที่สร้างขึ้นโดยอัตโนมัติสำหรับคุณดังนั้นตัวควบคุมมุมมองเริ่มต้นของแอปของคุณจะไม่มีสิ่งใดเก็บไว้ในคุณสมบัติ navigationController

ฉันแน่ใจว่าคุณเดาอยู่แล้วว่านี่เป็นกรณีของประเภทข้อมูลเสริม หากคุณตรวจสอบ UIViewController คุณจะเห็นว่าคุณสมบัติถูกกำหนดเป็น:

var navigationController: UINavigationController? { get }

ลองกลับไปที่กรณีการใช้งานของเรา หากคุณทราบข้อเท็จจริงว่าตัวควบคุมมุมมองของคุณมักจะมีตัวควบคุมการนำทางคุณสามารถดำเนินการต่อและบังคับให้แกะออกได้:

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

เมื่อคุณใส่! ข้างหลังชื่อคุณสมบัติที่คุณบอกคอมไพเลอร์ฉันไม่สนใจว่าคุณสมบัตินี้เป็นทางเลือกฉันรู้ว่าเมื่อรหัสนี้ดำเนินการจะมีที่เก็บค่าอยู่เสมอดังนั้นให้ปฏิบัติต่อตัวเลือกนี้เหมือนประเภทข้อมูลปกติ ไม่ดีเหรอ? จะเกิดอะไรขึ้นแม้ว่าจะไม่มีตัวควบคุมการนำทางไปยังตัวควบคุมมุมมองของคุณ หากคุณแนะนำว่าจะมีค่าที่เก็บไว้ใน navigationController ผิดหรือไม่? แอปของคุณจะขัดข้อง เรียบง่ายและน่าเกลียดเป็นอย่างนั้น

ดังนั้นใช้! เฉพาะในกรณีที่คุณมั่นใจ 101% ว่าปลอดภัย

ถ้าคุณไม่แน่ใจว่าจะมีตัวควบคุมการนำทางอยู่เสมอหรือไม่? แล้วคุณสามารถใช้? แทนที่จะเป็น!:

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

อะไรกัน? ด้านหลังชื่อคุณสมบัติบอกคอมไพเลอร์คือฉันไม่รู้ว่าคุณสมบัตินี้มีศูนย์หรือค่าดังนั้น: ถ้ามันมีค่าให้ใช้มันและในทางกลับกันให้พิจารณานิพจน์ทั้งหมด nil ได้อย่างมีประสิทธิภาพ? อนุญาตให้คุณใช้คุณสมบัตินั้นในกรณีที่มีตัวควบคุมการนำทาง ไม่ว่าจะมีการตรวจสอบชนิดใด ๆ หรือการหล่อใด ๆ ไวยากรณ์นี้เหมาะอย่างยิ่งเมื่อคุณไม่สนใจว่าคุณจะมีตัวควบคุมการนำทางหรือไม่และต้องการทำบางอย่างเฉพาะเมื่อมี

ขอบคุณมากสำหรับFantageek


8

Downcastingเป็นสองรูปแบบที่แตกต่างกันใน Swift

( as?)ซึ่งเป็นที่ทราบกันดีว่าเป็นConditional Formจะส่งกลับค่าที่เป็นทางเลือกของประเภทที่คุณกำลังพยายามดาวน์คาสต์

คุณสามารถใช้งานได้เมื่อคุณไม่แน่ใจว่าดาวน์คาสต์จะสำเร็จหรือไม่ ตัวดำเนินการรูปแบบนี้จะส่งคืนค่าที่เป็นทางเลือกเสมอและค่าจะเป็นศูนย์หากไม่สามารถดาวน์แคสต์ได้ สิ่งนี้ช่วยให้คุณสามารถตรวจสอบการดาวน์แคสต์ที่ประสบความสำเร็จ


( as!)ซึ่งทราบว่าเป็นแบบฟอร์มบังคับพยายามดาวน์แคสต์และบังคับให้คลายผลลัพธ์ออกมาเป็นการกระทำแบบผสมเดียว

คุณควรใช้เฉพาะเมื่อคุณแน่ใจว่าการดาวน์แคสต์จะประสบความสำเร็จเสมอไป รูปแบบของตัวดำเนินการนี้จะทำให้เกิดข้อผิดพลาดรันไทม์หากคุณพยายามดาวน์แคสต์เป็นประเภทคลาสที่ไม่ถูกต้อง

สำหรับรายละเอียดเพิ่มเติมโปรดตรวจสอบส่วนType Castingในเอกสารของ Apple


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] เป็น! สตริง // "โย่" (ไม่จำเป็น)
เจย์

0

ที่แรกก็คือ "โยนเงื่อนไข" (ดูใต้ "ผู้ประกอบการประเภทหล่อ" ในเอกสารที่ผมเคยเชื่อมโยง) หากการแคสต์สำเร็จค่าของนิพจน์จะรวมอยู่ในอ็อพชันและส่งคืนมิฉะนั้นค่าที่ส่งคืนจะเป็นศูนย์

ประการที่สองหมายความว่า OptionalString อาจเป็นอ็อบเจ็กต์สตริงหรืออาจเป็นศูนย์

พบข้อมูลเพิ่มเติมในคำถามที่เกี่ยวข้องนี้


0

อาจเป็นวิธีที่ง่ายที่สุดในการจดจำรูปแบบของตัวดำเนินการเหล่านี้ใน Swift โดย!บอกเป็นนัยว่า "this might trap" ในขณะที่?ระบุว่า "this might be nil"

อ้างถึง: https://developer.apple.com/swift/blog/?id=23


-1

ฉันเป็นมือใหม่หัดใช้ Swift และเขียนตัวอย่างนี้โดยพยายามอธิบายตามที่ฉันเข้าใจเกี่ยวกับ 'optionals' ถ้าฉันผิดโปรดแก้ไขฉัน

ขอบคุณ.


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

เทียบกับ

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

ตอบ: (1) ที่นี่โปรแกรมเมอร์ต้องแน่ใจว่า“obj.lName”มีวัตถุประเภทสตริง “obj.lastName”ดังนั้นเพียงแค่ให้ค่าที่จะ

ตอนนี้ถ้าโปรแกรมเมอร์ถูกต้องหมายถึง"obj.lName"เป็นวัตถุประเภทสตริงก็ไม่มีปัญหา "obj.lastName" จะถูกกำหนดเป็นค่าเดียวกัน

แต่ถ้าโปรแกรมเมอร์ผิดหมายถึง"obj.lName"ไม่ใช่วัตถุประเภทสตริงคือมันมีวัตถุประเภทอื่นเช่น "NSNumber" เป็นต้นแล้ว CRASH (Run Time Error)

(2) โปรแกรมเมอร์ไม่แน่ใจว่า“obj.lName”มีวัตถุประเภทสตริงหรือวัตถุประเภทอื่น ๆ ดังนั้นตั้งค่า“obj.lastName”นั้นเป็นถ้าเป็นประเภทสตริง

ตอนนี้ถ้าโปรแกรมเมอร์ถูกต้องหมายถึง“obj.lName”เป็นวัตถุประเภทสตริงก็ไม่มีปัญหา “obj.lastName”จะตั้งค่าเป็นค่าเดียวกัน

แต่ถ้าโปรแกรมเมอร์ผิดหมายความว่า obj.lName ไม่ใช่อ็อบเจกต์ประเภทสตริงคือมันมีอ็อบเจ็กต์ประเภทอื่น ๆ เช่น"NSNumber"ฯลฯ จากนั้น“obj.lastName”จะตั้งค่าเป็นค่าศูนย์ ไม่มีความผิดพลาด (มีความสุข :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.