قالب وردپرس درنا توس
Home / IOS Development / mikeash.com: Friday Q & A 2017-08-11: Swift.Unmanaged

mikeash.com: Friday Q & A 2017-08-11: Swift.Unmanaged



Friday Q & A 2017-08-11: Swift.Unmanaged

This article is also available in Estonian (translation by Johanne Teerink).

In order to work with C API, we sometimes need to convert Swift object references to and from shouters. Swift's Unmanaged struct is the default API for handling this. Today I would like to talk about what it does and how it will be used.

Overview
Getting quick references into and out of the C world includes two separate tasks.

The first task is to convert a reference to the raw byte for an invalid * or convert the raw replacement of an invalid * back to a reference. Since Swift references are implemented as pointers, this is straightforward. It's really just a matter of getting the type system to cooperate. You can do this with unsafeBitCast although I highly recommend it. If these details have changed, you will be in trouble while Unmanaged will continue to work.

The second task makes Swift's memory management work with a pointer that Swift can not see. Swift's ARC memory management requires that the compiler handles references to each item so that it can insert appropriate hold and delete calls. When the pointer is sent to C, it can no longer do this. Instead, you must manually tell the system how to handle memory management at each step. Unmanaged provides the facilities to do this.

Unmanaged breaks a reference to an item. It is possible to keep Unmanaged values ​​in the long run, but you usually use it as a short, temporary stop on or off a shout point.

From Swift to C
To send a reference from Swift to C, you must create a Wrong value from this reference and request it for a pointer. There are two ways to create a Unset value from a Swift reference. To find out what you need, you'll need to figure out your memory management requirements.

Using Unmanaged.passRetained (obj) will perform an unbalanced inventory on the object. This effectively provides ownership of any C API you transfer the pointer to. It must be balanced with a release at a later date, or the object will leak.

Using Unmanaged.passUnretained (obj) the object's inventory will count unchanged. This does not give ownership of the C API you send the pointer to. This fits when you transfer to a C API that takes ownership internally (for example, sends an item to a CFArray that will keep it) and when transferred to a C API that uses the value immediately but not hold stuck on it in the long run. If you transfer such a value to a C API that keeps the pointer in the long run and does not take ownership, your item may be distributed prematurely resulting in a crash or worse.

When you have the Unmanaged ] value, you can get a clue from it using the toOpaque method. Since you've already decided how to handle memory management, there's only one conversation here and no decisions to make at this time.

Here's an example of how to get a shout with ownership:

      la [1
9659017] ptr
= ruled . pass Retained ( obj ). toOpaque () FunctionCall ([19659021] ptr )

And here is an example without ownership:

      la    ptr    =    ] Unmanaged .   passUnretained  (  obj [19659020] )   toOpaque  () 
      FunctionCall  (  ptr ) 

In both cases is ptr and UnsafeMutableRawPointer which is Swifts equivalent to Cs void * and can be submitted to C APIs.

From C to Swift
To retrieve a reference from C to Swift code, create a ruled value from the pointer, then take a reference from it. There are two ways to make a reference: one that uses an unbalanced retention and one time that does not perform memory memory.

To create Unmanaged value, use fromOpaque . Unlike passRetained and passUnretained you must specify the generic type for Unmanaged when using fromOpaque so that the compiler knows what class you are expecting to receive.

When you have the value Unmanaged you can get a reference from it. Use takeRetainedValue will perform an unbalanced release that will balance an unbalanced retention previously performed with passRetained or by C-code that gives ownership of your code. Use takeUnretainedValue will achieve the object reference without performing any inventory or release.

Here is an example of getting a reference from a pointer while performing an unbalanced release:

      la    obj    =    ruled   <  MyClass > .   from Opaque  (  ptr ).   takeRetainedValue  () 
      Obj .   method  () 

And without inventory or release:

      la    obj    =    Unmanaged   <  MyClass  ]>  .   from Opaque  (  ptr ).   takeUnretainedValue  () 
      obj .   Method  () [19659031]   Patterns 
It is possible to use Umanaged unilaterally with C APIs that take a pointer and do things with it or return a pointer . But the most common way to use Unmanaged is with C APIs that pass around a context pointer. In this situation, you control both sides, and you must find out the memory management to avoid crashing or leaking. How you do it will depend on what you do. There are some basic patterns to use.

Synchronous Backup
Some APIs will call you back immediately with the context pointer and complete all the work before returning. In this case, you may pass the item unjustified and take it unjustified. For example, here are some code that call CFArrayApplyFunction and call a method on by yourself for each item in the group:

      la    context    =    Unmanaged .   passUnretained  (  itself ).   toOpaque  ()    la    vary    =    CFRangeMake  (  0     CFArrayGetCount  (  series  ] 
                                                        ]    =    ruled   <  MyClass > .   fromOpaque  (  context ! ).               ] 

] Context )

Bec Use Function Running instantly, and we know that even will stay alive for all the time. We do not need to make any memory on it.

Asynchronous One-Shot Reversal
Some APIs Perform a single callback later, for example to inform you that any task has been completed. In this case you will keep going in and drop in the callback. Here is an example of using CFHost to perform asynchronous DNS resolution. As a bonus, this code also contains a one-sided use of Unmanaged to retrieve the return value to CFHostCreateWithName .

It starts by creating the host. CFHostCreateWithName returns a Unmanaged presumably because it has not had any cached transition made to it. Since it returns a value that we own, we use takeRetainedValue () on it to get the underlying value:

      la    host    =    CFHostCreateWithName  (  ]     "mikeash.com"    as    CFString ).   takeRetainedValue  () 

We need a context to transfer to the host object. We ignore all fields in this context except info which is the crude pointer where we will put the pointer to itself :

      var    context    =    CFHostClientContext  () 
      context .   information    =    ruled .   passRetained  (  ]   toOpaque  () 

We make the revocation using CFHostSetClient

    : 

      CFHostSetClient  (       ]     typeInfo     error     info    i 

Here we get passed through info using Unmanaged again. Since we passed it retained, we use takeRetainedValue here to balance it. We can then use it Inner = ruled < MyClass > . fromOpaque ( information 19659019] vert )
}, ] ()
Inner Solved 9017] and Context )

This code starts the asynchronous resolution process:

     ] CFHostScheduleWithRunLoop  (  Host     CFRunLoopGetCurrent  (),    CFRunLoopMode .   commonModes .   rawValue ) [19659027]     .      null ) 

Asynchronous Multi-Shot Reversal
Finally, some APIs are recalling as they call many times later. To handle this, keep in order to make sure the item stays alive. You must not release in the callback since the first callback will destroy the object and provide subsequent callbacks with a pointer to rubbish. Instead, you must release the value later, once all the callbacks are completed. Generally, the API provides a separate callback to handle this.

Here is an example of using CFRunLoopTimer to perform a task once per second. It begins to create a context and fill out the field info as above:

      var    context    =    CFRunLoopTimerContext  () 
      context .   info    =    Unmanaged .   passRetained  (  itself ).   toOpaque  () 

This example also fills out the context's release field. In the release callback, it uses fromOpaque to get a Unmanaged for the info pointer and then call release ] to balance inventory:

      context .   release    =    {   Unmanaged   <  MyClass > .   from Opaque  (] 

] ! ). Release () }

It then creates the timer and sends the revocation:

      la    the timer    =    CFRunLoopTimerCreate  (  nil  ]   0    1    0  [19659296] 

hours info in

It collects yourself using Unmanaged .


Source link