有没有一种方法可以向任何对象(AnyObject)添加一个扩展(extension)?

21

在Objective-C中,我创建了一个非常方便的类别(在Swift中称为扩展),用于NSObject,它可以在运行时向任何NSObject添加任意键/值对的功能。 它使用关联对象将可变字典附加到对象上,然后提供获取和设置方法,从该字典中获取/设置键/值对。只需几行代码。

这使得可以在运行时将任意键/值对附加到任何对象上,包括系统创建的对象。这就是关键。有些情况下,系统框架会返回一个对象,您需要能够将值附加到它上面。

这个技巧还可以创建具有新实例变量的类别。(好吧,它们不是真正的实例变量,但它确实让您向类别中的对象添加新状态变量。)

在Swift 1.2中不可能做到这一点,因为:

  • Swift没有像Objective-C中的NSObject那样的所有对象的基类。它使用AnyObject,它是一个协议。
  • Swift 1.2不允许协议的扩展。

在Swift 1.2下,我不得不放弃这个想法。

但是,Swift 2允许扩展协议。我想:“太好了,现在我可以添加允许我向AnyObject添加键/值对的扩展!”

然而没有成功。

当我尝试为AnyObject创建我的扩展:

extension AnyObject: AssociatedObjectProtocol

我收到了错误信息:

'AnyObject'协议无法被扩展

糟糕!离成功很近,但又失败了。似乎这个语言明确禁止扩展AnyObject。为什么会这样,有没有办法绕过它呢?

我并不经常使用NSObject上的分类,但是当我需要时,它非常有用。我想在Swift中将其加入我的技能包。

我可以像在Objective-C中那样将其添加到NSObject中,但这意味着它只适用于从NSObject继承的对象——对于原生的Swift类不起作用。


1
随机提示:我认为您不希望您的协议以小写字母开头。https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-1002242-BBCIJGDB - Dan Rosenstark
3个回答

17

很遗憾,目前没有确切的方法能够完全满足你的需求。因此,我建议创建一个协议,并在其中添加默认实现的扩展。

protocol A: AnyObject {}

// in Swift you would rather use this
protocol A: class {}

// add default implementations
extension A {
    func aMethod() {
        // code
    }
}

// Example
class B: A {}

// use the method
B().aMethod()

这种方法不会自动使所有类都符合此协议(只有类才能符合它)。因此,您必须自己使它们符合。由于您不会经常使用它,这将是一个合理的解决方案。


我并不完全理解您的建议。您的意思是我应该在协议中定义方法签名,然后将代码作为扩展提供给任何需要它的类吗?这意味着我必须为每个目标类在扩展中重复代码,对吗? - Duncan C
@DuncanC 在这种情况下,您不需要在协议中定义任何要求,您可以在扩展中使用该方法,并且无需额外的代码即可符合它的任何类(在答案中添加了示例)。 - Qbyte
1
我无法理解你回答中的伪代码。 - Duncan C
3
“伪代码”是什么意思?这段代码是有效的Swift 2代码。 - Qbyte
协议扩展很奇怪...但这个答案非常棒和有帮助。我正在将它用在我的奇怪问题回答中:https://dev59.com/3pHea4cB1Zd3GeqPkBCl#33697088谢谢! - Dan Rosenstark

7
与@Qbyte的回答类似,声明NSObject符合协议意味着整个Cocoa/UIKit/Foundation宇宙中几乎每个类都将继承该功能:
protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        print("This is me, doing something.")
    }
}

extension NSObject: MyProtocol { }

您刚刚将 Apple 框架类的 99% 都转换成了 MyProtocol。您可以通过继承自 NSObject 或简单地符合它来使自己的类符合 MyProtocol

1
这并不是一个真正的解决方案。使用“extension NSObject”是完全合法的,但这意味着对于你自己的类,你需要使所有自己的类符合NSObjectProtocol。 - original_username
这个答案还可以,但是使用它时,它并没有为您提供一些基本的Swift编译时检查。例如,如果您想要一个变量var string: String { return self as! String },并且您调用arrayInstance.string,编译器不会告诉您任何信息,但是该操作是不可能的,就像如果您尝试["element"] as! String,编译器会告诉您该操作总是失败。 - Luca D'Alberti
如果你要让NSObject遵循你的协议,并扩展默认行为,那么直接扩展NSObject是否会有所不同,而不是使用一个协议呢? - Gobe
@Gobe,将其作为单独的协议实现,然后使NSObject符合该协议的好处是,如果需要,您还可以使其他内容符合该协议。如果您仅扩展NSObject,则必须对NSObject进行子类化,这可能并不总是可取的。 - Mark A. Donohoe

-1

我觉得你已经有了答案。在Swift中,扩展NSObject也可以完美地工作。(虽然可能不是6年前)

extension NSObject { }


海报意识到他们可以扩展NSObject。他们想要一种扩展所有本地Swift类的方法。这就是他们写的内容: “我可以像在Objective-C中那样将其添加到NSObject中,但这意味着它仅适用于继承自NSObject的对象 - 这使其无法用于本地Swift类。” - Patrick
这个问题已经有一个帖子了,链接在这里:https://dev59.com/LV0a5IYBdhLWcg3wkpes#30176106 唯一的方法是在每个地方都继承NSObject并进行扩展,或者像@Aaron Rasmussen所给出的示例那样使用自定义协议。 这就是为什么我说答案已经在这里了。 - Oleg G.
此外,根据您的具体需求,您也可以创建全局函数和/或全局变量。(甚至可以创建单例以使其更加“干净”) - Oleg G.

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