Just got a quick array question it might be a little stupid but im kind of new to coding, what is the difference between the code here:
var items:[String]
so from my understanding here you are defining the variable 'items' as an empty array string. items is of type array
var items: [String] = ()
here you are defining items as an empty array but shouldn't it written as:
var items = [String]()
or is this essentially the same code
This:
var items:[String]
means that you are declaring a variable named items that will be an array of String instances. However, you are not initializing that variable to any initial value. The compiler will not let you use this variable before it is initialized with a value, because you are not declaring the type ([String]) to be optional ([String]?), so it must be initialized and contain a non-nil value to be used by your code.
This:
var items: [String] = ()
means that you are declaring a variable named items that should be an array of String instances, but you are trying to initialized it with the value (), which in Swift is synonymous with Void. The compiler will not allow this. A similar, valid notation would be: var items: [String] = [] which uses an empty array of unspecified type ([]) to initialize the value. Since you declared items to be an array of String values, the compiler can infer that the untyped empty array ([]) should be an empty array of String values ([String]) and will allow it.
This:
var items = [String]()
is declaring a variable named items that you aren't explicitly specifying the type for, and immediately initializing it with an empty array of String values. From this, the compiler can infer the type of the variable is [String] so you don't need to declare it.
var items = [String]() is called initializer syntax. It means you're allocating memory for the future storage of an array of strings.
However, by doing var items:[String] you are not providing an initializer. This will cause an error--consider conceptually that you are pointing to an area of memory that you have not allocated space for. There's nowhere for additional variables to go!
On the other hand, var items: [String] = () doesn't seem to be any kind of standard syntax. It seems like you're casting an array of strings as void. This shouldn't compile.
Related
I'm currently working on introspection in Swift 2 and have Problems getting the specific type for an Array (in this example an Array<String>).
var prop = obj.valueForKey("strings")!
if prop is Array<String> {
println("true")
}
if prop is Array<Int> {
println("true")
}
Output is:
true
true
while it should be
true
false
Is there a way to find out the type for the members of the Array? For example, if I daclared the Array as Array<String> I want to get String or at least be able to check if it is.
MirrorType also did not lead to any success on that by now.
There are 2 ways to achieve what you want:
if prop.dynamicType == Array<Int>.self (or [Int].self) which is better than if prop.dynamicType == [Int]().dynamicType { because [Int]() creates an unused instance of "array of integers".
Typically, when you check if an array is specific-typed, you plan to use it
in a certain way (as a result, you will likely cast your array to
[Int]). Having that said, I recommend using if let arrayOfInts =
prop as? Array<Int> {. Using this construct, you will check for
type compatibility and prepare your array to be treated in a special way (using the casted arrayOfInts reference).
Anyway, it's up to you to decide what to do.
Perhaps what you want is the type of each individual item inside the Array rather than the type of the Array itself? If you are using collection types in Swift, all the items stored in the Array (or Dictionary) are of the same type (except if you declare the Array as Array for example to break the rules... which is not usually necessary, or wanted).
By declaring an Array with its initially values you are automatically telling the compiler what type they are. If you do something like this:
let obj = [1,2,3]
var property = obj[0]
if property is String {
print("true")
}
if property is Int {
print("true")
}
The compiler will already tell you that property is String always fails, and there is actually no need to do that test (because we already know that it will always fail).
If you are working with Objective-C APIs and types on the other hand, there may be occasions where you will need to test for type, this is a good example of testing for type in an Objective-C collection that has items of different types:
let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
print("\(date.timeIntervalSinceReferenceDate)")
}
Hope this helps.
I have an array of type [String]
let names = ["Joffrey", "Cersei", "Mountain", "Hound"]
I have a function which takes an array of [Any] type.
func printItems(items: [Any]){
for item in items {
print(item)
}
}
Now when I call the function with names as parameters,
printItems(names)
I get an error Cannot invoke 'printItems' with an argument list of type '([String])'.
Any is just a typealias for a protocol which all types implicitly conform to.
Thoughts?
This is a surprising limitation of Swift. You can't cast an array to type [Any] so you can't pass it to a function taking type [Any]. You can use map to cast each item of the array:
printItems(names.map {$0 as Any})
But, the right way to do this in Swift is to use Generics:
func printItems<T>(items: [T]) {
for item in items {
print(item)
}
}
let names = ["Joffrey", "Cersei", "Mountain", "Hound"]
let numbers = [3.1416, 2.71818, 1.4142, 1.618034]
printItems(names) // This now works
printItems(numbers) // This works too
While every type conforms Any, this is not the same as it being a universal implicit superclass that all types inherit from.
When you cast a type to a protocol, you create a new value with a different structure. So for a string to be of type Any, it needs to be physically transformed from the String representation:
sizeof(String) // 24 bytes (on 64-bit, anyway)
to the Any representation:
sizeof(Any) // 32 bytes, includes some meta data
// about what the type really is
Since value types are held directly in the array, the array would be a very different shape so under the hood the compiler would have to do the equivalent of this:
names.map { $0 as Any } // create a new array, with the Any versions
Swift could perhaps automate this process for you (it does if you pass a single variable into a function that takes Any). But personally I’m glad it doesn’t, I’d rather this be more explicit – suppose your array was huge, this would be a lot of processing happening implicitly under the hood.
This is different from when you have an array of reference types, all of which are pointers to the actual data and so all the same size, and which need no transformation when upcasting:
class C { }
class D: C { }
let d = D()
let c: C = d
unsafeBitCast(d, UnsafePointer<Void>.self) // these value will
unsafeBitCast(c, UnsafePointer<Void>.self) // be the same
So saying “this array of [D] is really an array of [C]” is just a matter of the compiler agreeing the types can be substituted, no data transformation needs to take place:
// so this works fine,
// no runtime transformation needed:
func f(cs: [C]) { }
let ds = [D(),D()]
f(ds)
But protocols still are different from superclass references when used with classes:
protocol P { }
extension C: P { }
sizeofValue(C()) // 8 bytes (just a pointer)
sizeofValue(C() as P) // 40 bytes
func g(ps: [P]) { }
g(ds) // won’t compile, needs transformation
You need to explicitly put in your let declaration that you
are declaring an Any array and not a specific type array.
With your current declaration, Swift will evaluate it to [String] array.
and not as [Any] array.
To fix your problem just do the following:
let names : [Any] = ["Joffrey", "Cersei", "Mountain", "Hound"]
If you want to retain your let declaration, use AnyObject
in your printItems function and it will be accepted by Swift.
func printItems(items: [AnyObject]){
for item in items {
print(item)
}
}
This question already has answers here:
Can't assign an string to an array element inside my Swift function
(2 answers)
Closed 7 years ago.
I am trying to create a method that takes an array of CGPoint optionals, and assigns each of them to nil. I am getting an error "Cannot assign a value of type nil to a value of type CGPoint?". I am confused because I thought that optionals could either be nil or some type of specified object. Thanks for any help (:
func resetArray(arr: [CGPoint?])
{
for index in 0...arr.count-1
{
arr[index] = nil
}
}
When a parameter is passed to a function, it is immutable, so you are not allowed to change it. For arrays, being value types, that means you cannot use any mutating method or otherwise perform any action that changes the value.
You can make the parameter writeable by prefixing it with the inout modifier:
func resetArray(inout arr: [CGPoint?])
^^^^^
Note that, when invoking the function, the parameter must be passed by prefixing it with the & ampersand character:
var array = [...]
resetArray(&array)
^
You can also make a parameter modifiable, but with the changes visible from within the function body only (which means changes occur on a copy of the original parameter passed to the function) - that can be done by using the var modifier:
func resetArray(var arr: [CGPoint?])
^^^
I am constructing an array of booleans to store the state of the sections in a UICollectionView. It is a variable stored as a property of my UIViewController:
var _weekSelections : Array<Bool>!
Then, in a function called by loadView(), I construct the array and assign a value to the first index:
_weekSelections = Array<Bool>(count:_weekCount, repeatedValue:false)
_weekSelections[0] = true
The value at index 0 remains false! The array is constructed, and has multiple elements, but any assignment that I make to an index does not affect the value stored at that index, even if I check the value on the very next line of code. I know that Swift makes a copy of an array if I perform an action that may change its length, but I don't think this is a case where a copy would me made. The only way I can get any value to change is if I manually create a copy as follows:
var copy = _weekSelections
copy[0] = true
_weekSelections = copy
Am I missing something obvious or could this be a strange bug?
For the sake of having my code on SO rather than Pastebin, here's my observation. This looks like some kind of bug or unexpected behaviour when using an optional array in a Swift class derived from an Objective C class. If you use a plain Swift class, this works as expected:
class Foo {
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
foo.test()
However, if you derive Foo from NSObject:
import Foundation
class Foo : NSObject { // This derivation is the only difference from the code above
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "false"
}
}
var foo = Foo()
foo.test()
Even in this case, if you do your weekSelections initialisation in an initialiser, then it works:
class Foo : NSObject {
var weekSelections: Array<Bool>!
init() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
Personally, I'd say that this is a bug. I can't see anything in any documentation that would explain the difference in behaviour when derived from NSObject.
I also can't see anything that says that optional array properties would be immutable. This would be especially strange when you consider that "immutable" arrays are actually mutable in Swift, i.e. this:
// Use "let" to declare an "immutable" array
let weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]); // Prints "true"; arrays are never really "immutable" in Swift
...works fine, and is currently documented as being valid, even if it seems a bit odd.
Personally, I'd use whatever workaround you can and raise a bug with Apple, to see what light they can shed.
Not an explanation, but a workaround. The issue isn't with the count:repeatedValue initializer, but rather with the array being assigned to an optional variable. From what I can tell optional arrays can only use accessor methods and not mutator methods--effectively they're immutable. Temporarily assigning _weekSelections to a non-optional variable before attempting to change its contents (and assigning back to _weekSelections when done) will work. Please note that this seems to create a new array (with the same elements) on assignments, so there may be memory issues to consider if the array is very big. Of course, simply using a non-optional variable in the first place will also work.
As for why optional arrays aren't mutable, that may be a bug or there may be some esoteric reason for it I'm not fathoming. Anyone else have a plausible-sounding theory?
I'm trying to code a type to represent a pointer in GPU device, and it should act like an array, with indexed property for get and set. I get no problem if the element type is a primitive type, but when I use a struct, I cannot change its member's value.
See this code:
#nowarn "9"
open System
open System.Runtime.InteropServices
[<Struct;StructLayout(LayoutKind.Sequential)>]
type MyStruct =
val mutable x : int
val mutable y : int
val mutable z : int
override this.ToString() = sprintf "(%d,%d,%d)" this.x this.y this.z
let deviceOnly() = failwith "this function should be used in quotation only"
type DevicePtr<'T>(h:nativeint) =
member this.Handle = h
member this.Item with get (idx:int) : 'T = deviceOnly() and set (idx:int) (value:'T) : unit = deviceOnly()
member this.Reinterpret<'T2>() = DevicePtr<'T2>(h)
member this.Volatile() = DevicePtr<'T>(h)
static member (+) (ptr:DevicePtr<'T>, offset:int) = DevicePtr<'T>(ptr.Handle + nativeint(offset * sizeof<'T>))
let test() =
let mutable test1 = MyStruct()
test1.x <- 1
let foo (collection:MyStruct[]) =
collection.[0].x <- 1
let bar (collection:DevicePtr<MyStruct>) =
collection.[0].x <- 1
//error FS0257:
// Invalid mutation of a constant expression.
// Consider copying the expression to a mutable local, e.g. 'let mutable x = ...'.
So, the type is DevicePtr<'T>, and it has indexed property Item with both get and set method. but the get method just return a value of 'T, so I cannot mutate it. But the system array works.
Anyone has some experience like this? to create a type works like array? which I hope the get function of the indexed property return a mutable ref instead of a value.
You cannot create a type that works with structures like an array because the language and runtime give special handling to arrays that other classes do not get.
With an array, an expression accessing an element that is a structure, as in your foo function, causes the element to be directly modified in place. The collection.[0] is effectively setting up a "pointer" or "reference" to an element that the .x expression is then applied to, allowing you to manipulate the object in place.
For other classes, the indexer is just another function, meaning a copy of the value is being returned. So in the bar function, the collection.[0] creates a copy of the value returned instead of the reference you get from the array. Because the .x would be modifying the temporary copy, the compiler issues the error you see. (A very similar warning would occur in C# or VB for similar code.) As the message suggests, you will need to create a variable to hold the copy, modify the copy, and assign it back to collection.[0].