如何在Swift的便利初始化器中返回自定义对象?

5

我想要做类似于这样的事情:

public extension UIImage {
    public convenience init(whatever: Int) {
        UIGraphicsBeginImageContextWithOptions(...)

        //...

        let image = UIGraphicsGetImageFromCurrentContext()
        UIGraphicsEndImageContext()

        return image // <- impossible
    }
}

但这是不可能的,因为“nil”是初始化程序的唯一有效返回值...我该怎么办?
例如,Objtive-C方法[UIImage imageNamed:]是一个类方法(在Objective-C中它可以返回任何想返回的内容),并且它被映射到了Swift初始化程序UIImage(named:)。
2个回答

7
你需要的是一个类工厂方法,而不是初始化器。Foundation/Cocoa中的大多数工厂方法都会自动桥接到初始化器,但如果你想通过“init”不能完成你想要做的事情,你可以添加一个新的类方法:
public extension UIImage {
    class func imageWithWhatever(whatever: Int) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(...)

        //...

        let image = UIGraphicsGetImageFromCurrentContext()
        UIGraphicsEndImageContext()

        return image
    }
}

有点遗憾的是,在Swift中无法返回不同的对象。我有一个框架,其中定义了一个复杂的视图控制器在storyboard中。我想能够重写init()或者创建一个自定义初始化程序来以编程方式实例化视图控制器,并将其设置为其子视图/插座等。但唯一的方法是使用工厂方法... - Nicolas Miari

1
这是因为您返回了一个新的对象,而不是self。init的目的是创建对象的结构,而不是一个新的对象,所以如果您想用方便的init来完成它,需要这样做:
public extension UIImage {
    public convenience init?(whatever: Int) {
        defer {
            UIGraphicsEndImageContext()
        }
        UIGraphicsBeginImageContextWithOptions(...)

        //...
        guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
        guard let image = currentContext.makeImage() else { return nil }

        self.init(cgImage:image)
    }
}

也许你不想使用便利初始化方法,而是想创建一个类函数来完成你所要求的操作:
public class func createImage(whatever: Int) -> UIImage? {
    defer {
        UIGraphicsEndImageContext()
    }
    UIGraphicsBeginImageContextWithOptions(...)

    //...
    guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
    guard let cgImage = currentContext.makeImage() else { return nil }
    let image = UIImage(cgImage: cgImage)

    return image
}

非常抱歉这不是100%符合代码规范,但基本上就是这个意思。


需要注意的是,在调用 self.init(...) 之后,你也可以在函数结束前修改 self,例如 self.foo = Foo()。虽然在使用 UIImage 的情况下并不是非常有用,但问题标题并没有指定必须使用它。 - arsenius
@arsenius 在这种情况下,那个注释并不重要,你只能在类被初始化之后操作它的属性,这应该是显而易见的。 - Knight0fDragon
“你可以在初始化后操作属性” 是我的观点。你不能做 let image = UIImage()...return image,但是你可以修改 self,这涵盖了许多方便初始化器的用例。原帖作者或其他人可能没有意识到这一点。 - arsenius
在这方面修改自己是毫无意义的,你只会增加混乱。坚持回答问题所要求的内容,人们在SO上寻找简明扼要的答案。 - Knight0fDragon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接