Swift扩展:如何在两个模块中使用相同的扩展函数?

34

假设我有一个叫做 SwiftKit 的框架,其中包含了一个 UIView 的扩展类方法 someClassMethod 和一个名为 someProperty 的属性:

// SwiftKit
public extension UIView {
    class func someClassMethod() {
        print("someClassMethod from Swift Kit")
    }
    
    var someProperty: Double {
        print("someProperty from Swift Kit")
        return 0
    }
}

我还有一个名为SwiftFoundation的框架,其中也有一个UIView扩展类方法名为someClassMethod和一个名为someProperty的属性:

// SwiftFoundation
public extension UIView {
    class func someClassMethod() {
        print("someClassMethod from Swift Foundation")
    }
    
    var someProperty: Double {
        print("someProperty from Swift Foundation")
        return 0
    }
}

然后我创建了一个项目,引入了这些框架。问题是,如果我在同一个Swift文件中导入它们并访问这些扩展,我会得到一个“某些属性/某些类方法的使用不明确”的错误,即使我以SwiftKit.UIView.someClassMethod()的形式指定调用也是如此。

import UIKit
import SwiftKit
import SwiftFoundation

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.view.someProperty              // error: Ambiguous use of 'somProperty'
        SwiftKit.UIView.someClassMethod()   // error: Ambiguous use of 'someClassMethod()'
    }
}

如果我只导入其中一个,就能消除歧义错误,但是会发生奇怪的事情:

import UIKit
import SwiftKit
//import SwiftFoundation

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.view.someProperty
        SwiftKit.UIView.someClassMethod()
    }
}

控制台输出:

来自 Swift Foundation 的 someProperty

来自 Swift Foundation 的 someClassMethod

我的问题是:如何避免这些扩展(包括类方法、实例方法和属性)名称的歧义?如果不能,是否意味着我们应该像在 Objective-C 中一样为扩展名称添加前缀?


1
我会假设 ModuleA.UIColor.randomColor() 可以工作(假设你将其定义为 类方法)。你试过了吗? - Martin R
我刚刚使用类似的扩展进行了检查,Module.UIColor.myCustomColor() 编译通过。 - Adam
@MartinR 感谢您的回复,我已经更清楚地表达了问题,非常感谢您的进一步帮助。 - guojiubo
1
我的假设(我自己没有测试过):它应该适用于纯Swift类,但不适用于继承自NSObject的类,因为在Objective-C中,在不同类别中定义相同的方法是未定义的行为。 - Martin R
我可以建议清理并构建吗? - Richard Birkett
3个回答

8

详情

  • Swift 3,Xcode 8.1
  • Swift 4,Xcode 9.1
  • Swift 5.1,Xcode 11.2.1

问题

框架SwiftFoundation和SwiftKit的属性和函数名称相同

决策

方法1

使用不同的属性和函数名称

// SwiftFoundation
public extension UIView {
    public class func swiftFoundationSomeClassMethod() {
        print("someClassMethod from Swift Foundation")
    }

    public var swiftFoundationSomeProperty: Double {
        print("someProperty from Swift Foundation")
        return 0
    }
}

方式二

将属性和函数分组

// SwiftKit
public extension UIView {
    public class SwiftKit {
        public class func someClassMethod() {
            print("someClassMethod from Swift Kit")
        }

        public var someProperty: Double {
            print("someProperty from Swift Kit")
            return 0
        }
    }

    var SwiftKit: SwiftKit { return SwiftKit() }
}

结果

import UIKit
import SwiftKit
import SwiftFoundation

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        _ = view.SwiftKit.someProperty
        UIView.SwiftKit.someClassMethod()

        _ = view.swiftFoundationSomeProperty
        UIView.swiftFoundationSomeClassMethod()
    }
}

项目

enter image description here enter image description here

你的方法

SwiftFoundation.UIView.swiftFoundationSomeClassMethod()

你的命名空间使用方式不正确,因为两个框架中的所有UIView扩展都包含在你的UIView类中。看下面的图片,你可以看到SwiftKit类和swiftFoundationSomeClassMethod()在SwiftFoundation中。这可能会让其他开发人员感到困惑。

enter image description here


1
你可以使用两个不同的文件,分别导入它们所需要的两个依赖项。然后你可以在文件A中使用来自框架A的公共扩展,在文件B中使用来自框架B的扩展。

1
如果它是一个 ObjC 的扩展对象,例如 UIView,则需要为扩展方法添加前缀。然而,那些觉得下划线不美观的人可以尝试使用 Swift 协议替换 UIColor.red.my_toImage() 为 UIColor.red.my.toImage(),链接在这里:在项目中更好地管理 Swift 扩展的方法

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