Swift Access to Variable Length Array

Swift access to variable length array

You can create a buffer pointer that starts at the given address and has the given
number of elements:

let buffers = UnsafeBufferPointer<AudioBuffer>(start: &bufferList.memory.mBuffers,
count: Int(bufferList.memory.mNumberBuffers))

for buf in buffers {
// ...
}

Update for Swift 3 (and later):

let buffers = UnsafeBufferPointer<AudioBuffer>(start: &bufferList.pointee.mBuffers,
count: Int(bufferList.pointee.mNumberBuffers))

for buf in buffers {
// ...
}

How to access in Swift a C struct that contains a variable sized array?

Zero-length arrays in C are not visible in Swift. A possible workaround is to add a helper function in the bridging header file, which returns the address of the first array item:

static uint32_t * _Nonnull idArrayPtr(const IDListPtr _Nonnull ptr) { return &ptr->idArray[0]; }

Now you can create a “buffer pointer” in Swift which references the variable length array:

let idListPtr = getIDList()
let idArray = UnsafeBufferPointer(start: idArrayPtr(idListPtr), count: Int(idListPtr.pointee.count))
for item in idArray {
print(item)
}

Tuple-Array with variable content types & length?

A tuple is inappropriate because you cannot declare a tuple in a generic manner.

A possible solution is to declare data as an array of CustomStringConvertible instead of a tuple.

The benefit is you can pass any type which supports String Interpolation and the number of items in the array is variable.

func debugData(names: [String], data: [[CustomStringConvertible]] ) {
var debugLine = ""
for line in data {
for i in 0..<line.count {
debugLine += "\(names[i])=\(line[i]) "
}
print(debugLine)
debugLine = ""
}
}

debugData(names: ["Name", "Age", "SexM"], data: [["Alex", 5, true], ["Lisa", 7, false], ["Max", 9, true]])

Parse json file with variable length in Swift

Rather than having a CoinData struct, you can decode a dictionary with the cryptocurrencies' names as the keys, and Price objects as the values.

let coinData = try decoder.decode([String: Price].self, from: data!)

Now to get the Price for Bitcoin for example, you can just do:

if let bitcoinPrice = coinData["bitcoin"] {
// ...
} else {
// there is no price info about bitcoin in the JSON!
}

In Swift, how to get the true size of an `Any` variable?

I'll begin with some technical details about the limitations of Any in this case.

So, what is Any? It's an empty protocol to which every type implicitly conforms to.

And how does the compiler represent variables of protocol types? It's by wrapping the actual value in an existential container. So basically when you're referencing a variable of this kind, you're actually talking to the container (well, actually not you, but the compiler is :).

An existential container has a layout that can be represented like this C structure:

struct OpaqueExistentialContainer {
void *fixedSizeBuffer[3];
Metadata *type;
WitnessTable *witnessTables[NUM_WITNESS_TABLES];
};

The container elements are greatly explained in this document, I'll also try to summarize them here:

  • fixedSizeBuffer either holds the whole value, if it takes less than 24 bytes, or holds a pointer to a heap allocated zone, containing the value
  • type is a pointer to the type metadata
  • witnessTables is what makes this layout occupy various sizes, as the number of protocol witness tables can vary from zero to virtually any number of protocols.

So, with the above in mind:

  • Any needs no witness tables, thus it occupies 32 bytes
  • a single protocol variable occupies 40 byes
  • a composed protocol variable occupies 32 + N*8, where N is the number of "independent" protocols involved in the composition

Note that the above is true if there are no class protocols involved, if a class protocol is involved, then the existential container layout is a little bit simplified, this is also better described in the linked document from above.


Now, back to the problem from the question, it's the existential container created by the compiler the one which prevents you from accessing the actual type. The compiler doesn't make this structure available, and transparently translates any calls to protocol requirements to dispatches through the witness tables stored in the container.

But, might I ask you, why are you circulating Any? I assume you don't want to handle all possible and future types in a generic manner. A marker protocol might help here:

protocol MemoryLayouted { }

extension MemoryLayouted {
var memoryLayoutSize: Int { MemoryLayout.size(ofValue: self) }
}

Then all you have left to do is to add conformance for the types you want to support:

extension Int: MemoryLayouted { }
extension String: MemoryLayouted { }
extension MyAwesomeType: MemoryLayouted { }

With the above in mind, you can rewrite your initial code to something like this:

let regularInt: Int = 1
let anyInt: MemoryLayouted = 2

print(regularInt.memoryLayoutSize) // 8
print(anyInt.memoryLayoutSize) // 8

You get consistent behaviour and type safety, a type safety that might translate to a more stable application.


P.S. A hacky approach, that allows you to use Any, might pe possible by unpacking the existential container via direct memory access. The Swift ABI is stable at this point, so the existential container layout is guaranteed not to change in the future, however not recommending going that route unless absolutely necessary.

Maybe someone that stumbles this question and has experience in the ABI layout code can provide the code for it.

How to create a fixed-size array of objects

Fixed-length arrays are not yet supported. What does that actually mean? Not that you can't create an array of n many things — obviously you can just do let a = [ 1, 2, 3 ] to get an array of three Ints. It means simply that array size is not something that you can declare as type information.

If you want an array of nils, you'll first need an array of an optional type — [SKSpriteNode?], not [SKSpriteNode] — if you declare a variable of non-optional type, whether it's an array or a single value, it cannot be nil. (Also note that [SKSpriteNode?] is different from [SKSpriteNode]?... you want an array of optionals, not an optional array.)

Swift is very explicit by design about requiring that variables be initialized, because assumptions about the content of uninitialized references are one of the ways that programs in C (and some other languages) can become buggy. So, you need to explicitly ask for an [SKSpriteNode?] array that contains 64 nils:

var sprites = [SKSpriteNode?](repeating: nil, count: 64)

This actually returns a [SKSpriteNode?]?, though: an optional array of optional sprites. (A bit odd, since init(count:,repeatedValue:) shouldn't be able to return nil.) To work with the array, you'll need to unwrap it. There's a few ways to do that, but in this case I'd favor optional binding syntax:

if var sprites = [SKSpriteNode?](repeating: nil, count: 64){
sprites[0] = pawnSprite
}

Is there a way to declare an arbitrary number of arrays in Swift?

Use an array of arrays, as jnpdx says in their comment.

let ballot1 = ["candidateB", "candidateA", "candidateD"] 
let ballot2 = ["candidateC", "candidateD"]
let ballots = [ballot1, ballot2]

or just

let ballots = [["candidateB", "candidateA", "candidateD"],
["candidateC", "candidateD"]]

Then you can refer to your ballots using indexes:

ballots[0] would give you an array of candidates for the first ballot, or you could loop through the outer array:

for (index, ballot) in ballots.enumerated() {
print("Ballot \(index+1) has candidates \(ballot[index])")
}

Note that you might want to make each ballot a struct, with fields for a title, the array of candidates, and any other information you might want. Then you could have an array of ballot structs:

struct Ballot {
let title: String
let description: String? // Optional field
let candidates: [String]
}

And then:

let ballots = [Ballot(title: "5th congressional district",
description: nil,
candidates: ["Joe", "Briana", "Jamal", "Ivan"]),
Ballot(title: "Mayor",
description: nil,
candidates: ["Adrienne", "Moisha", "Dave", "Demtri"])
]

And:

for aBallot in ballots {
print("Ballot titled \(aBallot.title) has candidates \(aBallot.candidates)")

Swift use sizeof with Int32 Array

You can get the number of elements in an array simply with

let count = testArray.count

and the total number of bytes of its elements with

var arrayLength = testArray.count * sizeof(Int32)
// Swift 3:
var arrayLength = testArray.count * MemoryLayout<Int32>.size

sizeof is used with types and sizeofValue with values, so both

var arrayLength = sizeof([Int32])
var arrayLength = sizeofValue(testArray)

would compile. But that gives you the size of the struct Array, not the size
of the element storage.

Swift Global Variables

You shouldn't use global variables, I don't think that's recommended in any language.

Now here you have what looks like a Singleton class (BroadService), that's good because it's a nice solution for what you're looking for.

Next all you need to do is add a property to that class. Let's say videoLink is a string, you can add a string property to BroadService, for example storedVideoLink as an optional String, and the next time you need to obtain that value after you have already fetched it, you can access it like so: BroadService.sharedInstance.storedVideoLink.

One more thing, to have BroadService work properly as a singleton, you should make its init private.

To sum up, here's what I'm suggesting:

class BroadService {
static let sharedInstance = BroadService()
var storedVideoLink: String?

private init() {} // to ensure only this class can init itself

func fetchBroadcasts(completion: @escaping ([Games]?) -> ()) {
// your code here
}
}

// somewhere else in your code:
BroadService.sharedInstance.fetchBroadcasts { (games) in
if let games = games {
let game = games[indexPath]
let videoLink = game.videoLink
BroadService.sharedInstance.storedVideoLink = videoLink
}
}
// now you can access it from anywhere as
// BroadService.sharedInstance.storedVideoLink

This way it all stays cohesive in the same class. You can even add a getter method for storedVideoLink so you don't have to access it directly, and in this method you could state that if the string is nil then you fetch the data, store the link to the string, and then return the string.



Related Topics



Leave a reply



Submit