import UIKit

let arr = [1, "a", 2.88]
let last = arr.last
if last is Int {
    print("The last is Int.")    // The last is Int.
} else {
    print("The last is not Int.")

数组后面明明是一个Double类型,可是通过is判断,打印出的结果却是The last is Int.而且不论最后一个是Double或者是Int都会显示同样的结果.但是如果是String就不会显示.


Swift arrays can only hold one type. When you declared:

let arr = [1, "a", 2.88]

Swift made arr of type [NSObject]. You can verify this by Option-clicking on arr to see its type. This only works because you have Foundation imported (your import UIKit imports Foundation as well). Try removing import UIKit.

Then, the values 1 and 2.88 were converted to NSNumber and "a" to NSString so that they can be stored in that[NSObject]array because Ints, Strings, and Doubles are not NSObjects. NSNumber and NSString are subclasses of NSObject. Swift picks the most restrictive type for the array. Had your array been[1, true, 2.88], the array type would have been [NSNumber].

The interesting thing about an NSNumber is that it is an object container that wraps many different types. You can put an Int in and take out a Double. So, it is misleading then when you test it with is. It responds "true" meaning, "I can be that if you want".

import Foundation

let n: NSNumber = 3.14

print(n is Int)       // "true"
print(n is Double)    // "true"
print(n is Bool)      // "true"

print(n as! Int)      // "3"
print(n as! Double)   // "3.14"
print(n as! Bool)     // "true"

Note: Had you declared your arr to be [Any], then this would have worked as you expected:

let arr:[Any] = [1, "a", 2.88]
let last = arr.last
if last is Int {
    print("The last is Int.")
} else {
    print("The last is not Int.")  // "The last is not Int."

But Swift is not going to create an array of type Any for you unless you ask explicitly (because quite frankly it is an abomination in a strongly typed language). You should probably rethink your design if you find yourself using[Any].

The only reason Swift creates [NSObject] when Foundation is imported is to make our lives easier when calling Cocoa and Cocoa Touch APIs. If such an API requires an NSArray to be passed, you can send [1, "a", 2.88] instead of [NSNumber(integer: 1), NSString(string: "a"), NSNumber(double: 2.88)].


When you have imported Foundation.frameworkyou inherit a certain magic behaviour with numbers: if you create array literal with numerical literals that can't be represented with an array of that value type, an array is created that wraps the numbers boxed in a NSNumber. If you don't import Foundation (i.e. you rely on just the Swift standard library), then you in fact can't declare the array in such cases just with let arr = [1, "a", 2.88] (whereas for instance let arr =[1,2] would still work – that would produce an[Int]) but need to statelet arr:[Any] = [1, "a", 2.88]– at which case no boxing to NSNumber happens (NSNumber is not even available) and the original type is somehow retained.

NSNumber is a box that can be used to wrap all manners of scalar (numerical) basic types, and you can use the as or is operator in Swift to successfully treat any NSNumber as any of the numerical types it can wrap. For instance the following holds:

var a = NSNumber(bool: true)
print("\(a is Bool)")   // prints "true"
print("\(a is Int)")    // prints "true"
print("\(a is Double)") // prints "true"
print("\(a is Float)")  // prints "true"

It is possible to figure out the kind of numerical type you used sometimes, but it's just not exposed for some reason to NSNumber but is only available in the corresponding CoreFoundation type CFNumber:

extension NSNumber {
    var isBoolean:Bool {
        return CFNumberGetType(self as CFNumber) == CFNumberType.CharType

    var isFloatingPoint:Bool {
        return CFNumberIsFloatType(self as CFNumber)

    var isIntegral:Bool {
        return CFNumberGetType(self as CFNumber).isIntegral

extension CFNumberType {
    var isIntegral:Bool {
        let raw = self.rawValue
        return (raw >= CFNumberType.SInt8Type.rawValue && raw <= CFNumberType.SInt64Type.rawValue)
                || raw == CFNumberType.NSIntegerType.rawValue
                || raw == CFNumberType.LongType.rawValue
                || raw == CFNumberType.LongLongType.rawValue

You can read more on this topic over here – there are some caveats.

Note also that the reference documentation also states the following:

… number objects do not necessarily preserve the type they are created with …