Pointer usage in Swift

Source: Internet
Author: User

Pointer usage in Swift

Apple expects pointers in Swift to minimize their chances of appearance. Therefore, pointers in Swift are mapped to a generic type and abstract. To some extent, this makes it difficult to use pointers in Swift, especially for developers who are not familiar with pointers and have little experience in Pointer operations (including myself, using pointers in Swift is indeed a challenge. In this article, I hope to summarize some common methods and scenarios for using pointers in Swift from the basic usage. This article assumes that you know at least what the pointer is. If you are not clear about the concept of the pointer itself, you can take a look at this five-minute C pointer tutorial (or its Chinese version ), it should be helpful.

Preliminary

In Swift, pointers are represented by a special type, that is, UnsafePointer <T>. The UnsafePointer <T> is also immutable by following the Cocoa principle. Of course, it also has a variable, UnsafeMutablePointer <T>. For most of the time, pointers in C will be introduced to Swift using these two types: in C, the const modified pointer corresponds to UnsafePointer (the most common should be the const char * of the C string), while other variable pointers correspond to UnsafeMutablePointer. In addition, Swift has an UnsafeBufferPointer <T> that represents a set of continuous data pointers, and an opaque COpaquePointer that represents an incomplete structure. In addition, you may have noticed that the pointer type pointing to the content is generic struct. We can use this generic type to constrain the type pointing to the pointer to provide certain security.

For an UnsafePointer <T> type, we can use the memory attribute to value it. If this pointer is of the variable UnsafeMutablePointer <T> type, we can also assign values to it through memory. For example, if we want to write a counter that uses pointers to directly operate on memory, we can do this:

 
 
  1. func incrementor(ptr: UnsafeMutablePointer<Int>) { 
  2.     ptr.memory += 1 
  3.  
  4. var a = 10 
  5. incrementor(&a) 
  6.  
  7. a  // 11 

The pointer in this example is similar to the pointer in C. By adding the & symbol before the variable name, we can pass the pointer pointing to this variable to the method that accepts the pointer as a parameter. In the above incrementor, we changed the content pointed to by the pointer by directly operating the memory attribute.

Similar to this method, the inout keyword of Swift is used. When we pass variables into the inout Parameter Function, we also use the & symbol to represent the address. However, the difference is that in the function body, we do not need to process the pointer type, but can directly operate on the parameters.

 
 
  1. func incrementor1(inout num: Int) { 
  2.     num += 1 
  3.  
  4. var b = 10 
  5. incrementor1(&b) 
  6.  
  7. b  // 11 

Although the meaning expressed during parameter transfer is the same as that in C, it is a "variable address", but in Swift, we cannot directly obtain an UnsafePointer instance through this symbol. Note that this is different from C:

 
 
  1. // Unable to compile
  2. Run a = 100
  3. Let B = &

Pointer initialization and Memory Management

In Swift, the address of an existing object cannot be obtained directly. We can still create a new UnsafeMutablePointer object. Unlike Automatic Memory Management for other objects in Swift, manual memory application and release are required for pointer management. The memory of an UnsafeMutablePointer may be in three states:

  • Memory is not allocated, which means this is a null pointer or has been released before
  • Memory has been allocated, but the value has not been initialized
  • The memory is allocated and the value has been initialized.

Only the pointer in the third State can be used properly. The initialization method (init) of UnsafeMutablePointer is to convert from other types to UnsafeMutablePointer. If we want to create a new pointer, we need to use alloc: This class method. This method accepts a num: Int as the parameter. It will apply to the system for the memory of the corresponding generic type for the number of num. The following code applies for an Int-sized memory and returns a pointer to this memory:

 
 
  1. var intPtr = UnsafeMutablePointer<Int>.alloc(1) 
  2. // "UnsafeMutablePointer(0x7FD3A8E00060)" 

Next we should initialize the content of this pointer. We can use the initialize: Method to complete initialization:

 
 
  1. IntPtr. initialize (10)
  2. // IntPtr. memory is 10

After initialization, we can use memory to operate the memory value pointed to by the pointer.

After use, we 'd better release the content and pointer itself pointed to by the pointer as soon as possible. With initialize: The destroy pair is used to destroy the object pointed to by the pointer, And the dealloc corresponding to alloc: is used to release the previously applied memory. They should all be paired:

 
 
  1. intPtr.destroy() 
  2. intPtr.dealloc(1) 
  3. intPtr = nil 

Note that destroy is not necessary for 'ordinary value' mapped to 'int' in C such as 'int', because these values are allocated to the constant segment. However, for an object or struct instance of a class, memory leakage may occur if the initialization and destruction of pairing are not guaranteed. So there is no special consideration, no matter what the memory is, it is a good habit to ensure initialize: pairing with destroy.

Pointer to array

When an array is passed as a parameter to c api in Swift, Swift has helped us complete the conversion. This is a good example in Apple's official blog:

 
 
  1. import Accelerate 
  2.  
  3. let a: [Float] = [1, 2, 3, 4] 
  4. let b: [Float] = [0.5, 0.25, 0.125, 0.0625] 
  5. var result: [Float] = [0, 0, 0, 0] 
  6.  
  7. vDSP_vadd(a, 1, b, 1, &result, 1, 4) 
  8.  
  9. // result now contains [1.5, 2.25, 3.125, 4.0625] 

For C APIs that generally accept the const array, the required type is UnsafePointer, while the non-const array corresponds to UnsafeMutablePointer. For the const parameter, we directly pass the Swift array (a and B in the above example). For the variable array, add & in front and then pass in (result in the above example ).

Swift simplifies parameter passing and makes it easy to use. However, if we want to use pointers to directly operate arrays in memory mode, we need to use a special type: UnsafeMutableBufferPointer. Buffer Pointer is a continuous memory Pointer, which is usually used to express Collection types such as arrays or dictionaries.

 
 
  1. Var array = [1, 2, 3, 4, 5]
  2. Var arrayPtr = UnsafeMutableBufferPointer <Int> (start: & array, count: array. count)
  3. // BaseAddress is the pointer of the first element.
  4. Var basePtr = arrayPtr. baseAddress as UnsafeMutablePointer <Int>
  5.  
  6. BasePtr. memory // 1
  7. BasePtr. memory = 10
  8. BasePtr. memory // 10
  9.  
  10. // The next element
  11. Var nextPtr = basePtr. successor ()
  12. NextPtr. memory // 2

Pointer operation and conversion

WithUnsafePointer

As we have said above, in Swift, you cannot use the & symbol to directly obtain the address as in C. If we want to perform pointer operations on a variable, we can use the auxiliary method withUnsafePointer. This method accepts two parameters. The first is any inout type, and the second is a closure. Swift converts the first input to a pointer, and then uses the converted Unsafe pointer as a parameter to call the closure. It looks like this:

 
 
  1. var test = 10 
  2. test = withUnsafeMutablePointer(&test, { (ptr: UnsafeMutablePointer<Int>) -> Int in 
  3.     ptr.memory += 1 
  4.     return ptr.memory 
  5. }) 
  6.  
  7. test // 11 

Here we actually did the same thing as the incrementor at the beginning of the article. The difference is that we do not need to call a method to convert the value to a pointer. The benefits of doing so are obvious for those pointer operations that only execute once. The intention of "we just want to do something about this pointer" can be clearer and clearer.

UnsafeBitCast

UnsafeBitCast is a very dangerous operation that forcibly converts the memory to the target type pointed to by a pointer. Because this type of conversion is performed outside of Swift type management, the compiler cannot ensure that the obtained type is correct. You must be clear about what you are doing. For example:

 
 
  1. let arr = NSArray(object: "meow") 
  2. let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), CFString.self) 
  3. str // “meow” 

Because NSArray can store any NSObject object, when we use CFArrayGetValueAtIndex to take the value from it, the result will be an UnsafePointer <Void>. Because we understand that the object contains a String, we can directly convert it to a CFString.

A more common use case of unsafeBitCast is conversion between different types of pointers. Because the pointer itself occupies a certain size, conversion of pointer types will not cause any fatal problems. This is common in collaboration with some C APIs. For example, many C APIs require the input of void *, which corresponds to UnsafePointer <Void> in Swift. We can convert any pointer to UnsafePointer in the following way.

 
 
  1. Var count = 100
  2. Var voidPtr = withUnsafePointer (& count, {(a: UnsafePointer <Int>)-> UnsafePointer <Void> in
  3. Return unsafeBitCast (a, UnsafePointer <Void>. self)
  4. })
  5. // VoidPtr is UnsafePointer <Void>. Equivalent to void * in C *
  6.  
  7. // Convert back to UnsafePointer <Int>
  8. Var intPtr = unsafeBitCast (voidPtr, UnsafePointer <Int>. self)
  9. IntPtr. memory // 100

Summary

Swift is designed with security as an important principle. Although it may be a bit wordy, it is still necessary to reiterate that the direct use and operation pointer in Swift should be the final means, they are always insecure. Migrating from the traditional C code and the Objective-C code that works seamlessly with it to Swift is not a small project. Our code library will certainly see some areas of collaboration with C from time to time. Of course we can choose to use Swift to rewrite some outdated code, but for a security or performance-critical part, we may have no choice but to continue using the c api. If we want to continue using those APIs, it will be helpful to know some basic Swift pointer operations and usage knowledge.

For new code, try to avoid the type starting with Unsafe, which means you can avoid a lot of unnecessary troubles. The biggest benefit Swift brings to developers is that it allows us to use more advanced programming ideas for faster and more focused development. Only by respecting this idea can we better enjoy the advantages brought by this new language. Obviously, this idea does not include the use of UnsafePointer everywhere :)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.