In Swift: Difference Between Array VS Nsarray VS [Anyobject]

in Swift: Difference between Array VS NSArray VS [AnyObject]

Array is a struct, therefore it is a value type in Swift.
NSArray is an immutable Objective C class, therefore it is a reference type in Swift and it is bridged to Array<AnyObject>.
NSMutableArray is the mutable subclass of NSArray.

var arr : NSMutableArray = ["Pencil", "Eraser", "Notebook"]
var barr = ["Pencil", "Eraser", "Notebook"]

func foo (var a : Array<String>)
{
a[2] = "Pen"
}

func bar (a : NSMutableArray)
{
a[2] = "Pen"
}

foo(barr)
bar(arr)

println (arr)
println (barr)

Prints:

(
Pencil,
Eraser,
Pen
)
[Pencil, Eraser, Notebook]

Because foo changes the local value of a and bar changes the reference.
It will also work if you do let arr instead of var as with other reference types.

[AnyObject] is not identical to NSArray

Found answer to both of my questions -

  1. Since NSArray is immutable and [AnyObject] is Array type in Swift that's mutable, compiler complains they are not identical. I changed NSArray! to Array! in Swift code. Other option is was to change Objective-C code to success:(void (^)(NSMutableArray *response))success (not preferred as it kills the purpose of using the bridge).

  2. Cast the Array to NSMuatableArray before calling the block in Objective-C

Why is a Swift ArrayInt compatible with AnyObject?

If you import Foundation (which you must be, because you reference NSCoder) then [Int] is implicitly bridged to NSArray because Int is implicitly bridged to NSNumber. Int64 and your non-objc structs are not implicitly bridged to ObjC types, so arrays of those are not bridged to NSArray.

Why casting function type as AnyObject works

Behind the scenes as AnyObject converts the casted value to an Objective-C compatible one: Int's become NSNumber, array's become NSArray, and so on. Swift-only values get wrapped within opaque SwiftValue instances.

print(type(of: test2)) // __SwiftValue

This is why adding as AnyObject makes your code compile, as the right-hand of the operator is now an object.

Swift why does as? require AnyObject vs Any

I guess this doesn't really answer the original question. However, it is a workaround that gets the job done. I still don't understand why the solution above doesn't work (especially the fatal error when casting from [AnyObject] to [Any]), but I took a different approach which works great:

extension NSArray {
public func toSwiftArray<Type>() -> [Type] {
var swiftArray = [Type]()

for value in self {
if let valueOfType = value as? Type {
swiftArray.append( valueOfType )
}
}

return swiftArray
}

public func toSwiftArray<Type>() -> ([Type], [Any]) {
var swiftTypeArray = [Type]()
var unknownTypeArray = [Any]()

for value in self {
if let valueOfType = value as? Type {
swiftTypeArray.append( valueOfType )
} else {
unknownTypeArray.append( value )
}
}

return (swiftTypeArray, unknownTypeArray)
}
}

Not sure why I couldn't use .filter to do this, but this is a very straightforward solution to the problem, and it also allows for a version that returns a list of the values that couldn't be converted. This is very handy routine for converting to NSArray with full type safety.

AnyObject and Any in Swift

AnyObject is only for reference types (classes), Any is for both value and reference types.

So you should go for [String: Any].

Type Casting for Any and AnyObject

Swift provides two special types for working with nonspecific types:

  • Any can represent an instance of any type at all, including function
    types.
  • AnyObject can represent an instance of any class type.

NOTE:

Use Any and AnyObject only when you explicitly need the behavior and
capabilities they provide. It is always better to be specific about
the types you expect to work with in your code.

From The Swift Programming Language:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342

-

Also note that when you work with Cocoa API, it's common to receive an Array of AnyObject, this is because Objective-C arrays are NOT typified.
So you need to cast them to the array type you expect.

-

EDIT: (december 22, 2015)

On the last statement, note that this is changing with Swift 2.0 and Xcode 7.

Apple has introduced ‘Lightweight’ generics in Objective-C so lots of Cocoa APIs now already returns the correct type.

EDIT: (october 18, 2016)

Note that, as of Swift 3.0, Objective-C ids are now imported as Any, not anymore as AnyObject.

How can this [AnyObject] return as AnyObject?

So why no any warning or error issue?

There would have been if you hadn't of said as AnyObject:

class Brain {
var internalProgram = [AnyObject]()
var program: AnyObject {
get {
// compiler error:
// Return expression of type '[AnyObject]' does not conform to 'AnyObject'
return internalProgram
}
}
}

The compiler is telling us that [AnyObject] doesn't conform to AnyObject – which is perfectly true. A Swift Array is a struct, not a class, therefore cannot directly be typed as an AnyObject.

However, you then say as AnyObject. By doing so, you're bridging the Swift Array to NSArray (when Foundation is imported) – which is a class. Therefore it can be typed as an AnyObject. You can see the full list of Foundation types which can be bridged to here.

Furthermore it's worth noting that in Swift 3, everything can be bridged to AnyObject due to the introduction of the opaque _SwiftValue type, which can wrap an arbitrary Swift value in an Obj-C compatible box (including Array when Foundation isn't imported).

Because anything can now be an AnyObject, it's pretty much as weak a type as Any. On top of this, it also lets you call any known @objc method on it, completely disregarding type safety and is almost guaranteed to cause you problems with _SwiftValue boxing. For those reasons, you should avoid using AnyObject wherever you can. There is nearly always a stronger type available for you to use.



Related Topics



Leave a reply



Submit