简体   繁体   English

将GPUImage过滤器子类移植到Swift

[英]Porting GPUImage filter subclasses to Swift

I'm trying to port an app from Objective-C to Swift, but I'm having problems with a subclass of GPUImageFilter. 我正在尝试将应用程序从Objective-C移植到Swift,但我遇到了GPUImageFilter子类的问题。

In Obj-C, subclassing GPUImageFilter and using a different fragment shader was as easy as 在Obj-C中,继承GPUImageFilter并使用不同的片段着色器就像这样简单

- (id)init;
{
    NSString *fragmentShaderPathname = [[NSBundle mainBundle] pathForResource:@"TestShader" ofType:@"fsh"];
    NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragmentShaderPathname encoding:NSUTF8StringEncoding error:nil];

    if (!(self = [super initWithFragmentShaderFromString:fragmentShaderString]))
    {
        return nil;
    }

    return self;
}

I can't figure out how to do this in Swift. 我无法弄清楚如何在Swift中做到这一点。 This is my new initializer: 这是我的新初始化程序:

override init() {
    let fragmentShaderPathname = NSBundle.mainBundle().pathForResource("TestShader", ofType: "fsh")!
    let fragmentShaderString = NSString(contentsOfFile: fragmentShaderPathname, encoding: NSUTF8StringEncoding, error: nil)

    super.init(fragmentShaderFromString: fragmentShaderString)
}

As soon as this gets called, the app crashes with this error message: /Users/peterwunder/Documents/XCode/welp/welp/GPUImageTestFilter.swift: 12: 7: fatal error: use of unimplemented initializer 'init(vertexShaderFromString:fragmentShaderFromString:)' for class 'welp.GPUImageTestFilter' 一旦调用此应用程序,应用程序就会崩溃并显示以下错误消息: /Users/peterwunder/Documents/XCode/welp/welp/GPUImageTestFilter.swift: 12: 7: fatal error: use of unimplemented initializer 'init(vertexShaderFromString:fragmentShaderFromString:)' for class 'welp.GPUImageTestFilter'

What am I doing wrong? 我究竟做错了什么?

Interesting problem! 有趣的问题! You're getting tripped up due to the differences in initialization between Swift and Objective-C. 由于Swift和Objective-C之间的初始化不同,您将被绊倒。 If the GPUImageFilter class were written in Swift, the compiler wouldn't let you have this setup, but since you're mixing & matching it doesn't complain until runtime. 如果GPUImageFilter类是用Swift编写的,那么编译器就不会让你进行这种设置,但是由于你GPUImageFilter混合和匹配,所以直到运行时它才会出现问题。 Here's what's happening when you try to instantiate your subclass: 以下是您尝试实例化子类时发生的情况:

  1. You call let myFilter = CustomGPUImageFilter() 你调用let myFilter = CustomGPUImageFilter()
  2. Your init() loads the shader string and calls super.init(fragmentShaderString: "foo") 你的init()加载着色器字符串并调用super.init(fragmentShaderString: "foo")
  3. GPUImageFilter.init(fragmentShaderString:) calls [self initWithVertexShaderFromString:@"bar" fragmentShaderFromString:@"foo"] . GPUImageFilter.init(fragmentShaderString:)调用[self initWithVertexShaderFromString:@"bar" fragmentShaderFromString:@"foo"]

What type is self in that last line? 最后一行是什么类型的self If you're calling from your subclass's initializer, the type of self is actually that of your subclass: CustomGPUImageFilter . 如果您从子类的初始化程序调用,则self的类型实际上是您的子类的类型: CustomGPUImageFilter So the initializer that gets called is actually on your subclass. 因此被调用的初始化程序实际上在您的子类上。 Why is this a problem? 为什么这是个问题?

Unfortunately, Swift classes don't automatically inherit the initializers from their superclass if they define their own designated initializers, which is what your init() is. 不幸的是,如果Swift类定义了自己的指定初始值设定项,那么它们就不会自动从超类继承初始化器,这就是你的init() So the runtime tries to call CustomGPUImageFilter.init(vertexShaderFromString:fragmentShaderFromString:) , doesn't find it, and crashes with that error message. 因此,运行时尝试调用CustomGPUImageFilter.init(vertexShaderFromString:fragmentShaderFromString:) ,找不到它,并与该错误消息崩溃。

Happily, the fix is quite simple. 令人高兴的是,修复非常简单。 If you mark your initializer as convenience init , you'll no longer be defining a designated initializer, which means your subclass will inherit the initializers from GPUImageFilter . 如果您标记您的初始化为convenience init ,您将不再被定义指定初始化,这意味着你的子类继承初始化GPUImageFilter Then in step 3 above, the runtime will find the inherited initializer on your subclass and execute without a problem. 然后在上面的步骤3中,运行时将在子类上找到继承的初始化程序并执行而不会出现问题。 Note that you'll need to call self.init... instead of super.init... : 请注意,您需要调用self.init...而不是super.init...

convenience override init() {
    let fragmentShaderPathname = NSBundle.mainBundle().pathForResource("TestShader", ofType: "fsh")!
    let fragmentShaderString = NSString(contentsOfFile: fragmentShaderPathname, encoding: NSUTF8StringEncoding, error: nil)

    self.init(fragmentShaderFromString: fragmentShaderString)
}

For more about this process, read the section on class inheritance and initialization in the Swift documentation. 有关此过程的更多信息,请阅读Swift文档中有关类继承和初始化的部分。 The process is significantly different from Objective-C. 该过程与Objective-C有很大不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM