Switch using array not running? - arrays

For some reason my program refuses to run no matter what I do. Right now the code below gives me error
EXC_BAD_INSTRUCTION (code=EXC_BAD_i386_INVOP, subcode=0x0)
the code:
var array = [String]()
array[0] = "lmao"
switch array[0] {
case "lmao":
print("fug")
default:
print("fugger")
}

You are not allocating any space in the array - it is empty, so accessing element 0 causes an error. Use array.append("lmao") rather than array[0] = "lmao".
Alternatively, if you know exactly how many elements you need (say, 10) use
var array = [String](repeating: "", count: 10)
as your declaration, then you can directly set array[0] through to array[9]. You'll still get an error accessing array[10], because the array does not have 11 members...

You should use append method to add a value into your array:
array.append("lmao")
It would be a good idea to take some minutes to read Apple's documentation on swift array. It will help you to understand how things like memory allocations and mutability works.
Here is a fully working example:
var str = "Hello, playground"
var array = [String]()
array.append("lmao")
switch array[0] {
case "lmao":
print("fug")
default:
print("fugger")
}
(Which displays fug)

Related

Swift Allocate Array of Pointers

How can I use UnsafeMutablePointer<T?> as UnsafeMutablePointer<UnsafeRawPointer?>!
e.g. Trying to allocate n blocks of memory for type T, to get values from a CFSet cfsetref:
var array = UnsafeMutableRawPointer<T?>.allocate(capacity: n)
CFSetGetValues(cfsetref, array) // error
Which gives the error
Cannot convert value of type 'UnsafeMutablePointer<T?>' to expected
argument type 'UnsafeMutablePointer<UnsafeRawPointer?>!'
I tried declaring array as UnsafeMutablePointer<UnsafeRawPointer?> then doing
for i in 0..<n {
var d = array[i]
d?.bindMemory(to: T.self, capacity: 1)
}
But I still get EXC_BAD_INSTRUCTION errors when attempting to access array[i] (after binding the memory again to T)
Many things depends on how you get your cfsetref and what actually is T.
But anyway, CFSetGetValues expects UnsafeMutablePointer<UnsafeRawPointer?>! as shown in the error message.
let n = CFSetGetCount(cfsetref)
let array = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: n)
array.initialize(to: nil, count: n)
CFSetGetValues(cfsetref, array)
To safely access the contents of array, you need to know how T is managed by Swift ARC. For example, assuming T is NSNumber, you need to tell Swift ARC to manage the result with writing something like this:
let managedArray = UnsafeMutableBufferPointer(start: array, count: n).map{Unmanaged<NSNumber>.fromOpaque($0!).takeRetainedValue()}
print(managedArray)
But as well as other CF-collection types, the better way to handle CFSet is bridging it to Swift Set:
if let swiftSet = cfsetref as? Set<NSNumber> {
let swiftArray = Array(swiftSet)
print(swiftArray)
}
How do you get your cfsetref and what actually is T? With such information, I would try to tell you what sort of code you need to write in your actual case.

2D arrays: Index out of range

I'm pretty new to Swift, and I'm trying to store my data as a 2D AnyObject array. I have declared var internalData = [[AnyObject]]() and a struct like so:
struct WiFiData {
var latency: Double
var duration: Double
}
Now, in another function, I would like to switch on the instance variable currentExit, which is an enum:
private var currentExit = Exit.A
enum Exit {
case A
case B
case C
case NotSelected
}
func someFunc() {
switch self.currentExit {
case .A:
self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) // <- CRASHES ON THIS LINE
......// other cases
}
However, it always crashes on the line self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) with the following error:
fatal error: Index out of range
Can anyone tell me why and how I could fix it? Any help is appreciated, thanks!
You instantiated a 2d array with [[AnyObject]](), but it's still empty, meaning there's nothing at self.internalData[0] for you to append to. There are a lot of things you could do to handle this, but if you know you're going to have 3 arrays inside self.internalData you may as well instantiate each internal array like so:
self.internalData = [[AnyObject](), [AnyObject](), [AnyObject]()]
Now you do have an empty array at self.internalData[0] that you can append items to.
It seems, though, like if you already know you're going to have 3 arrays of WiFiData it'd be even better just to create 3 variables:
var a = [WiFiData]()
var b = [WiFiData]()
etc.
and then you can access the individual arrays by name.
There are no "2D arrays" in Swift. There are arrays of arrays, which are different. For example, there is no promise that each row will have the same number of columns in an array of arrays.
In your case, the problem is that internalData has no elements in it. There has to be an internalData[0] element already created before you can append to it. You're assignment of var internalData = [[AnyObject]]() means there's no element 0. It's an empty array (of arrays).
Also keep in mind that the type [[AnyObject]] is very likely to cause a lot of problems for you. AnyObject is useful for working with Cocoa APIs, but generally creates a lot of headaches and should very rarely be part of a Swift property. You should almost certainly create some more specific type to hold your data. It's unclear from your example what you expect internalData to hold. Your code suggests, though, that you mean something more like:
var internalData: [Exit: [ExitRecord]] = [:]
enum Exit {
case A
case B
case C
case NotSelected
}
enum ExitRecord {
case wifi(latency: Float, duration: Float)
}
func someFunc() {
switch self.currentExit {
case .A:
var currentRecords = internalData[.A] ?? []
currentRecords.append(.wifi(latency: 1.5, duration: 4.0))
internalData[.A] = currentRecords
......// other cases
}

Swift array as c function buffer

I'm using swift array as output buffer for a function that takes a pointer and fills it, like this:
var buffer : [Int32] = ...
tehFillFunc(UnsafeMutablePointer<Int32>(buffer))
This works fine, the problem is that compiler is complaining that Variable 'buffer' was never mutated; consider changing to 'let' constant, which I don't want to do as I'm pretty sure it was mutated in my fill function.
So, is there a way to silence it? (I could just do some dummy set, but I'd prefer to do it properly).
Edit: As requested complete example code that shows the problem (c is not even necessary):
class ViewController: UIViewController {
func fill(sth: UnsafeMutablePointer<Int32>) {
sth[0] = 7
}
override func viewDidLoad() {
super.viewDidLoad()
var array = [Int32].init(count: 10, repeatedValue: 0)
fill(UnsafeMutablePointer<Int32>(array))
print("\(array)")
}
}
However, the solution was already posted. In simplest form:
fill(&array)
Usually you need to have specify buffer size. And in this case I prefer following solution:
let bufferSize = 1000
var buffer = [Int32](count: bufferSize, repeatedValue: 0)
tehFillFunc(&buffer)
There's a method on Array for this purpose:
var buffer : [Int32] = ...
buffer.withUnsafeMutableBufferPointer {
tehFillFunc($0.baseAddress)
}
I makes sure the array is alive at least until withUnsafeMutableBufferPointer returns. As it takes an inout parameter, buffer has to be a var.

Storing a reference to array in swift

I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]

Swift Generic Array 'not identical' error

I'm just going through some Swift tuts that are obviously already outdated as of Beta3 ...
func exchange<T>(data:[T], i:Int, j:Int)
{
let temp = data[i];
data[i] = data[j];
data[j] = temp;
}
Playgrounds tells me:
Error: #lvalue $T8 is not identical to T.
How do I change it to make it work?
Arrays in Swift are value types. That means that data is copied when passed into your exchange method, but you are trying to modify the copy to affect the original version. Instead you should do one of two things:
1. Define data as an inout parameter:
func exchange<T>(inout data:[T], i:Int, j:Int)
Then when calling it you have to add an & before the call:
var myArray = ["first", "second"]
exchange(&myArray, 0, 1)
2. Return a copy of the Array (recommended)
func exchange<T>(data:[T], i:Int, j:Int) -> [T]
{
var newData = data
newData[i] = data[j]
newData[j] = data[i]
return newData
}
I recommend this way over the in-out parameter because in-out parameters create more complicated state. You have two variables pointing to and potentially manipulating the same piece of memory. What if exchange decided to do its work on a separate thread? There is also a reason that Apple decided to make arrays value types, using in-out subverts that. Finally, returning a copy is much closer to Functional Programming which is a promising direction that Swift can move. The less state we have in our apps, the fewer bugs we will create (in general).

Resources