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.
Related
I'm basically trying to wrap a C structure within a Swift class.
The Swift class has an instance property of type of the C structure.
The C structure contains several properties which are of type const char *.
In order to assign values to the structure, I wrote getters and setters for each property:
public class MyClass: NSObject {
private var configuration = c_structure_config()
}
// Function of MyClass
// f.ex. on "registrationUri:"
private(set) public var registrationUri: String {
get {
return String(cString: configuration.reg_uri)
}
set {
if (configuration.reg_uri != nil) {
configuration.reg_uri.deallocate()
}
let maxLength = newValue.count + 1 // +1 for NULL
var buffer: [CChar] = [CChar](UnsafeMutableBufferPointer<Int8>.allocate(capacity: maxLength))
guard newValue.getCString(&buffer, maxLength: maxLength, encoding: .utf8) == true else {
fatalError("Could not allocate memory for Account Config reg uri")
}
// "configuration" is the instance property (see above)
// reg_uri is of type char const *
configuration.reg_uri = UnsafePointer<Int8>(buffer)
}
}
However, this approach leads to weird crashes and error reports complaining about pointers overlapping range (Fatal error: UnsafeMutablePointer.initialize overlapping range).
I know that I'm deallocating and allocating memory whenever the string is set and that that's probably not very efficient. I haven't found a better solution so far though.
What's wrong here (or is this right, I made a wrong assumption and I gotta search somewhere else)?
There are a couple of problems with your solution.
First, it is ill-advised to use String.count to count to get the size of the underlying C buffer. The reason is that String.count returns the number of actual characters of your string, and not the number of bytes used to represent it. Not all characters fit the 256 bits of Int8 (a.k.a. CChar). Hence, your code will probably crash if you attempt setting your property with non-ascii characters. It is better to use the String.utf8CString property, which returns a contiguous array of CChar.
Second, since your buffer is allocated within the setter, it will be deallocated when your setter returns (Swift arrays are instances of value types, who's lifetime is bound by the stack). That means the pointer corresponding to buffer's base is actually invalidated when your setter returns. I suspect this is the reason why the error you reported occurs at runtime.
Finally, please do not test for true in guards and if statements.
Here is a corrected version:
var registrationUri: String {
get {
return String(cString: reg_uri)
}
set {
if (reg_uri != nil) {
reg_uri.deallocate()
}
newValue.withCString { cString in
// No need to add 1, newValue.utf8CString already has the correct buffer capacity.
let capacity = newValue.utf8CString.count
let buffer: UnsafeMutablePointer<Int8> = .allocate(capacity: capacity)
buffer.assign(from: cString, count: capacity)
reg_uri = UnsafePointer(buffer)
}
}
}
// ...
myInstance.registrationUri = "こんいちは"
print(myInstance.registrationUri)
// Prints "こんいちは"
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
}
I have the following C struct:
typedef struct {
char** categories;
int category_size;
} category_fmc_s_type;
My Swift array has the following values:
let categories = ["Weekday", "Weekend"]
I want to populate the C Struct field 'categories' with 'Weekday' & 'Weekend'. To do this I call my toPointer():
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
I keep getting the following XCode 8 error:
Cannot convert value of type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>>' to expected argument type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!'
Any suggestions? I don't understand why there is the optional and '!' in the C-Struct definition implicitly.
As the compiler emits as an error, you need to unwrap after Int8 w/ "?" as follows.
fileprivate func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
then,
func testMyCat() {
let categories = ["Weekday", "Weekend"]
let buffer = toPointer(categories)
var mycat = category_fmc_s_type()
mycat.categories = buffer // you would see compile error w/o "?"
}
the code above works w/o error. Martin's solution gives a compile error at
mycat.categories = &cargs (see the link)
I don't know why.
Check the reference of utf8String property of NSString:
Discussion
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the
C string if it needs to be stored outside of the memory context in
which you use this property.
The term memory context is not well-defined, but one thing sure is that you cannot expect the allocated region for the C string would live forever. When the member categories in the category_fmc_s_type is accessed, the pointers may be pointing to the already freed regions.
Applying the suggestion from Martin R to your code, your code would be like this:
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
buffer.initialize(from: args.lazy.map{strdup($0)})
return buffer
}
And remember, after you finish using the category_fmc_s_type, you need to deallocate the regions allocated by strdup(_:) and UnsafeMutablePointer.allocate(capacity:):
fileprivate static func freePointer(_ pointers: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>, count: Int) {
for i in 0..<count {
free(pointers[i])
}
pointers.deinitialize(count: count)
pointers.deallocate(capacity: count)
}
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)
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]