Как API-интерфейсы завершения Objective-C становятся доступными через async-await?

В UIKit есть несколько старых добрых API-интерфейсов на основе завершения, например вот этот:

func open(
    _ url: URL,
    options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:],
    completionHandler completion: (@MainActor @Sendable (Bool) -> Void)? = nil
)

Но также, с появлением Swift Structured Concurrency, они добавили async версии этих же методов:

func open(
    _ url: URL, 
    options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]
) async -> Bool

Об этом преобразовании существует полная документация, и в ней говорится, что привязки async/await для таких методов генерируются автоматически по фиксированному определенному алгоритму.

Но меня интересует следующее - как именно происходит преобразование?

Я предполагаю, что он использует один из двух возможных подходов:

  • буквально просто оберните звонок в продолжение
  • какой-то другой сложный магический способ

Итак, какой из них они используют, как они это делают?

Проверяя SIL, я вижу, что он использует какой-то шаблон продолжения, но не общедоступные withXXXContinuation API. Хотя это та же основная идея.

Sweeper 23.08.2024 11:36

Можете ли вы опубликовать свои выводы в ответе с фактическим уровнем SIL?

Isaaс Weisberg 23.08.2024 12:07

Я не писал ответ, потому что было непонятно, чего вы ожидаете в ответе. Я опубликую ответ, когда вернусь.

Sweeper 23.08.2024 12:15

Ничего особенного. Swift с самого начала создавал код совместимости с Objective-C. Например, преобразование параметров и аргументов из NSString/NSArray/NSDictionary в String/Array/Dictionary и наоборот. Затем появилась возможность преобразовывать функцию, принимающую NSError**, в выбрасывающую функции. Итак, теперь он просто выполняет ту же работу: преобразует какой-то неудобный API Objective-C в какой-то удобный API Swift.

Cy-4AH 23.08.2024 13:06

@ Cy-4AH Cy-4AH, мой вопрос о том, «как» это сделано, а не просто о базовом синопсисе.

Isaaс Weisberg 23.08.2024 13:42

И я сказал, как: компилятор Swift генерирует оболочки совместимости Objective-C.

Cy-4AH 23.08.2024 14:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По сути, он просто использует продолжение, точно так же, как вы соединяете API-интерфейсы обработчика завершения с Swift Concurrency с помощью withXXXContinuation { ... }. Сгенерированный SIL мало чем отличается.

Я написал два таких файла:

// foo.h
#import <Foundation/Foundation.h>

@interface SimpleClass : NSObject
 - (void)presentWithCompletion:(void (^)(BOOL success))completion;
@end
// main.swift
let foo = SimpleClass()
print(await foo.present())

и скомпилирован в SIL с помощью swiftc -emit-sil -import-objc-header foo.h main.swift.

Результат

sil_stage canonical

import Builtin
import Swift
import SwiftShims

@MainActor @_hasStorage @_hasInitialValue let foo: SimpleClass { get }

// foo
sil_global hidden [let] @$s4main3fooSo11SimpleClassCvp : $SimpleClass

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  // function_ref async_Main
  %2 = function_ref @async_Main : $@convention(thin) @async () -> () // user: %8
  %3 = integer_literal $Builtin.Int64, 2048       // user: %4
  %4 = struct $Int (%3 : $Builtin.Int64)          // user: %10
  %5 = metatype $@thick ().Type                   // user: %6
  %6 = init_existential_metatype %5 : $@thick ().Type, $@thick any Any.Type // user: %10
  // function_ref thunk for @escaping @convention(thin) @async () -> ()
  %7 = function_ref @$sIetH_yts5Error_pIegHrzo_TR : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error any Error) // user: %8
  %8 = partial_apply [callee_guaranteed] %7(%2) : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error any Error) // user: %9
  %9 = convert_function %8 : $@async @callee_guaranteed () -> (@out (), @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <()> // user: %10
  %10 = builtin "createAsyncTask"<()>(%4 : $Int, %6 : $@thick any Any.Type, %9 : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) // user: %11
  %11 = tuple_extract %10 : $(Builtin.NativeObject, Builtin.RawPointer), 0 // user: %13
  // function_ref swift_job_run
  %12 = function_ref @swift_job_run : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> () // user: %17
  %13 = builtin "convertTaskToJob"(%11 : $Builtin.NativeObject) : $Builtin.Job // user: %14
  %14 = struct $UnownedJob (%13 : $Builtin.Job)   // user: %17
  %15 = builtin "buildMainActorExecutorRef"() : $Builtin.Executor // user: %16
  %16 = struct $UnownedSerialExecutor (%15 : $Builtin.Executor) // user: %17
  %17 = apply %12(%14, %16) : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> ()
  // function_ref swift_task_asyncMainDrainQueue
  %18 = function_ref @swift_task_asyncMainDrainQueue : $@convention(thin) () -> Never // user: %19
  %19 = apply %18() : $@convention(thin) () -> Never
  unreachable                                     // id: %20
} // end sil function 'main'

// async_Main
sil private @async_Main : $@convention(thin) @async () -> () {
bb0:
  %0 = builtin "buildMainActorExecutorRef"() : $Builtin.Executor // user: %1
  %1 = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, %0 : $Builtin.Executor // user: %28
  alloc_global @$s4main3fooSo11SimpleClassCvp     // id: %2
  %3 = global_addr @$s4main3fooSo11SimpleClassCvp : $*SimpleClass // users: %15, %7
  %4 = metatype $@thick SimpleClass.Type          // user: %6
  // function_ref SimpleClass.__allocating_init()
  %5 = function_ref @$sSo11SimpleClassCABycfC : $@convention(method) (@thick SimpleClass.Type) -> @owned SimpleClass // user: %6
  %6 = apply %5(%4) : $@convention(method) (@thick SimpleClass.Type) -> @owned SimpleClass // user: %7
  store %6 to %3 : $*SimpleClass                  // id: %7
  %8 = integer_literal $Builtin.Word, 1           // user: %10
  // function_ref _allocateUninitializedArray<A>(_:)
  %9 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %10
  %10 = apply %9<Any>(%8) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %12, %11
  %11 = tuple_extract %10 : $(Array<Any>, Builtin.RawPointer), 0 // user: %33
  %12 = tuple_extract %10 : $(Array<Any>, Builtin.RawPointer), 1 // user: %13
  %13 = pointer_to_address %12 : $Builtin.RawPointer to [strict] $*Any // user: %30
  %14 = alloc_stack $Bool                         // users: %29, %34, %17
  %15 = load %3 : $*SimpleClass                   // users: %16, %25
  %16 = objc_method %15 : $SimpleClass, #SimpleClass.present!foreign : (SimpleClass) -> () async -> Bool, $@convention(objc_method) (Optional<@convention(block) (Bool) -> ()>, SimpleClass) -> () // user: %25
  %17 = get_async_continuation_addr Bool, %14 : $*Bool // users: %27, %18
  %18 = struct $UnsafeContinuation<Bool, Never> (%17 : $Builtin.RawUnsafeContinuation) // user: %21
  %19 = alloc_stack $@block_storage UnsafeContinuation<Bool, Never> // users: %26, %23, %20
  %20 = project_block_storage %19 : $*@block_storage UnsafeContinuation<Bool, Never> // user: %21
  store %18 to %20 : $*UnsafeContinuation<Bool, Never> // id: %21
  // function_ref @objc completion handler block implementation for @escaping @callee_unowned @convention(block) (@unowned Bool) -> () with result type Bool
  %22 = function_ref @$sSbIeyBy_SbTz_ : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Bool, Never>, Bool) -> () // user: %23
  %23 = init_block_storage_header %19 : $*@block_storage UnsafeContinuation<Bool, Never>, invoke %22 : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Bool, Never>, Bool) -> (), type $@convention(block) (Bool) -> () // user: %24
  %24 = enum $Optional<@convention(block) (Bool) -> ()>, #Optional.some!enumelt, %23 : $@convention(block) (Bool) -> () // user: %25
  %25 = apply %16(%24, %15) : $@convention(objc_method) (Optional<@convention(block) (Bool) -> ()>, SimpleClass) -> ()
  dealloc_stack %19 : $*@block_storage UnsafeContinuation<Bool, Never> // id: %26
  await_async_continuation %17 : $Builtin.RawUnsafeContinuation, resume bb1 // id: %27

bb1:                                              // Preds: bb0
  hop_to_executor %1 : $Optional<Builtin.Executor> // id: %28
  %29 = load %14 : $*Bool                         // user: %31
  %30 = init_existential_addr %13 : $*Any, $Bool  // user: %31
  store %29 to %30 : $*Bool                       // id: %31
  // function_ref _finalizeUninitializedArray<A>(_:)
  %32 = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> // user: %33
  %33 = apply %32<Any>(%11) : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> // users: %43, %40
  dealloc_stack %14 : $*Bool                      // id: %34
  // function_ref default argument 1 of print(_:separator:terminator:)
  %35 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %36
  %36 = apply %35() : $@convention(thin) () -> @owned String // users: %42, %40
  // function_ref default argument 2 of print(_:separator:terminator:)
  %37 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %38
  %38 = apply %37() : $@convention(thin) () -> @owned String // users: %41, %40
  // function_ref print(_:separator:terminator:)
  %39 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %40
  %40 = apply %39(%33, %36, %38) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
  release_value %38 : $String                     // id: %41
  release_value %36 : $String                     // id: %42
  release_value %33 : $Array<Any>                 // id: %43
  %44 = integer_literal $Builtin.Int32, 0         // user: %45
  %45 = struct $Int32 (%44 : $Builtin.Int32)      // user: %47
  // function_ref exit
  %46 = function_ref @exit : $@convention(c) (Int32) -> Never // user: %47
  %47 = apply %46(%45) : $@convention(c) (Int32) -> Never
  unreachable                                     // id: %48
} // end sil function 'async_Main'

// thunk for @escaping @convention(thin) @async () -> ()
sil shared [transparent] [reabstraction_thunk] @$sIetH_yts5Error_pIegHrzo_TR : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error any Error) {
// %1                                             // user: %2
bb0(%0 : $*(), %1 : $@convention(thin) @async () -> ()):
  %2 = apply %1() : $@convention(thin) @async () -> ()
  %3 = tuple ()                                   // user: %4
  return %3 : $()                                 // id: %4
} // end sil function '$sIetH_yts5Error_pIegHrzo_TR'

// swift_job_run
sil [available 12.0.0] @swift_job_run : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> ()

// swift_task_asyncMainDrainQueue
sil [available 12.0.0] @swift_task_asyncMainDrainQueue : $@convention(thin) () -> Never

// SimpleClass.__allocating_init()
sil shared @$sSo11SimpleClassCABycfC : $@convention(method) (@thick SimpleClass.Type) -> @owned SimpleClass {
// %0 "$metatype"                                 // user: %1
bb0(%0 : $@thick SimpleClass.Type):
  %1 = thick_to_objc_metatype %0 : $@thick SimpleClass.Type to $@objc_metatype SimpleClass.Type // user: %2
  %2 = alloc_ref_dynamic [objc] %1 : $@objc_metatype SimpleClass.Type, $SimpleClass // user: %4
  // function_ref @nonobjc SimpleClass.init()
  %3 = function_ref @$sSo11SimpleClassCABycfcTO : $@convention(method) (@owned SimpleClass) -> @owned SimpleClass // user: %4
  %4 = apply %3(%2) : $@convention(method) (@owned SimpleClass) -> @owned SimpleClass // user: %5
  return %4 : $SimpleClass                        // id: %5
} // end sil function '$sSo11SimpleClassCABycfC'

// _allocateUninitializedArray<A>(_:)
sil [always_inline] [_semantics "array.uninitialized_intrinsic"] @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer)

// @objc completion handler block implementation for @escaping @callee_unowned @convention(block) (@unowned Bool) -> () with result type Bool
sil shared [transparent] [thunk] @$sSbIeyBy_SbTz_ : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Bool, Never>, Bool) -> () {
// %0                                             // user: %2
// %1                                             // user: %5
bb0(%0 : $*@block_storage UnsafeContinuation<Bool, Never>, %1 : $Bool):
  %2 = project_block_storage %0 : $*@block_storage UnsafeContinuation<Bool, Never> // user: %3
  %3 = load %2 : $*UnsafeContinuation<Bool, Never> // user: %7
  %4 = alloc_stack $Bool                          // users: %5, %8, %7
  store %1 to %4 : $*Bool                         // id: %5
  // function_ref _resumeUnsafeContinuation<A>(_:_:)
  %6 = function_ref @$ss25_resumeUnsafeContinuationyySccyxs5NeverOG_xntlF : $@convention(thin) <τ_0_0> (UnsafeContinuation<τ_0_0, Never>, @in τ_0_0) -> () // user: %7
  %7 = apply %6<Bool>(%3, %4) : $@convention(thin) <τ_0_0> (UnsafeContinuation<τ_0_0, Never>, @in τ_0_0) -> ()
  dealloc_stack %4 : $*Bool                       // id: %8
  return undef : $()                              // id: %9
} // end sil function '$sSbIeyBy_SbTz_'

// _resumeUnsafeContinuation<A>(_:_:)
sil shared [available 12.0.0] @$ss25_resumeUnsafeContinuationyySccyxs5NeverOG_xntlF : $@convention(thin) <T> (UnsafeContinuation<T, Never>, @in T) -> () {
// %0                                             // user: %3
// %1                                             // user: %3
bb0(%0 : $UnsafeContinuation<T, Never>, %1 : $*T):
  // function_ref UnsafeContinuation.resume<>(returning:)
  %2 = function_ref @$sScc6resume9returningyxn_ts5NeverORs_rlF : $@convention(method) <τ_0_0, τ_0_1 where τ_0_1 == Never> (@in τ_0_0, UnsafeContinuation<τ_0_0, Never>) -> () // user: %3
  %3 = apply %2<T, Never>(%1, %0) : $@convention(method) <τ_0_0, τ_0_1 where τ_0_1 == Never> (@in τ_0_0, UnsafeContinuation<τ_0_0, Never>) -> ()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$ss25_resumeUnsafeContinuationyySccyxs5NeverOG_xntlF'

// _finalizeUninitializedArray<A>(_:)
sil shared [readnone] [_semantics "array.finalize_intrinsic"] @$ss27_finalizeUninitializedArrayySayxGABnlF : $@convention(thin) <Element> (@owned Array<Element>) -> @owned Array<Element> {
[%0: escape! v** => %r.v**, escape! v**.c*.v** => %r.v**.c*.v**]
// %0                                             // user: %2
bb0(%0 : $Array<Element>):
  %1 = alloc_stack $Array<Element>                // users: %6, %5, %4, %2
  store %0 to %1 : $*Array<Element>               // id: %2
  // function_ref Array._endMutation()
  %3 = function_ref @$sSa12_endMutationyyF : $@convention(method) <τ_0_0> (@inout Array<τ_0_0>) -> () // user: %4
  %4 = apply %3<Element>(%1) : $@convention(method) <τ_0_0> (@inout Array<τ_0_0>) -> ()
  %5 = load %1 : $*Array<Element>                 // user: %7
  dealloc_stack %1 : $*Array<Element>             // id: %6
  return %5 : $Array<Element>                     // id: %7
} // end sil function '$ss27_finalizeUninitializedArrayySayxGABnlF'

// default argument 1 of print(_:separator:terminator:)
sil shared @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String {
bb0:
  %0 = string_literal utf8 " "                    // user: %5
  %1 = integer_literal $Builtin.Word, 1           // user: %5
  %2 = integer_literal $Builtin.Int1, -1          // user: %5
  %3 = metatype $@thin String.Type                // user: %5
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %5
  %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %6
  return %5 : $String                             // id: %6
} // end sil function '$ss5print_9separator10terminatoryypd_S2StFfA0_'

// default argument 2 of print(_:separator:terminator:)
sil shared @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String {
bb0:
  %0 = string_literal utf8 "\n"                   // user: %5
  %1 = integer_literal $Builtin.Word, 1           // user: %5
  %2 = integer_literal $Builtin.Int1, -1          // user: %5
  %3 = metatype $@thin String.Type                // user: %5
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %5
  %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %6
  return %5 : $String                             // id: %6
} // end sil function '$ss5print_9separator10terminatoryypd_S2StFfA1_'

// print(_:separator:terminator:)
sil @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()

// exit
// clang name: exit
sil [clang exit] @exit : $@convention(c) (Int32) -> Never

// @nonobjc SimpleClass.init()
sil shared [thunk] @$sSo11SimpleClassCABycfcTO : $@convention(method) (@owned SimpleClass) -> @owned SimpleClass {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $SimpleClass):
  %1 = objc_method %0 : $SimpleClass, #SimpleClass.init!initializer.foreign : (SimpleClass.Type) -> () -> SimpleClass, $@convention(objc_method) (@owned SimpleClass) -> @owned SimpleClass // user: %2
  %2 = apply %1(%0) : $@convention(objc_method) (@owned SimpleClass) -> @owned SimpleClass // user: %3
  return %2 : $SimpleClass                        // id: %3
} // end sil function '$sSo11SimpleClassCABycfcTO'

// Array._endMutation()
sil shared [_semantics "array.end_mutation"] @$sSa12_endMutationyyF : $@convention(method) <Element> (@inout Array<Element>) -> () {
[%0: noescape! **]
// %0                                             // users: %9, %1
bb0(%0 : $*Array<Element>):
  %1 = struct_element_addr %0 : $*Array<Element>, #Array._buffer // user: %2
  %2 = struct_element_addr %1 : $*_ArrayBuffer<Element>, #_ArrayBuffer._storage // user: %3
  %3 = struct_element_addr %2 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %4
  %4 = load %3 : $*Builtin.BridgeObject           // user: %5
  %5 = end_cow_mutation %4 : $Builtin.BridgeObject // user: %6
  %6 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%5 : $Builtin.BridgeObject) // user: %7
  %7 = struct $_ArrayBuffer<Element> (%6 : $_BridgeStorage<__ContiguousArrayStorageBase>) // user: %8
  %8 = struct $Array<Element> (%7 : $_ArrayBuffer<Element>) // user: %9
  store %8 to %0 : $*Array<Element>               // id: %9
  %10 = tuple ()                                  // user: %11
  return %10 : $()                                // id: %11
} // end sil function '$sSa12_endMutationyyF'

// String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
sil [always_inline] [readonly] [_semantics "string.makeUTF8"] @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String

// UnsafeContinuation.resume<>(returning:)
sil shared [available 12.0.0] @$sScc6resume9returningyxn_ts5NeverORs_rlF : $@convention(method) <T, E where E == Never> (@in T, UnsafeContinuation<T, Never>) -> () {
// %0                                             // user: %3
// %1                                             // user: %2
bb0(%0 : $*T, %1 : $UnsafeContinuation<T, Never>):
  %2 = struct_extract %1 : $UnsafeContinuation<T, Never>, #UnsafeContinuation.context // user: %3
  %3 = builtin "resumeNonThrowingContinuationReturning"<T>(%2 : $Builtin.RawUnsafeContinuation, %0 : $*T) : $()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$sScc6resume9returningyxn_ts5NeverORs_rlF'



// Mappings from '#fileID' to '#filePath':
//   'main/main.swift' => 'main.swift'

Вызов present примерно преобразуется в

  • создайте UnsafeContinuation, обернув RawUnsafeContinuation, который кажется просто необработанным указателем. Этот указатель создается инструкцией get_async_continuation_addr.
  • передать замыкание present (см. раздел с пометкой «Реализация блока обработчика завершения @objc для...»)
  • в завершение звоните UnsafeContinuation.resume
  • дождитесь результата (см. инструкцию await_async_continuation)

Обратите внимание, что именно это и делает withUnsafeContinuation (источник).

Сравните вывод с компиляцией этого файла Swift:

// main.swift
class SimpleClass {
    func present(completion: (Bool) -> Void) {}
}

let foo = SimpleClass()
print(await withUnsafeContinuation { continuation in
    foo.present { continuation.resume(returning: $0) }
})

Код SIL примерно такой же. Есть просто явный вызов функции withUnsafeContinuation, отражающий то, что вы сделали в коде Swift. Вы по-прежнему можете увидеть все шаги, которые я упомянул выше, потому что withUnsafeContinuation отмечен @_alwaysEmitIntoClient. Вы можете найти вызов resume в разделе с пометкой «закрытие №1 в замыкании №1».

Ого, спасибо, ты профи!

Isaaс Weisberg 23.08.2024 15:03

Другие вопросы по теме