📜 ⬆️ ⬇️

Unsafe to Swift

The creators of modern programming languages ​​are trying with all their might to divert programmers from working directly with pointers and memory, either by not including such features (for example, Java) in the language or by marking them with unsafe (C #) scary words. Lucky writers on swift, this language fell into the second category, and although this is not recommended, there are warnings in the documentation about possible memory leaks and other horror stories!

In this article I would like to consider the work with pointers in the swift, as well as to speculate on why this may be needed at all.

And so, the first question is why?

In most cases, programming on swift under iOS does not have to work with pointers; moreover, most programmers have never worked with pointers explicitly. Nevertheless, such a low-level code in some cases makes it possible to optimize a program either by speed or by the amount of memory used. Coma addition of such a possibility is interesting from an academic point of view.
')
How?

Typical work with pointers consists of three steps: allocating memory, working with allocated space, and freeing memory.

For example:

let p = UnsafeMutablePointer<Int>.alloc(1) p.initialize(20) p.destroy(1) p.dealloc(1) 

From a swift point of view, a pointer is a generic of the appropriate type (in this case, integer)
Here we allocate memory for the Int type, initialize it with a value of 20, and release it.

However, messing with one integer value in this way is not particularly useful, another thing is if we need a temporary array:

 let a = UnsafeMutablePointer<Int32>.alloc(N) memset(a, 0, sizeof(Int32) * N) 

Here we allocated memory for the array “a”, with the type of Int32 elements of size N and initialized it by setting the values ​​of sizeof (Int32) * N bytes to 0.

Now, copy the array, command:

 var b = a 

in contrast to the swift arrays, it is not the data itself that copies the arrays, but a pointer to them. Data can be copied like this:

 let b = UnsafeMutablePointer<Int32>.alloc(N) memcpy(b, a, sizeof(Int32) * N) 

Iteration over such an array can be done quite simply:

 for var i = 0; i < N; i++ { a[i] = <...> } 

And you can use math pointers:

 var p = a while p < a + N { p.memory = <...> p++ } 

(Adding an integer to the pointer, we shift the address to which it points to the corresponding number of elements)

However, quite a boring theory, we will try to solve some problem using pointers.
For example, we get the size of a picture from a GIF file.

For this we need a test file and a description of the format header:
OffsetLengthContents
03 bytes"GIF"
33 bytes"87a" or "89a"
62 bytesWidth
eight2 bytesHeight
.........

First, read the GIF file and get a pointer to the beginning of the sequence of bytes read:

 let fileName = NSBundle.mainBundle().pathForResource("SomeGif", ofType: "gif")! let data = NSData(contentsOfFile: fileName)! guard data.length > 10 else { return } var p = UnsafeMutablePointer<Void>(data.bytes) 

Thus, the "p" pointer refers to the beginning of the buffer with data from the GIF file, we also checked that the buffer size is more than 10 bytes (that's what we are going to read).

According to the description of the format, the first 3 bytes in the header should be the string “GIF”, to check, create a Swift line based on the first three bytes from the buffer and check:

 let str = String(bytesNoCopy: p, length: 3, encoding: NSASCIIStringEncoding, freeWhenDone: false) guard str == "GIF" else { return } 

Thus, we create a Swift string interpreting the first 3 bytes to which the pointer p refers to, as a character set in ASCII, the most remarkable thing is that the data from the buffer itself is not copied!

Further, if the check was successful, read the size of the image that interests us. For this we use the structure of two 16 bit integers:

 struct GifSize { var width: UInt16 var height: UInt16 } 

According to the format, you need to shift from the beginning of the file to 6 bytes (the first three of which we previously interpreted as a string) and interpret the following 4 bytes as two 16-bit numbers:

 p += 6 let pSize = UnsafeMutablePointer<GifSize>(p) NSLog("Gif width = \(pSize.memory.width), height = \(pSize.memory.height)") 

Thus, we got the size of the image in the GIF file by reading its title without third-party libraries using pointers!

(Generally speaking, with a similar use of structures in swift there are a number of pitfalls and limitations, but they are worthy of a separate note)

Source: https://habr.com/ru/post/276005/


All Articles