ตรวจสอบว่าวัตถุเป็นประเภทที่กำหนดใน Swift


267

AnyObjectฉันมีอาร์เรย์ที่ถูกสร้างขึ้นจาก ฉันต้องการวนซ้ำและหาองค์ประกอบทั้งหมดที่เป็นอินสแตนซ์ของอาร์เรย์

ฉันจะตรวจสอบว่าวัตถุเป็นประเภทที่กำหนดใน Swift ได้อย่างไร


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

ฉันได้แก้ไขคำถามนี้เพื่อยกเลิกการคัดค้านจากstackoverflow.com/q/24093433ซึ่งฉันลงคะแนนให้เปิดใหม่ มันมีทั้งคำถามที่คล้ายกัน แต่มีประโยชน์ แต่คำตอบนั้นค่อนข้างชัดเจนดังนั้นมันจะมีประโยชน์ในการแยกพวกมันออก
Jeremy Banks

1
มีความเป็นไปได้ที่ซ้ำกันของวิธีค้นหาประเภทของวัตถุ (ใน Swift)
Esqarrouth

คำตอบ:


304

หากคุณต้องการตรวจสอบกับบางประเภทคุณสามารถทำสิ่งต่อไปนี้:

if let stringArray = obj as? [String] {
    // obj is a string array. Do something with stringArray
}
else {
    // obj is not a string array
}

คุณสามารถใช้ "เป็น!" และนั่นจะทำให้เกิดข้อผิดพลาดรันไทม์หากobjไม่ใช่ประเภท[String]

let stringArray = obj as! [String]

คุณยังสามารถตรวจสอบองค์ประกอบครั้งละหนึ่งรายการ:

let items : [Any] = ["Hello", "World"]
for obj in items {
   if let str = obj as? String {
      // obj is a String. Do something with str
   }
   else {
      // obj is not a String
   }
}

เหตุใดจึงมีเพียงโยนข้อผิดพลาดรันไทม์และไม่ใช่ข้อผิดพลาดเวลารวบรวมเมื่อ?ไม่มีอยู่ ดูเหมือนasและ?เมื่อรวมกันแล้วจะทำการตรวจสอบรันไทม์ เมื่อใดควรใช้asโดย?ไม่เหมาะสม ขอบคุณล่วงหน้า.
Unheilig

@ Unheilig คุณควรใช้asโดยไม่มี?หากไม่มีวิธีที่โปรแกรมของคุณสามารถกู้คืนจากวัตถุที่ไม่ได้เป็นประเภทนั้นได้เพราะโปรแกรมจะหยุดทันทีหากไม่มี ใช้?ในifคำสั่งช่วยให้โปรแกรมที่จะดำเนินการต่อไป
drewag

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

@ Unheilig ฉันขอโทษฉันไม่เข้าใจสิ่งที่คุณพูด / ถาม การ?อนุญาตให้ส่งคืนnilก่อให้เกิดคำสั่ง if เพื่อส่งคืนfalseและทำให้เกิดการผ่านไปยังคำสั่ง else แต่ผมคิดว่าคำอธิบายที่จะช่วยให้มีความเข้าใจ แต่if letจริง ๆ แล้วเป็นกรณีพิเศษในคอมไพเลอร์
drewag

1
@Unheilig ที่ถูกต้องคุณสามารถใช้ var ถ้าคุณต้องการเพื่อให้สามารถปรับเปลี่ยนค่าในขณะที่อยู่ในขอบเขตท้องถิ่นที่ (การเปลี่ยนแปลงเหล่านั้นจะไม่ส่งผลกระทบต่อที่อยู่นอกขอบเขต)
drewag

202

ในSwift 2.2 - 5ตอนนี้คุณสามารถทำได้:

if object is String
{
}

จากนั้นกรองอาร์เรย์ของคุณ:

let filteredArray = originalArray.filter({ $0 is Array })

หากคุณมีหลายประเภทให้ตรวจสอบ:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

การแก้ปัญหานี้จะสั้น แต่มันก็มีข้อเสีย: คุณไม่สามารถใช้objectเป็นStringภายในวงเล็บ (อย่างน้อยในสวิฟท์ 2) ในขณะที่มีletวิธีการแก้ปัญหาที่คุณสามารถทำมันได้
Ferran Maylinch

@FerranMaylinch ไม่เข้าใจสิ่งที่คุณหมายถึงเพราะการใช้objectในบล็อกนั้นใช้ได้
ความหมายมีความสำคัญ

@ ความหมาย - เรื่องเช่นคุณจะไม่สามารถทำได้object.uppercaseStringเพราะประเภทของตัวแปรที่ไม่ได้ถูกโยนไปที่ประเภทนั้นคุณเพิ่งตรวจสอบว่าวัตถุ (ชี้โดยตัวแปร) เป็นString
Ferran Maylinch

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

152

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

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

func area (shape: Shape) -> Double {
  if shape is Circle { ... }
  else if shape is Rectangle { ... }
}

“ ใช้ตัวดำเนินการตรวจสอบชนิด (คือ) เพื่อตรวจสอบว่าอินสแตนซ์นั้นเป็นประเภทย่อยบางประเภทหรือไม่ ตัวดำเนินการตรวจสอบชนิดจะคืนค่าเป็นจริงถ้าอินสแตนซ์นั้นเป็นประเภทย่อยและเป็นเท็จถ้าไม่ใช่” ข้อความที่ตัดตอนมาจาก: Apple Inc. “ ภาษาการเขียนโปรแกรม Swift” iBooks

ในข้างต้นวลี 'ของประเภทรองบางอย่าง' เป็นสิ่งสำคัญ การใช้is Circleและis Rectangleเป็นที่ยอมรับโดยคอมไพเลอร์เพราะค่าshapeดังกล่าวถูกประกาศเป็นShape(ซูเปอร์คลาสของCircleและRectangle)

หากคุณกำลังใช้ประเภทดั้งเดิม superclass Anyจะเป็น นี่คือตัวอย่าง:

 21> func test (obj:Any) -> String {
 22.     if obj is Int { return "Int" }
 23.     else if obj is String { return "String" }
 24.     else { return "Any" }
 25. } 
 ...  
 30> test (1)
$R16: String = "Int"
 31> test ("abc")
$R17: String = "String"
 32> test (nil)
$R18: String = "Any"

2
ถ้าฉันเก็บประเภทดั้งเดิมไว้ในอาร์เรย์หรือถ้าอาร์เรย์เป็นชนิดดั้งเดิมนั้นจะisยังคงทำงานที่นี่ ขอบคุณ
Unheilig

มันควรจะทำงานถ้าคุณประกาศเป็นobject Anyอัปเดตด้วยตัวอย่าง
GoZoner

ขอบคุณสำหรับการตอบ. ดูเหมือนว่าจะมีแนวโน้ม ฉันเพียง แต่สงสัยว่าเป็นไปตามคำตอบด้านล่างซึ่งAnyObjectเป็นข้อเสนอแนะที่ดูเหมือนว่าจะได้รับการโต้กลับเนื่องจากไม่ได้รับค่าจากAnyObject NSObjectหากAnyแตกต่างกันนี่ก็เป็นทางออกที่ดีเช่นกัน ขอบคุณ
Unheilig

21

ฉันมี 2 วิธีในการทำ:

if let thisShape = aShape as? Square 

หรือ:

aShape.isKindOfClass(Square)

นี่คือตัวอย่างโดยละเอียด:

class Shape { }
class Square: Shape { } 
class Circle: Shape { }

var aShape = Shape()
aShape = Square()

if let thisShape = aShape as? Square {
    println("Its a square")
} else {
    println("Its not a square")
}

if aShape.isKindOfClass(Square) {
    println("Its a square")
} else {
    println("Its not a square")
}

แก้ไข: 3 ตอนนี้:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}

1
isKindOfClassเป็นวิธีการของNSObjectโปรโตคอล; มันควรจะใช้ได้กับคลาสที่นำมาใช้เท่านั้น (คลาสทั้งหมดที่สืบทอดจาก NSObject รวมถึงคลาส Swift แบบกำหนดเองใด ๆ ที่ปรับใช้อย่างชัดเจน)
Nicolas Miari


9

สมมติว่าdrawTriangleเป็นตัวอย่างของ UIView หากต้องการตรวจสอบว่า drawTriangle เป็นประเภท UITableView หรือไม่:

ในสวิฟท์ 3 ,

if drawTriangle is UITableView{
    // in deed drawTriangle is UIView
    // do something here...
} else{
    // do something here...
}

นอกจากนี้ยังสามารถใช้สำหรับคลาสที่กำหนดด้วยตัวเอง คุณสามารถใช้สิ่งนี้เพื่อตรวจสอบมุมมองย่อย


5

ทำไมไม่ใช้ฟังค์ชั่นบิวท์อินที่สร้างมาเพื่องานนี้โดยเฉพาะ?

let myArray: [Any] = ["easy", "as", "that"]
let type = type(of: myArray)

Result: "Array<Any>"

ฟังก์ชั่น type () นั้นง่ายมาก
:)

5

ถูกเตือนเกี่ยวกับสิ่งนี้:

var string = "Hello" as NSString
var obj1:AnyObject = string
var obj2:NSObject = string

print(obj1 is NSString)
print(obj2 is NSString)
print(obj1 is String)
print(obj2 is String) 

ทั้งสี่บรรทัดสุดท้ายคืนค่าจริงเนื่องจากถ้าคุณพิมพ์

var r1:CGRect = CGRect()
print(r1 is String)

... มันพิมพ์ "false" แน่นอน แต่คำเตือนบอกว่า Cast จาก CGRect to String ล้มเหลว ดังนั้นบางประเภทจะเชื่อมโยงกันและคำหลัก 'คือ' จึงเรียกการส่งโดยนัย

คุณควรใช้หนึ่งในสิ่งต่อไปนี้:

myObject.isKind(of: MyClass.self)) 
myObject.isMember(of: MyClass.self))

2

หากคุณต้องการตรวจสอบคลาสโดยไม่ได้รับคำเตือนเนื่องจากค่าที่ไม่ได้ใช้ (ให้ someVariable ... ) คุณสามารถแทนที่สิ่งที่อนุญาตด้วยบูลีน:

if (yourObject as? ClassToCompareWith) != nil {
   // do what you have to do
}
else {
   // do something else
}

Xcode เสนอสิ่งนี้เมื่อฉันใช้วิธีการให้และไม่ได้ใช้ค่าที่กำหนด


2

ทำไมไม่ใช้สิ่งนี้

fileprivate enum types {
    case typeString
    case typeInt
    case typeDouble
    case typeUnknown
}

fileprivate func typeOfAny(variable: Any) -> types {
    if variable is String {return types.typeString}
    if variable is Int {return types.typeInt}
    if variable is Double {return types.typeDouble}
    return types.typeUnknown
}

ใน Swift 3


2

Swift 4.2 ในกรณีของฉันใช้ฟังก์ชัน isKind

isKind (of :) ส่งคืนค่าบูลีนที่ระบุว่าผู้รับเป็นอินสแตนซ์ของคลาสที่กำหนดหรืออินสแตนซ์ของคลาสใด ๆ ที่สืบทอดจากคลาสนั้น

  let items : [AnyObject] = ["A", "B" , ... ]
  for obj in items {
    if(obj.isKind(of: NSString.self)){
      print("String")
    }
  }

อ่านเพิ่มเติมhttps://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind


1
นั่นไม่ใช่ Swift มันเป็นโกโก้และทำงานเฉพาะที่มันจะทำงานกับ Objective C.
ด้าน

1

myObject as? Stringผลตอบแทนที่ได้nilถ้าไม่ได้เป็นmyObject Stringมิฉะนั้นจะส่งคืน a String?เพื่อให้คุณสามารถเข้าถึงสตริงได้ด้วยตนเองmyObject!หรือส่งมันmyObject! as Stringอย่างปลอดภัย



1

เพียงเพื่อความสมบูรณ์ตามคำตอบที่ยอมรับและอื่น ๆ :

let items : [Any] = ["Hello", "World", 1]

for obj in items where obj is String {
   // obj is a String. Do something with str
}

แต่คุณยังสามารถ ( compactMapรวมถึง "แมป" ค่าที่filterไม่ได้):

items.compactMap { $0 as? String }.forEach{ /* do something with $0 */ ) }

และรุ่นที่ใช้switch:

for obj in items {
    switch (obj) {
        case is Int:
           // it's an integer
        case let stringObj as String:
           // you can do something with stringObj which is a String
        default:
           print("\(type(of: obj))") // get the type
    }
}

แต่ติดกับคำถามเพื่อตรวจสอบว่าเป็นอาร์เรย์ (เช่น[String]):

let items : [Any] = ["Hello", "World", 1, ["Hello", "World", "of", "Arrays"]]

for obj in items {
  if let stringArray = obj as? [String] {
    print("\(stringArray)")
  }
}

หรือมากกว่าโดยทั่วไป (ดูคำตอบสำหรับคำถามอื่น ๆ ):

for obj in items {
  if obj is [Any] {
    print("is [Any]")
  }

  if obj is [AnyObject] {
    print("is [AnyObject]")
  }

  if obj is NSArray {
    print("is NSArray")
  }
}

1

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

พิจารณารหัสนี้เช่น:

func handleError ( error: Error ) {
    if let nsError = error as? NSError {

ชนิดข้อมูลทุกสอดคล้องกับErrorโปรโตคอลสามารถแปลงเป็นNSErrorวัตถุดังนั้นนี้จะเคยประสบความสำเร็จ แต่นั่นไม่ได้หมายความว่าerrorเป็นNSErrorวัตถุหรือคลาสย่อยของมัน

การตรวจสอบประเภทที่ถูกต้องจะเป็น:

func handleError ( error: Error ) {
    if type(of: error) == NSError.self {

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

func handleError ( error: Error ) {
    if error is NSError.Type {

0

หากคุณมีการตอบสนองเช่นนี้:

{
  "registeration_method": "email",
  "is_stucked": true,
  "individual": {
    "id": 24099,
    "first_name": "ahmad",
    "last_name": "zozoz",
    "email": null,
    "mobile_number": null,
    "confirmed": false,
    "avatar": "http://abc-abc-xyz.amazonaws.com/images/placeholder-profile.png",
    "doctor_request_status": 0
  },
  "max_number_of_confirmation_trials": 4,
  "max_number_of_invalid_confirmation_trials": 12
}

และคุณต้องการตรวจสอบค่าis_stuckedที่จะอ่านเป็น AnyObject สิ่งที่คุณต้องทำคือ

if let isStucked = response["is_stucked"] as? Bool{
  if isStucked{
      print("is Stucked")
  }
  else{
      print("Not Stucked")
 }
}

0

หากคุณไม่ทราบว่าคุณจะได้รับชุดพจนานุกรมหรือพจนานุกรมเดี่ยวในการตอบสนองจากเซิร์ฟเวอร์คุณต้องตรวจสอบว่าผลลัพธ์มีอาร์เรย์หรือไม่
ในกรณีของฉันมักจะได้รับพจนานุกรมหลายครั้งยกเว้นครั้งเดียว ดังนั้นเพื่อจัดการว่าฉันใช้รหัสด้านล่างสำหรับ swift 3

if let str = strDict["item"] as? Array<Any>

ที่นี่เป็นอย่างไร Array ตรวจสอบว่าค่าที่ได้รับเป็นอาร์เรย์ (ของรายการพจนานุกรม) ในกรณีอื่นคุณสามารถจัดการได้หากเป็นรายการพจนานุกรมเดี่ยวซึ่งไม่ได้เก็บไว้ในอาร์เรย์


0

รุ่น Swift 5.2 และ Xcode: 11.3.1 (11C504)

นี่คือวิธีการตรวจสอบชนิดข้อมูลของฉัน:

 if let typeCheck = myResult as? [String : Any] {
        print("It's Dictionary.")
    } else { 
        print("It's not Dictionary.") 
    }

ฉันหวังว่ามันจะช่วยคุณ


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