Доступ к частной функции UIKit без использования заголовка моста

Рассмотрим частную функцию C _UICreateScreenUIImage, которая возвращает моментальный снимок UIImage текущего экрана устройства:

OBJC_EXTERN UIImage *_UICreateScreenUIImage(void) NS_RETURNS_RETAINED;

Я могу поместить это в заголовок моста и получить доступ к нему в Swift следующим образом:

MyApp-мост-header.h

@import UIKit;
UIImage *_UICreateScreenUIImage(void) NS_RETURNS_RETAINED;

MyClass.swift

let image = _UICreateScreenUIImage()
print(image) // <UIImage: 0x7fc4ba6081c0>, {375, 667}

Есть ли способ получить доступ к _UICreateScreenUIImage в чистом Swift без использования заголовка моста?

Первоначальная мысль заключалась в том, чтобы создать расширение на UIImage, но расширение ожидает, что я объявлю тело функции в расширении:

extension UIImage {
    public func _UICreateScreenUIImage(_: Void) -> UIImage // "Expected '{' in body of function declaration"
}

Эта реализация в любом случае ошибочна, поскольку _UICreateScreenUIImage не является методом на UIImage.

Является ли доступ и доступ к этому методу в чистом Swift?


Люди, похоже, путают мой вопрос: "Как сделать снимок экрана?" Это не то, что я прошу. Я спрашиваю, как мне получить доступ к таким методам, как UIImage *_UICreateScreenUIImage(void); в Swift. Это может быть любой частный метод, например +(UIImage *)_deviceSpecificImageNamed:(NSString *)name inBundle:(NSBundle *)bundle; или +(UIImage *)_pu_PhotosUIImageNamed:(NSString *)name; .

Ответ 1

Это намного проще, чем вы ожидали:

@asmname("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> UIImage

// That it – go ahead and call it:
_UICreateScreenUIImage()

Как это бывает, @asmname фактически только что был изменен в строках 2.3 на @_silgen_name, поэтому будьте готовы соответствующим образом отрегулировать:

@_silgen_name("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> UIImage

Насколько мне известно, @_silgen_name не обеспечивает разрешение методов Objective-C. Для этого существует еще более мощный Objective-C API времени выполнения:

let invokeImageNamed: (String, NSTimeInterval) -> UIImage? = {
    // The Objective-C selector for the method.
    let selector: Selector = "animatedImageNamed:duration:"
    guard case let method = class_getClassMethod(UIImage.self, selector)
        where method != nil else { fatalError("Failed to look up \(selector)") }

    // Recreation of the method implementation function.
    typealias Prototype = @convention(c) (AnyClass, Selector, NSString, NSTimeInterval) -> UIImage?
    let opaqueIMP = method_getImplementation(method)
    let function = unsafeBitCast(opaqueIMP, Prototype.self)

    // Capture the implemenation data in a closure that can be invoked at any time.
    return { name, interval in function(UIImage.self, selector, name, interval) }
}()

extension UIImage {
    // Convenience method for calling the closure from the class.
    class func imageNamed(name: String, interval: NSTimeInterval) -> UIImage? {
        return invokeImageNamed(name, interval)
    }
}

UIImage.imageNamed("test", interval: 0)

Что касается обработки NS_RETURNS_RETAINED, это не будет создано для вас. Вместо этого вы можете использовать возвращаемый тип Unmanaged и обернуть это функцией в удобное для вас время:

@_silgen_name("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> Unmanaged<UIImage>
func UICreateScreenUIImage() -> UIImage {
    return _UICreateScreenUIImage().takeRetainedValue()
}