2016-02-10 64 views
-2

我想加載和操縱SKUIImageColorAnalyzerSKUIAnalyzedImageColors對象來自私人StoreKitUI.framework公開在運行時從框架加載的類的接口

首先,我試圖在運行時加載框架:

guard case let libHandle = dlopen("/System/Library/PrivateFrameworks/StoreKitUI.framework/StoreKitUI", RTLD_NOW) where libHandle != nil else { 
    fatalError("StoreKitUI not found") 
} 

然後,我驗證SKUIImageColorAnalyzer類,可以發現:

guard let analyzerClass: AnyClass = NSClassFromString("SKUIImageColorAnalyzer") else { 
    fatalError("SKUIImageColorAnalyzer lookup failed") 
} 

我想在使用analyzeImage:類方法SKUIImageColorAnalyzer,它需要UIImage進行分析並返回SKUIAnalyzedImageColors對象。我做這個通過驗證SKUIImageColorAnalyzer對象上存在analyzeImage:選擇,並重新創建功能:

let selector: Selector = "analyzeImage:" 
guard case let method = class_getClassMethod(analyzerClass, selector) where method != nil else { 
    fatalError("failed to look up \(selector)") 
} 

// recreate the method's implementation function 
typealias Prototype = @convention(c) (AnyClass, Selector, UIImage) -> AnyObject? // returns an SKUIAnalyzedImageColors object 
let opaqueIMP = method_getImplementation(method) 
let function = unsafeBitCast(opaqueIMP, Prototype.self) 

現在,我可以得到一個UIImage對象,並把它傳遞作爲參數傳遞給函數:

let img = UIImage(named: "someImage.jpg")! 
let analyzedImageColors = function(analyzerClass, selector, img) // <SKUIAnalyzedImageColors: 0x7f90d3408eb0> 

我知道analyzedImageColors的類型是SKUIAnalyzedImageColors,但編譯器仍然認爲它的類型是AnyObject,這取決於我上面聲明Prototype的方式。現在我想訪問一個SKUIAnalyzedImageColors對象的屬性。

header,我可以看到對象上有諸如backgroundColortextPrimaryColortextSecondaryColor的屬性。我可以使用valueForKey訪問這些屬性,但我想公開一個公共接口SKUIAnalyzedImageColors,以便我可以訪問這些屬性。

我第一次嘗試這樣的:

// Create a "forward declaration" of the class 
class SKUIAnalyzedImageColors: NSObject { } 


// Create convenience extensions for accessing properties 
extension SKUIAnalyzedImageColors { 
    func backgroundColor() -> UIColor { 
     return self.valueForKey("_backgroundColor") as! UIColor 
    } 

    func textPrimaryColor() -> UIColor { 
     return self.valueForKey("_textPrimaryColor") as! UIColor 
    } 

    func textSecondaryColor() -> UIColor { 
     return self.valueForKey("_textSecondaryColor") as! UIColor 
    } 
} 

// ... 

// modify the prototype to return an SKUIAnalyzedImageColors object 
typealias Prototype = @convention(c) (AnyClass, Selector, UIImage) -> SKUIAnalyzedImageColors? 

// ... 

// access the properties from the class extension 
analyzedImageColors?.backgroundColor() // Optional(UIDeviceRGBColorSpace 0.262745 0.231373 0.337255 1) 

這仍然要求我使用valueForKey。有沒有辦法從運行時加載的框架暴露一個類的公共接口?

回答

2

做動態Objective-C的最簡單的方法是使用Objective-C。

ImageAnalyzer.h:

#import <UIKit/UIKit.h> 

NS_ASSUME_NONNULL_BEGIN 

@interface SKUIAnalyzedImageColors : NSObject 

@property (nonatomic, readonly) UIColor* backgroundColor; 
@property (nonatomic, readonly) BOOL isBackgroundLight; 
@property (nonatomic, readonly) UIColor* textPrimaryColor; 
@property (nonatomic, readonly) UIColor* textSecondaryColor; 

@end 

SKUIAnalyzedImageColors* _Nullable analyzeImage(UIImage* image); 

NS_ASSUME_NONNULL_END 

ImageAnalyzer.m:

#import "ImageColorAnalyzer.h" 
#include <dlfcn.h> 

static Class _SKUIImageColorAnalyzerClass; 

@interface SKUIImageColorAnalyzer : NSObject 
+ (SKUIAnalyzedImageColors*)analyzeImage:(UIImage*)arg1; 
@end 

SKUIAnalyzedImageColors* analyzeImage(UIImage* image) 
{ 
    if (!_SKUIImageColorAnalyzerClass) 
    { 
     if (!dlopen("/System/Library/PrivateFrameworks/StoreKitUI.framework/StoreKitUI", RTLD_NOW)) 
     { 
      NSLog(@"No framework."); 
      return nil; 
     } 
     _SKUIImageColorAnalyzerClass = NSClassFromString(@"SKUIImageColorAnalyzer"); 
     if (!_SKUIImageColorAnalyzerClass) 
     { 
      NSLog(@"No Class."); 
      return nil; 
     } 
    } 

    return [_SKUIImageColorAnalyzerClass analyzeImage:image]; 
} 

然後,可以從任一夫特或Objective-C代碼使用analyzeImage函數和SKUIAnalyzedImageColors類容易。

if let image = UIImage(named:"MyImage") { 
    if let colors = analyzeImage(image) { 
     print("Background Color: \(colors.backgroundColor)") 
    } 
} 

如果你真的想要做的這一切在斯威夫特,首先聲明要使用SKUIAnalyzedImageColors的Objective-C接口部分:

@objc protocol ImageColors { 
    var backgroundColor: UIColor { get } 
    var isBackgroundLight: Bool { get } 
    var textPrimaryColor: UIColor { get } 
    var textSecondaryColor: UIColor { get } 
} 

然後使用unsafeBitCast投不透明對象實例到你想要的Objective-C界面:

let img = UIImage(named: "someImage.jpg")! 
let rawAnalyzedImageColors = function(analyzerClass, selector, img) 

let analyzedImageColors = unsafeBitCast(rawAnalyzedImageColors, ImageColors.self) 
print("Background color: \(analyzedImageColors.backgroundColor)") 
+1

謝謝,但我真的在尋找一個純粹的Swift解決方案而不使用Objective-C。如果我想使用Objective-C,我可以從框架中取出標題並通過橋頭導入它們以暴露類和接口。 – JAL

+0

此外,您純粹的Swift解決方案仍然需要我通過協議公開接口。有沒有辦法動態加載該對象的接口,而不是必須手動聲明所有內容? – JAL

+0

儘管我很欣賞你的反思方法,不管。 – JAL