2016-04-24 2 views
4
// Swift 
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.") 
} 

Я не могу понять результат печати.
Почему он печатает «Последний - это Int».Тип двойного номера: Int in Swift

// Swift 
import UIKit 
let arr = [1, "a", -2] 
let last = arr.last 
if last is Double { 
    print("The last is Double.") // The last is Double. 
} else { 
    print("The last is not Double.") 
} 

И эта печать «Последнее двойное». Почему?
Может ли кто-нибудь мне помочь?
Чем вы сильно отличаетесь.

ответ

10

Быстрые массивы могут содержать только один тип. Когда вы объявили:

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

Swift сделал arr типа [NSObject]. Вы можете проверить это на Вариант -clicking на arr для просмотра его типа. Это работает только потому, что у вас есть Фонд импортирован (ваш import UIKit импорт Foundation). Попробуйте удалить import UIKit.

Затем значения 1 и 2.88 были преобразованы в NSNumber и "a" к NSString так, что они могут быть сохранены в этом массиве, потому что [NSObject]Int с, String с, и Double ы не NSObjects. NSNumber и NSString являются подклассами NSObject. Swift выбирает наиболее ограничительный тип для массива. Если бы ваш массив был [1, true, 2.88], тип массива был бы [NSNumber].

Интересная вещь о NSNumber заключается в том, что это контейнер объектов, который обертывает множество разных типов. Вы можете положить Int и вынуть Double. Таким образом, это вводит в заблуждение, когда вы тестируете его с помощью is. Он отвечает «истинному» значению: «Я могу быть таким, если вы хотите».


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" 

Примечание: Если бы вы объявили о своей arr быть [Any], то это сработало бы, как вы ожидали:

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

Но Свифт не собирается создать массив типа Any для вас, если вы не спросите явно (потому что, откровенно говоря, это мерзость на строго типизированном языке). Вероятно, вам следует пересмотреть свой дизайн, если вы обнаружите, что используете [Any].

Единственная причина Swift создает [NSObject] когда Foundation импортируется, чтобы сделать нашу жизнь проще при вызове какао и Cocoa Touch API. Если для такого API требуется передать NSArray, вы можете отправить [1, "a", 2.88] вместо [NSNumber(integer: 1), NSString(string: "a"), NSNumber(double: 2.88)].

+0

> Затем значения преобразуются в 'NSNumber'. Но когда я проверяю тип переменной 'last', это' NSObject? ', А не 'NSNumber'. – pacification

+0

'last' возвращает необязательную переменную типа, хранящегося в массиве. Массив - это '[NSObject]'. Этот конкретный экземпляр содержит два 'NSNumber' и' NSString', которые являются подклассами 'NSObject'. – vacawama

+0

Кажется, что компилятор никогда не выводит типы как 'Any' или' [Any] ', поэтому единственный возможный способ вывести тип этого массива (если импортируется' Foundation') - это преобразование 'NSNumber', а затем использовать '[AnyObject]' или '[NSObject]'. Без 'Foundation' компилятор вызовет ошибку. Вы все равно можете использовать '[Any]' явно. – Sulthan

0

Проблема заключается ваш arr, если вы объявите

let arr = [1, "a", 2.88] // arr is [NSObject] 

так

let arr = [1, "a", 2.88] 
let last = arr.last // last is a NSObject? 
if last is Int { // is Int or is Double will get the same result 
    print("The last is Int.") // because the number of NSNumber is larger than the number of NSString 
} else { 
    print("The last is not Int.") 
} 

, если вы объявите, как это:

let arr = [1, "a", "b"] 
let last = arr.last 
if last is Int { 
    print("The last is Int.") 
} else { 
    print("The last is not Int.") // Will print because the number of NSString is larger than NSNumber. 
} 
0

Вы можете объявить массив с типом [Any] для получить желаемый результат:

let arr: [Any] = [1, "a", 2.88] 
arr.last is Int // false 
0

Когда вы импортировали Foundation.framework вы унаследовали определенное магическое поведение с числами: если вы создаете массив буквальный с числовыми литералами, которые не могут быть представлены с массивом этого типа значения, массив создается, что упаковывает номера в коробке в NSNumber. Если вы не импортируете Foundation (т. Е. Полагаетесь только на стандартную библиотеку Swift), вы фактически не можете объявить массив в таких случаях только с let arr = [1, "a", 2.88] (тогда как, например, let arr = [1,2] будет работать - это создаст [Int]) но нужно указать let arr:[Any] = [1, "a", 2.88] - в этом случае не происходит никакого бокса в NSNumber (NSNumber даже не доступен), и первоначальный тип каким-то образом сохраняется.

NSNumber представляет собой коробку, которая может быть использована, чтобы обернуть все манеры скалярного (числовые) основные типов, и вы можете использовать оператор as или is в Swift для успешного лечения любой NSNumber как любой из числовых типов можно обернуть. Например справедливо следующее:

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" 

Можно выяснить вид числовой тип, который вы использовали иногда, но это просто не подвергается по какой-то причине в NSNumber, но доступен только в соответствующем CoreFoundation типа 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 
    } 
} 

Вы можете узнать больше на эту тему over here - есть некоторые оговорки.

Следует также отметить, что the reference documentation также утверждает следующее:

... количество объектов не обязательно сохранять тип они созданы с ...

.

Смежные вопросы