Kiểm tra xem một đối tượng có phải là loại đã cho trong Swift không


267

Tôi có một mảng được tạo thành AnyObject. Tôi muốn lặp lại nó và tìm tất cả các phần tử là các thể hiện mảng.

Làm cách nào để kiểm tra xem một đối tượng có thuộc loại đã cho trong Swift không?


Câu hỏi của bạn hỏi về việc tìm loại đối tượng nhất định, nhưng bạn đã chấp nhận một câu trả lời chỉ có khả năng kiểm tra xem một đối tượng có thuộc loại nhất định hay không. Tôi đề nghị bạn chỉnh sửa câu hỏi của bạn để cụ thể rằng, nếu không nhiều độc giả sẽ không hài lòng với câu trả lời bạn đã chấp nhận. (Tất cả các câu trả lời khác đều tương tự nhau, vì vậy may mắn là bạn không cần phải lo lắng về việc làm cho chúng không hợp lệ bằng cách thu hẹp câu hỏi của bạn.)
Jeremy Banks

Tôi đã chỉnh sửa câu hỏi này để phân tán nó khỏi stackoverflow.com/q/24093433 , mà tôi đang bỏ phiếu để mở lại. Chúng đều hữu ích, tương tự, câu hỏi, nhưng câu trả lời khá khác biệt nên sẽ rất hữu ích khi giữ chúng tách biệt.
Jeremy Banks

Câu trả lời:


304

Nếu bạn muốn kiểm tra một loại cụ thể, bạn có thể làm như sau:

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

Bạn có thể sử dụng "như!" và điều đó sẽ gây ra lỗi thời gian chạy nếu objkhông phải là kiểu[String]

let stringArray = obj as! [String]

Bạn cũng có thể kiểm tra một yếu tố tại một thời điểm:

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
   }
}

Tại sao điều đó chỉ gây ra lỗi thời gian chạy chứ không phải lỗi thời gian biên dịch khi ?không có. Nghe có vẻ như as?khi kết hợp sẽ thực hiện kiểm tra thời gian chạy. Khi nào sẽ thích hợp để sử dụng asmà không cần ?? Cảm ơn trước.
Unheilig

@Unheilig Bạn chỉ nên sử dụng as?không có cách nào để chương trình của bạn có thể phục hồi từ đối tượng không thuộc loại đó vì chương trình sẽ dừng ngay lập tức nếu không. Sử dụng ?trong ifcâu lệnh cho phép chương trình tiếp tục.
vẽ

Cảm ơn cho phản ứng. Sửa lỗi cho tôi nếu tôi sai: Tôi nghĩ rằng việc sử dụng ?trong trường hợp này sẽ thực hiện kiểm tra loại "chung", nếu có, với mệnh đề if, nếu không, cho mệnh đề khác. Nếu không có cái ?khác sẽ không bao giờ được nhập và như bạn đã chỉ ra gây ra lỗi thời gian chạy. Cảm ơn một lần nữa.
Unheilig

@Unheilig Tôi xin lỗi, tôi không hiểu bạn đang nói / hỏi gì. Việc ?cho phép gán trở lại nillàm cho câu lệnh if trở lại falsevà do đó rơi vào câu lệnh khác. Tuy nhiên, tôi nghĩ rằng lời giải thích đó giúp ích cho sự hiểu biết, nhưng if letthực sự là một trường hợp đặc biệt trong trình biên dịch
drewag

1
@Unheilig Đúng, bạn có thể sử dụng var nếu bạn muốn có thể sửa đổi giá trị trong khi ở phạm vi cục bộ đó (những thay đổi đó sẽ không ảnh hưởng đến bên ngoài phạm vi)
drewag

202

Trong Swift 2.2 - 5 bây giờ bạn có thể làm:

if object is String
{
}

Sau đó, để lọc mảng của bạn:

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

Nếu bạn có nhiều loại để kiểm tra:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

Giải pháp này ngắn hơn, nhưng có một nhược điểm: bạn không thể sử dụng objectnhư Stringbên trong niềng răng (ít nhất là trong Swift 2), trong khi với letgiải pháp bạn có thể thực hiện.
Ferran Maylinch

@FerranMaylinch Đừng hiểu ý của bạn vì sử dụng objecttrong khối là tốt.
ý nghĩa-vấn đề

@ ý nghĩa-những vấn đề ví dụ như bạn sẽ không thể làm được object.uppercaseStringvì kiểu của biến không được đúc để loại đó, bạn chỉ cần kiểm tra rằng đối tượng (được trỏ bởi biến) là mộtString
Ferran Maylinch

Làm thế nào bạn có thể làm điều này nếu loại lớp bạn đang kiểm tra là tùy ý? Nếu bạn chỉ có một biến bạn cần lấy một loại lớp từ?
Alex Zavatone

152

Nếu bạn chỉ muốn biết nếu một đối tượng một kiểu con của một kiểu nhất định thì có một cách tiếp cận đơn giản hơn:

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

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

Sử dụng toán tử kiểm tra loại (is) để kiểm tra xem một thể hiện có thuộc loại lớp con nhất định không. Toán tử kiểm tra kiểu trả về true nếu thể hiện của kiểu lớp con đó và false nếu nó không phải. Trích từ: Apple Inc., Ngôn ngữ lập trình Swift. Sách điện tử .

Ở trên, cụm từ 'của một loại lớp con nhất định' rất quan trọng. Việc sử dụngis Circleis Rectangleđược trình biên dịch chấp nhận vì giá trị đó shapeđược khai báo là Shape(một siêu lớp của CircleRectangle).

Nếu bạn đang sử dụng các kiểu nguyên thủy, thì siêu lớp sẽ là Any. Đây là một ví dụ:

 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
Điều gì xảy ra nếu tôi lưu trữ một kiểu nguyên thủy trong một mảng hoặc nếu mảng là một trong các kiểu nguyên thủy, isvẫn sẽ hoạt động ở đây? Cảm ơn.
Unheilig

Nó sẽ hoạt động nếu bạn khai báo objectnhư Any. Cập nhật với một ví dụ.
GoZoner

Cảm ơn đã trả lời. Có vẻ đầy hứa hẹn. Nghi ngờ duy nhất của tôi là theo câu trả lời dưới đây, trong đó AnyObjectđược đề xuất, dường như đã bị vặn lại do AnyObjectkhông được thừa kế từ NSObject. Nếu Anykhác, thì đây thực tế cũng là một giải pháp tuyệt vời. Cảm ơn.
Unheilig

21

Tôi có 2 cách để làm điều đó:

if let thisShape = aShape as? Square 

Hoặc là:

aShape.isKindOfClass(Square)

Dưới đây là một ví dụ chi tiết:

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")
}

Chỉnh sửa: 3 ngay bây giờ:

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

1
isKindOfClasslà một phương thức của NSObjectgiao thức; nó chỉ nên hoạt động cho các lớp chấp nhận nó (tất cả các lớp giảm dần từ NSObject, cộng với bất kỳ lớp Swift tùy chỉnh nào chấp nhận nó một cách rõ ràng)
Nicolas Miari


9

Giả sử drawTrigin là một phiên bản của UIView. Để kiểm tra xem drawTrigin có thuộc loại UITableView không:

Trong Swift 3 ,

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

Điều này cũng có thể được sử dụng cho các lớp được xác định bởi chính bạn. Bạn có thể sử dụng điều này để kiểm tra các lượt xem của một khung nhìn.


5

Tại sao không sử dụng chức năng tích hợp được xây dựng đặc biệt cho nhiệm vụ này?

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

Result: "Array<Any>"

Hàm type () chỉ đơn giản :)
vivi

5

Được cảnh báo về điều này:

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) 

Tất cả bốn dòng cuối cùng trả về đúng, điều này là do nếu bạn nhập

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

... Tất nhiên, nó in "sai", nhưng Cảnh báo nói rằng Truyền từ CGRect sang String không thành công. Vì vậy, một số loại được bắc cầu, từ khóa 'là' gọi một từ ẩn.

Bạn nên sử dụng một trong những điều sau đây:

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

2

Nếu bạn chỉ muốn kiểm tra lớp mà không nhận được cảnh báo vì giá trị được xác định không sử dụng (hãy để someVariable ...), bạn có thể chỉ cần thay thế công cụ let bằng boolean:

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

Xcode đã đề xuất điều này khi tôi sử dụng cách cho phép và không sử dụng giá trị được xác định.


2

Tại sao không sử dụng một cái gì đó như thế này

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
}

trong Swift 3.


2

Swift 4.2, Trong trường hợp của tôi, sử dụng hàm isKind.

isKind (of :) Trả về giá trị Boolean cho biết người nhận là một thể hiện của lớp đã cho hay một thể hiện của bất kỳ lớp nào kế thừa từ lớp đó.

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

Đọc thêm https://developer.apple.com/documentation/objectivec/nsobjectprotatio/1418511-iskind


1
Đó không phải là Swift. Nó là Ca cao và chỉ hoạt động khi nó hoạt động cho Mục tiêu C.
matt

1

myObject as? Stringtrả về nilnếu myObjectkhông phải là a String. Mặt khác, nó trả về a String?, do đó bạn có thể truy cập chuỗi chính nó myObject!hoặc truyền nó myObject! as Stringmột cách an toàn.


1

Swift 3:

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

if aShape.isKind(of: Circle.self) {
}

1

Chỉ vì mục đích hoàn chỉnh dựa trên câu trả lời được chấp nhận và một số câu hỏi khác:

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

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

Nhưng bạn cũng có thể ( compactMapcũng "ánh xạ" các giá trị filterkhông):

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

Và một phiên bản sử dụng 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
    }
}

Nhưng bám vào câu hỏi, để kiểm tra xem đó có phải là một mảng không (ví dụ [String]):

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

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

Hoặc nói chung hơn (xem câu trả lời câu hỏi khác này ):

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?sẽ không luôn cung cấp cho bạn kết quả mong đợi vì askhông kiểm tra xem loại dữ liệu thuộc loại cụ thể không nhưng chỉ khi loại dữ liệu có thể được chuyển đổi thành hoặc được biểu thị dưới dạng dạng loại cụ thể.

Hãy xem xét mã này chẳng hạn:

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

Mỗi loại dữ liệu phù hợp với Errorgiao thức có thể được chuyển đổi thành một NSErrorđối tượng, vì vậy điều này sẽ luôn thành công . Tuy nhiên, điều đó không có nghĩa errorlà trên thực tế đó là một NSErrorđối tượng hoặc một lớp con của nó.

Kiểm tra loại chính xác sẽ là:

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

Tuy nhiên, điều này chỉ kiểm tra loại chính xác. Nếu bạn cũng muốn bao gồm lớp con của NSError, bạn nên sử dụng:

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

0

Nếu bạn có Phản hồi như thế này:

{
  "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
}

và bạn muốn kiểm tra giá trị is_stuckedsẽ được đọc là AnyObject, tất cả những gì bạn phải làm là điều này

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

0

Nếu bạn không biết rằng bạn sẽ nhận được một mảng từ điển hoặc từ điển duy nhất trong phản hồi từ máy chủ, bạn cần kiểm tra xem kết quả có chứa một mảng hay không.
Trong trường hợp của tôi luôn nhận được một loạt từ điển ngoại trừ một lần. Vì vậy, để xử lý việc tôi đã sử dụng đoạn mã dưới đây cho swift 3.

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

Đây là? Mảng kiểm tra xem giá trị thu được có phải là mảng không (của các mục trong từ điển). Trong trường hợp khác, bạn có thể xử lý nếu đó là mục từ điển duy nhất không được giữ trong một mảng.


0

Phiên bản Swift 5.2 & Xcode: 11.3.1 (11C504)

Đây là giải pháp kiểm tra loại dữ liệu của tôi:

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

Tôi hy vọng nó sẽ giúp bạn.


Khi trả lời một câu hỏi cũ, câu trả lời của bạn sẽ hữu ích hơn nhiều cho những người dùng StackOverflow khác nếu bạn đưa vào một số ngữ cảnh để giải thích cách trả lời của bạn, đặc biệt đối với một câu hỏi đã có câu trả lời được chấp nhận. Xem: Làm thế nào để tôi viết một câu trả lời tốt .
David Buck
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.