2017-03-02 35 views
3

在Swift數組和NSArrays之間進行投影很容易。我發現,我不認爲應該編譯的情況下:爲什麼`var objCArray = array as NSArray` legal?

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 

第二行從我雙打的斯威夫特陣列創建一個NSArray,但它存儲在var。這測試編譯器,它是合法的更改數組的內容。這意味着,如果你試圖再變異的數組,你會得到一個錯誤,即使objCArray聲明爲var

objCArray[0] = 123 

爲什麼線var objCArray = doubleArray as NSArray是否合法?

+2

'var objCArray'只是意味着您可以稍後爲其指定其他內容,例如, 'var objCArray = someOtherDoubleArray as NSArray' –

+4

爲什麼它應該是非法的? 'var objCArray'定義了一個可變指針,你可以改變它所持有的地址。它指向的對象是否可變是另一回事 –

回答

0

NSArray是一個不可變的對象。有一個NSArray的子類叫做NSMutableArray,它允許你修改它包含的對象。如果您將代碼更改爲var objCArray = doubleArray as NSMutableArray,您將可以執行您的要求。

+0

這並不回答「爲什麼行'var objCArray = doubleArray作爲NSArray'合法嗎?」 – JAL

+0

@JAL它不回答直接問題「爲什麼它是合法的」,但它確實回答了「我爲什麼在說'objCArray [0] = 2'時爲什麼會出錯」的基本問題。 – creeperspeak

+0

從技術上來說,答案就是'NSArray'的'subscript'只有'get',請參閱我的答案。 – JAL

2

這是有效的,因爲var只是意味着您可以稍後分配別的東西,正如Martin在評論中所說的。 NSArray是一個不可變的對象。在基金會斯威夫特模塊NSArraysubscript被標記爲只get

extension NSArray { 

    // ... 

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

    // ... 
} 

至於爲什麼是彌合法律,見本說明雨燕陣列模塊:

/// 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` 
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 as NSArray legal

因爲像其他var聲明一樣,它允許您替換變量的值。在這種情況下,你將被允許使用不同的NSArray更換的NSArray在objCArray

objCArray = [2.9] 

儘管如此,NSArray的本身是不可變的。這僅僅是這個Objective-C類的一個事實。因此,例如,您不能指定objCArray[0]。這與letvar無關;這是一個什麼一個NSArray本身是一個事實:

objCArray[0] = 123 // error 

現在,你可能會說:這是從斯威夫特結構的工作原理完全不同。非常真實!但NSArray是而不是一個Swift結構。它不是Swift,它不是一個結構。這是一個Objective-C類! Swift數組是Swift結構,您應該使用它。


進一步討論:這個問題,因爲它的立場,顯然是荒謬的,我們甚至可以完全集中在斯威夫特結構看到這一點:

  • 你被允許分配給var Swift中結構體的屬性只有如果結構體引用本身聲明爲var

  • 您是允許分配給斯威夫特一個結構的let財產,即使如果的結構基準本身與var聲明。

  • 但是,這並不意味着,僅僅因爲結構只有let屬性,它不能用var聲明!那將是荒謬的。然而,這種荒謬與你的問題中的荒謬完全相同。

    struct S { 
        let name = "Matt" 
    } 
    var s = S() // should this be illegal... 
    // ... just because assigning to S.name is illegal??? 
    
+0

如果我們將該行更改爲'var objCArray = doubleArray',該怎麼辦?我們能夠改變'objCArray',還是會給我們一個運行時錯誤,因爲我們會試圖改變初始的不可變數組的值? – halileohalilei

+0

@halileohalilei試試看看。 (當你這樣做的時候,看看'objCArray'的推斷類型。) – matt

+0

所以如果有人想知道,我嘗試了它,第二個數組是這種情況下第一個數組的**副本**。第二個的推斷類型也與第一個數組相同。 – halileohalilei

0

的類型轉換是合法的,因爲夫特不可改變陣列(的doubleArray)是通過自動(免費電話)與橋接的NSArray兼容。所以你對objCArray的賦值給你一個可變的對一個底層賦值類型的引用(這是不可變的)。

從此,objCArray被視爲引用類型。

這意味着您可以執行對象類型(NSArray)將允許的任何方法,並將新的對象實例分配給變量(objCArray)。

在這種情況下,NSArray類被設計爲操作不可變數組,因此它不會讓您將值分配給下標。編譯器根據NSArray的下標實現來檢測它,而不是基於引用NSArray對象的變量的可變性。

這是引用類型(NSArray)和值類型(Swift Array)之間的差異之一,它按預期工作。 Swift的「let」應用於有價值的類型時,可以防止對變量內容進行任何修改。使用引用類型的「let」可以防止更改引用變量,但可以修改引用對象的內容。