在运行时公开从框架加载的 class 的接口
Expose an interface of a class loaded from a framework at runtime
我想加载和操作 SKUIImageColorAnalyzer
并且
SKUIAnalyzedImageColors
个对象来自私有 StoreKitUI.framework
。
首先,我尝试在运行时加载框架:
guard case let libHandle = dlopen("/System/Library/PrivateFrameworks/StoreKitUI.framework/StoreKitUI", RTLD_NOW) where libHandle != nil else {
fatalError("StoreKitUI not found")
}
然后,我验证可以找到 SKUIImageColorAnalyzer
class:
guard let analyzerClass: AnyClass = NSClassFromString("SKUIImageColorAnalyzer") else {
fatalError("SKUIImageColorAnalyzer lookup failed")
}
我想在 SKUIImageColorAnalyzer
上使用 analyzeImage:
class 方法,它接收 UIImage
进行分析,returns 接收 SKUIAnalyzedImageColors
目的。我通过验证 analyzeImage:
选择器存在于 SKUIImageColorAnalyzer
对象上并重新创建函数来做到这一点:
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
类型,但是根据我上面声明Prototype
的方式,编译器仍然认为它的类型是AnyObject
。现在我想访问 SKUIAnalyzedImageColors
对象的属性。
从header可以看出对象上有backgroundColor
、textPrimaryColor
、textSecondaryColor
等属性。我可以使用 valueForKey
访问这些属性,但我想在 SKUIAnalyzedImageColors
上公开一个 public 接口,以便我可以访问这些属性。
我的第一次尝试是这样的:
// 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
。有没有办法从运行时加载的框架在 class 上公开 public 接口?
执行动态 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];
}
然后您可以从 Swift 或 Objective-C 代码轻松使用 analyzeImage
函数和 SKUIAnalyzedImageColors
class。
if let image = UIImage(named:"MyImage") {
if let colors = analyzeImage(image) {
print("Background Color: \(colors.backgroundColor)")
}
}
如果你真的想在Swift中全部完成,首先声明你想使用的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)")
我想加载和操作 SKUIImageColorAnalyzer
并且
SKUIAnalyzedImageColors
个对象来自私有 StoreKitUI.framework
。
首先,我尝试在运行时加载框架:
guard case let libHandle = dlopen("/System/Library/PrivateFrameworks/StoreKitUI.framework/StoreKitUI", RTLD_NOW) where libHandle != nil else {
fatalError("StoreKitUI not found")
}
然后,我验证可以找到 SKUIImageColorAnalyzer
class:
guard let analyzerClass: AnyClass = NSClassFromString("SKUIImageColorAnalyzer") else {
fatalError("SKUIImageColorAnalyzer lookup failed")
}
我想在 SKUIImageColorAnalyzer
上使用 analyzeImage:
class 方法,它接收 UIImage
进行分析,returns 接收 SKUIAnalyzedImageColors
目的。我通过验证 analyzeImage:
选择器存在于 SKUIImageColorAnalyzer
对象上并重新创建函数来做到这一点:
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
类型,但是根据我上面声明Prototype
的方式,编译器仍然认为它的类型是AnyObject
。现在我想访问 SKUIAnalyzedImageColors
对象的属性。
从header可以看出对象上有backgroundColor
、textPrimaryColor
、textSecondaryColor
等属性。我可以使用 valueForKey
访问这些属性,但我想在 SKUIAnalyzedImageColors
上公开一个 public 接口,以便我可以访问这些属性。
我的第一次尝试是这样的:
// 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
。有没有办法从运行时加载的框架在 class 上公开 public 接口?
执行动态 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];
}
然后您可以从 Swift 或 Objective-C 代码轻松使用 analyzeImage
函数和 SKUIAnalyzedImageColors
class。
if let image = UIImage(named:"MyImage") {
if let colors = analyzeImage(image) {
print("Background Color: \(colors.backgroundColor)")
}
}
如果你真的想在Swift中全部完成,首先声明你想使用的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)")