为什么`var objCArray = array as NSArray`合法?

时间:2017-03-02 21:34:18

标签: swift

在Swift数组和NSArrays之间进行转换很容易。我找到了一个我认为不应编译的案例:

let doubleArray = [1.1, 2.22, 3.333, 4.4444, 5.55555, 
  6.666666, 7.7777777, 8.88888888, 9.999999999]

var objCArray = doubleArray as NSArray

第二行从我的Swift双精度数组中创建一个NSArray,但它将它存储在var中。这测试了编译器改变数组内容的合法性。这意味着如果你试图改变数组,你会得到一个错误,即使objCArray被声明为var

objCArray[0] = 123

为什么行var objCArray = doubleArray as NSArray合法?

4 个答案:

答案 0 :(得分:5)

let doubleArray = [1.1, 2.22, 3.333, 4.4444, 5.55555, 6.666666, 7.7777777, 8.88888888, 9.999999999]
var objCArray = doubleArray as NSArray
  

为什么行var objCArray = doubleArray为NSArray legal

因为,与其他var声明一样,它允许您替换变量的值。在这种情况下,您可以使用不同的 NSArray替换objCArray的NSArray:

objCArray = [2.9]

然而,NSArray本身是不可改变的。这只是关于这个Objective-C类的一个事实。因此,例如,您无法分配到objCArray[0]。这与letvar无关;这是关于NSArray本身是什么的事实:

 objCArray[0] = 123 // error

现在,您可能会说:这与Swift结构的工作完全不同。非常真实!但是NSArray 不是一个Swift结构。它不是Swift,它不是结构。这是一个Objective-C课程! Swift Array是一个Swift结构,你应该使用它。

进一步讨论:这个问题显然是荒谬的,我们甚至可以通过专注于Swift结构来看到这一点:

  • 如果结构引用本身是用var声明的,则允许您在Swift 中分配结构的var属性。

  • 如果使用{声明结构引用本身, 允许在Swift中分配给结构的let属性,甚至 {1}}。

  • 但这并不意味着,只是因为结构只有var属性,所以不能用let声明它!那太荒谬了。然而,荒谬与你问题中的荒谬完全相同。

    var

答案 1 :(得分:2)

这是有效的,因为var只是意味着您可以稍后在评论中指出其他内容。 NSArray是一个不可变对象。在NSArray的基金会Swift模块中,subscript仅标记为get

extension NSArray {

    // ...

    @available(iOS 6.0, *)
    open subscript(idx: Int) -> Any { get }

    // ...
}

至于为什么桥接合法,请参阅Swift Array模块中的这个注释:

/// Bridging Between Array and NSArray
/// ==================================
///
/// When you need to access APIs that expect data in an `NSArray` instance
/// instead of `Array`, use the type-cast operator (`as`) to bridge your
/// instance. For bridging to be possible, the `Element` type of your array
/// must be a class, an `@objc` protocol (a protocol imported from Objective-C
/// or marked with the `@objc` attribute), or a type that bridges to a
/// Foundation type.
///
/// The following example shows how you can bridge an `Array` instance to
/// `NSArray` to use the `write(to:atomically:)` method. In this example, the
/// `colors` array can be bridged to `NSArray` because its `String` elements
/// bridge to `NSString`. The compiler prevents bridging the `moreColors`
/// array, on the other hand, because its `Element` type is
/// `Optional<String>`, which does *not* bridge to a Foundation type.
///
///     let colors = ["periwinkle", "rose", "moss"]
///     let moreColors: [String?] = ["ochre", "pine"]
///
///     let url = NSURL(fileURLWithPath: "names.plist")
///     (colors as NSArray).write(to: url, atomically: true)
///     // true
///
///     (moreColors as NSArray).write(to: url, atomically: true)
///     // error: cannot convert value of type '[String?]' to type 'NSArray'
///
/// Bridging from `Array` to `NSArray` takes O(1) time and O(1) space if the
/// array's elements are already instances of a class or an `@objc` protocol;
/// otherwise, it takes O(*n*) time and space.
///
/// Bridging from `NSArray` to `Array` first calls the `copy(with:)`
/// (`- copyWithZone:` in Objective-C) method on the array to get an immutable
/// copy and then performs additional Swift bookkeeping work that takes O(1)
/// time. For instances of `NSArray` that are already immutable, `copy(with:)`
/// usually returns the same array in O(1) time; otherwise, the copying
/// performance is unspecified. The instances of `NSArray` and `Array` share
/// storage using the same copy-on-write optimization that is used when two
/// instances of `Array` share storage.
///
/// - Note: The `ContiguousArray` and `ArraySlice` types are not bridged;
///   instances of those types always have a contiguous block of memory as
///   their storage.
/// - SeeAlso: `ContiguousArray`, `ArraySlice`, `Sequence`, `Collection`,
///   `RangeReplaceableCollection`

答案 2 :(得分:0)

NSArray是一个不可变对象。有一个名为NSArray的{​​{1}}子类,允许您修改它包含的对象。如果您将代码更改为NSMutableArray,则可以执行您要求的操作。

答案 3 :(得分:0)

类型转换是合法的,因为Swift不可变数组(doubleArray)通过自动(免费)桥接与NSArray兼容。因此,您对objCArray的赋值为您提供了对基础值类型(即不可变)的可变引用。

从那时起,objCArray被视为引用类型。

这意味着您可以执行对象类型(NSArray)允许的任何方法,并将新对象实例分配给变量(objCArray)。

在这种情况下,NSArray类旨在操作不可变数组,因此它不允许您为下标赋值。编译器根据NSArray的下标实现检测到这一点,而不是基于引用NSArray对象的变量的可变性。

这是引用类型(NSArray)和有价值类型(Swift Array)之间的差异之一,它可以按预期工作。当应用于有价值的类型时,Swift的“let”可以防止对变量内容进行任何修改。对引用类型使用“let”可以防止更改引用变量,但允许您修改引用对象的内容。