当前位置:网站首页>Pointer usage in swift

Pointer usage in swift

2020-12-07 16:22:18 Jack who writes bugs on IOS

Original author : Wang Wei (onevcat)
Link to the original text : https://onevcat.com/2015/01/s...

Apple Expect in Swift The middle pointer can minimize the chance of appearing , So in Swift The pointer is mapped to a generic type , And it's more abstract . This, to a certain extent, results in Swift The difficulty of using the pointer in , Especially for those who are not familiar with pointers , Developers who don't have much experience with pointer manipulation ( Including myself ) Come on , stay Swift Using pointers in is a real challenge . In this article , I want to start with the most basic use , To sum up, it's in Swift Some common ways and scenarios of using pointers in . This article assumes that you at least know what a pointer is , If the concept of the pointer itself is not clear , You can read this first Five minutes C Pointer tutorial  ( Or its Chinese version ), It should be very helpful .

preliminary

stay Swift in , Pointers are represented by a special type , That's it  UnsafePointer<T>. Followed Cocoa The principle of immutability ,UnsafePointer<T>  It's also immutable . Of course, correspondingly , It also has a variant ,UnsafeMutablePointer<T>. Most of the time ,C Pointers in both types are introduced to Swift in :C in const Decorated pointer correspondence  UnsafePointer ( The most common one should be C A string of  const char *  了 ), Other mutable pointers correspond to  UnsafeMutablePointer. besides ,Swift There are... That represent a set of continuous data pointers in  UnsafeBufferPointer<T>, An opaque pointer to a nonholonomic structure  COpaquePointer  wait . In addition, you may have noticed , It can be determined that the type of pointer to the content is generic struct, We can use this generic to constrain the type that the pointer points to to to provide some security .

For one  UnsafePointer<T>  type , We can go through  pointee  Property takes its value , If the pointer is mutable  UnsafeMutablePointer<T>  type , We can also go through  pointee  Assign it a value . For example, if we want to write a counter that uses pointers to operate memory directly , You can do that :

func incrementor(ptr: UnsafeMutablePointer<Int>) {
    ptr.pointee += 1
}

var a = 10
incrementor(&a)

a  // 11

Here and C The pointer to is similar to , We can do this by adding & The symbol can pass a pointer to the variable to a method that accepts the pointer as an argument . Above incrementor We do it directly through pointee Property changes what the pointer points to .

Similar to this is the use of Swift Of inout keyword . We're passing variables into inout Parameter function , Also use & The symbol represents the address . But the difference is that we don't have to deal with pointer types inside the body of the function , Instead, you can operate on the parameters directly .

func incrementor1(inout num: Int) {
    num += 1
}

var b = 10
incrementor1(&b)

b  // 11

although & The meaning expressed in parameter passing and C In the same , It's some one “ The address of the variable ”, But in Swift We can't get one directly from this symbol UnsafePointer Example . We need to pay attention to this and C Somewhat different :

//  Can't compile 
let a = 100
let b = &a

Pointer initialization and memory management
stay Swift Can't get the address of the existing object directly in , We can still create new UnsafeMutablePointer object . And Swift Automatic memory management is different for other objects in , For pointer Management , We need to release the memory manually . One UnsafeMutablePointer There are three possible states of memory for :

  • Memory is not allocated , That means it's a null The pointer , Or it's been released before
  • Memory is allocated , But the value has not yet been initialized
  • Memory is allocated , And the value has been initialized

Only the pointer in the third state can be used normally .UnsafeMutablePointer Initialization method of (init) It's all about converting from other types to UnsafeMutablePointer The job of . If we want to create a new pointer , What needs to be done is to use allocate(capacity:) This class method . The method is based on the parameters capacity: Int Apply to the system capacity The number of memory corresponding to the generic type . The following code applies for a Int Size of memory , And return a pointer to this memory :

var intPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
// "UnsafeMutablePointer(0x7FD3A8E00060)"

The next thing to do is to initialize the contents of this pointer , We can use initialize(to:) Method to complete initialization :.

intPtr.initialize(to: 10)
// intPtr.pointee  by  10

After initialization , We can go through pointee To manipulate the memory value pointed to by the pointer .

After use , We'd better release what the pointer points to and the pointer itself as soon as possible . And initialize: Paired deinitialize: Used to destroy the object pointed to by the pointer , And with the allocate(capacity:) Corresponding deallocate(capacity:) Used to release the memory previously requested . They should all be paired :

intPtr.deinitialize()
intPtr.deallocate(capacity: 1)
intPtr = nil
Notice that here, actually, for Int This is in C It is mapped to int Of “ Trivial value ” Come on ,deinitialize It's not necessary , Because these values are assigned to constant segments . But for objects like classes or struct instances , If you don't guarantee initialization and destruction of pairing , There will be a memory leak . So without special consideration , No matter what's in memory , Guarantee initialize: and deinitialize Pairing can be a good habit .

Pointer to array

stay Swift Pass an array as an argument to C API when ,Swift Has helped us complete the conversion , This is in Apple Of The official blog There's a good example of this :

import Accelerate

let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]

vDSP_vadd(a, 1, b, 1, &result, 1, 4)

// result now contains [1.5, 2.25, 3.125, 4.0625]

For general acceptance const Array of C API, The type of requirement is UnsafePointer, Instead of const The array corresponding to UnsafeMutablePointer. When using , about const Parameters of , We will directly Swift Array passed in ( In the previous example a and b); And for mutable arrays , Add in front & You can pass it in later ( In the previous example result).

For Chuan Shen ,Swift simplified , It is very convenient to use . But if we want to use pointers like before pointee The way to directly operate the array , You need a special type of :UnsafeMutableBufferPointer.Buffer Pointer It's a pointer to a continuous piece of memory , It is usually used to express a collection type such as an array or a dictionary .

var array = [1, 2, 3, 4, 5]
var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count)     // baseAddress  Is the pointer to the first element , The type is  UnsafeMutablePointer<Int>     if let basePtr = arrayPtr.baseAddress { 
    print(basePtr.pointee)  // 1
    basePtr.pointee = 10
    print(basePtr.pointee) // 10
    
    // The next element 
    let nextPtr = basePtr.successor()
    print(nextPtr.pointee) // 2
}

Pointer manipulation and conversion
withUnsafePointer / withUnsafeMutablePointer
As we said above , stay Swift It can't be like C Use it like that & The symbol gets the address directly for operation . If we want to pointer a variable , We can use withUnsafePointer or withUnsafeMutablePointer These two auxiliary methods . These two methods take two parameters , The first is inout Any type of , The second is a closure .Swift The first input is converted to a pointer , And then convert this into Unsafe As a parameter , To call the closure .withUnsafePointer or withUnsafeMutablePointer The difference of the former is that the converted pointer is immutable , After the latter conversion, the pointer can be changed . It's probably like this :

var test = 10
test = withUnsafeMutablePointer(to: &test, { (ptr: UnsafeMutablePointer<Int>) -> Int in
    ptr.pointee += 1
    return ptr.pointee
})

test // 11

In fact, we did what we did at the beginning of the article incrementor The same thing , The difference is that you don't need to call a method to convert a value to a pointer . The benefits of doing this are obvious for pointer operations that only perform once , Can be “ We just want to do something about this pointer ” This intention is expressed more clearly .

unsafeBitCast
unsafeBitCast It's a very dangerous operation , It will force a pointer to the memory bit by bit to the type of the target . Because this conversion is in Swift In addition to the type management of , So the compiler can't make sure that the resulting type is really correct , You have to know exactly what you're doing . such as :

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

because NSArray It can be stored at will NSObject Object's , When we are using CFArrayGetValueAtIndex When you take a value from it , The result will be a UnsafePointer<Void>. Because we know very well that it contains String object , So you can cast it directly to CFString.

About unsafeBitCast A more common usage scenario is the conversion between different types of pointers . Because the size of the pointer itself is fixed , So there is no fatal problem with pointer type conversion . This is with some C API Collaboration is very common . For example, there are many C API The required input is void *, Corresponding to Swift In Chinese, it means UnsafePointer<Void>. We can convert any pointer to UnsafePointer.

var count = 100
let voidPtr = withUnsafePointer(to: &count, { (a: UnsafePointer<Int>) -> UnsafePointer<Void> in
    return unsafeBitCast(a, to: UnsafePointer<Void>.self)
})
// voidPtr  yes  UnsafePointer<Void>. amount to  C  Medium  void *

//  Convert back to  UnsafePointer<Int>
let intPtr = unsafeBitCast(voidPtr, to: UnsafePointer<Int>.self)
intPtr.pointee //100

summary
Swift From the design point of view, safety is an important principle , Although it may be a little wordy , But I still want to reiterate that in Swift Direct use and manipulation of pointers should be used as a last resort , They can't always be safe . From traditional C Code and seamless Objective-C Code migration to Swift It's not a small project , Our code base is bound to have some and C A place for collaboration . We can of course choose to use Swift Rewrite some old code , But for things like security or performance critical , We may as well continue to use C API There is no choice but . If we want to continue to use those API Words , Learn some basic Swift Knowledge of pointer manipulation and usage can be helpful .

For new code , Avoid using Unsafe Type at the beginning , It means you can avoid a lot of unnecessary trouble .Swift The biggest benefit to developers is that we can use more advanced programming ideas , Faster and more focused development . Only with respect for this idea , We can better enjoy the advantages of this new language . obviously , This idea does not include the use of everywhere UnsafePointer Of :)

版权声明
本文为[Jack who writes bugs on IOS]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201207162005532a.html