2016-02-03 8 views
11

Betrachten Sie die private C Funktion _UICreateScreenUIImage, die eine UIImage Momentaufnahme des aktuellen Gerätebildschirm zurück:Zugang Private UIKit Funktion ohne Verwendung von Bridging-Header

OBJC_EXTERN UIImage *_UICreateScreenUIImage(void) NS_RETURNS_RETAINED; 

ich dies in einem Überbrückung-Header und den Zugang in Swift setzen kann wie so:

MeineAnw-Bridging-header.h

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

MyClass.swift

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

Gibt es einen Weg, auf _UICreateScreenUIImage in reinem Swift ohne Verwendung eines Überbrückungskopfes zuzugreifen?

Ein erster Gedanke war, eine Erweiterung auf UIImage zu schaffen, aber die Erweiterung erwartet mich den Körper der Funktion in der Verlängerung zu erklären:

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

Diese Implementierung sowieso fehlerhaft ist, als _UICreateScreenUIImage nicht ist eine Methode auf UIImage.

Ist das Aussetzen und Zugreifen auf diese Methode in reinem Swift möglich?


Die Leute scheinen meine Frage mit "Wie mache ich einen Screenshot?" Das ist nicht was ich verlange. Ich frage, wie ich in Swift auf Methoden wie UIImage *_UICreateScreenUIImage(void); zugreifen kann. Es könnte eine beliebige private Methode sein, beispielsweise +(UIImage *)_deviceSpecificImageNamed:(NSString *)name inBundle:(NSBundle *)bundle; oder +(UIImage *)_pu_PhotosUIImageNamed:(NSString *)name; .

Antwort

14

Es ist viel einfacher, als man erwarten würde:

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

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

Wie es passiert, @asmname hat tatsächlich nur in der 2.3 baut auf @_silgen_name, so bereit sein, entsprechend anpassen geändert:

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

Nach meinem Wissen, @_silgen_name bietet keine Auflösung von Objective-C-Methoden. Dazu gibt es den leistungsstarken Evenmore Objective-C-Laufzeit-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's 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) 

Soweit Umgang mit NS_RETURNS_RETAINED, wird dies für Sie nicht erzeugt werden. Stattdessen können Sie einen Rückgabetyp von Unmanaged verwenden und diesen in eine Funktion für Ihren Komfort einbinden:

+1

Dies funktioniert! Tolle! Wo werden 'asmname' und' _silgen_name' dokumentiert? Ist das Äquivalent von "extern C" in Swift? – JAL

+2

Zu meinem Wissen ist nicht dokumentiert (außerhalb von LLVM Interna bin ich mir sicher). Der Ausdruck 'asmname' würde anzeigen, dass er in der Tat äquivalent zu" extern "ist, da er den Assembler einfach anweist, das Symbol an anderer Stelle aufzusuchen. '_silgen_name' würde genauer gesagt bedeuten, dass es' builtined ""() 'Deklarationen während der SIL-Generierung erzeugt (die wiederum vom Assembler ähnlich 'extern' aufgelöst werden). –

+2

Vielen Dank! Ich erwarte, dies als akzeptiert zu betrachten und Ihnen das Kopfgeld zuzusprechen. Eine weitere Folgefrage: Was wäre, wenn ich auf eine private Klassenmethode auf 'UIImage' selbst zugreifen möchte, zum Beispiel:' + (UIImage *) _ pu_PhotosUIImageNamed: (NSString *) name; '?Würde das dem gleichen Muster folgen? – JAL