简体   繁体   English

调用NSSet的setByAddingX方法时,哪个集合的对象会赢得平局?

[英]When calling NSSet's setByAddingX methods, which set's objects win a tie?

I bring this up because objects that compare the same with isEquals: aren't necessarily identical. 之所以提出这一点,是因为将与isEquals:比较相同的对象不一定相同。 Some (many) objects only compare certain properties to determine equality. 一些(许多)对象仅比较某些属性来确定相等性。

That makes the exact behavior of the following NSSet methods important: 这使得以下NSSet方法的确切行为很重要:

setByAddingObject:
setByAddingObjectsFromArray:
setByAddingObjectsFromSet:

The documentation does not specify what happens when the receiver and the parameter contain equivalent objects. 该文档没有指定当接收方和参数包含等效对象时会发生什么。 Will the resulting NSSet contain the object from the receiver, or the object from the "other" parameter? 生成的NSSet将包含接收方的对象还是“ other”参数的对象?

Note that in NSMutableSet, it DOES specify the behavior of its add methods--objects will NOT be added if an "equal" object already exists in the set. 请注意,在NSMutableSet中,它确实指定其添加方法的行为-如果集合中已经存在“相等”对象,则不会添加对象。

The documentation of NSMutableSet's addObject: method used to cover a similar case: NSMutableSet的addObject:方法的文档用于覆盖类似的情况:

If anObject is already present in the set, this method has no effect on either the set or anObject . 如果set中已经存在anObject ,则此方法对set或anObject无效。

But, as you can see from following the link, the current version doesn't even say that. 但是,从链接中可以看到,当前版本甚至没有这么说。 And even that statement really only covers trying to add the same object; 甚至该语句实际上仅涵盖尝试添加相同的对象。 it does not specifically address adding a different but equal object. 它没有专门解决添加一个不同但相等的对象的问题。

Relying on observed but not documented behavior is dangerous, not just because it can change between OS versions, but also because it can change within the very same process. 依靠观察到的但未记录在案的行为是危险的,不仅因为它可以在OS版本之间进行更改,而且还因为它可以在同一过程中进行更改。 That's because NSSet is a class cluster , meaning there may be multiple implementations. 那是因为NSSet是一个类集群 ,这意味着可能有多种实现。 Which one you get depends on how you create the set; 您将获得哪一个取决于您如何创建集合。 there is no way to ensure that a specific implementation will be chosen or even exist.* 无法确保将选择甚至存在特定的实现。*

That's because it shouldn't matter. 那是因为没关系。 Every one of the clustered subclasses presents the same behavior as defined in the cluster's interface and documentation. 集群子类的每个子类都具有与集群接口和文档中定义的行为相同的行为。 (If it ever doesn't, that's a bug and you should report it .) Given that all the subclasses do effectively the same things, it shouldn't matter which one you get an instance of. (如果没有,那就是一个错误,应该报告它 。)鉴于所有子类实际上都执行相同的操作,因此,获得哪个实例无关紧要。

The same principle applies to your objects. 同样的原则适用于您的对象。 They're equal! 他们是平等的! For that reason, it shouldn't matter which one is in the set. 因此,集合中的哪一个都不重要。 If it does matter, then they are not truly equal, and you need to make the objects' definition of equality more rigid. 如果确实重要,则它们不是真正相等的,您需要使对象的相等性定义更加严格。 (Don't forget to update both isEqual: and hash .) (不要忘记同时更新isEqual: hash 。)

Depending on what you're using the set for, you may want to take that even farther and ensure that no two equal objects can exist. 根据您使用该集合的目的,您可能想进一步利用它并确保不存在两个相等的对象。 To do this, move the ownership, maintenance, and use of the set into the member objects' class, and have it always return a matching object instead of creating a new one whenever possible and appropriate. 为此,请将集合的所有权,维护和使用移到成员对象的类中,并使其始终返回匹配的对象,而不是在可能和适当的情况下创建一个新的对象。

*And even if you could choose one of the implementations, there's no guarantee that it'd have the behavior you observed forever—it could, and Murphy says probably will, be different in another OS version. *即使您可以选择其中一种实现,也无法保证它具有您永远观察到的行为-Murphy说,在另一个OS版本中,它可能会有所不同。

I tested this with the following code. 我使用以下代码对此进行了测试。 SomeClass is defined such that propertyA is the only property considered in hash and isEquals: 定义SomeClass使得propertyA是hash和isEquals中唯一考虑的属性:

SomeClass *objectA = [[[SomeClass alloc] init] autorelease];
objectA.propertyA = @"test";
objectA.propertyB = @"objectA";

SomeClass *objectB = [[[SomeClass alloc] init] autorelease];
objectB.propertyA = @"test";
objectB.propertyB = @"objectB";

NSSet *setA = [NSSet setWithObject:objectA];
NSSet *setB = [NSSet setWithObject:objectB];
NSSet *setC = [setA setByAddingObjectsFromSet:setB];

NSLog(@"Set A\n%@", [setA description]);
NSLog(@"Set B\n%@", [setB description]);
NSLog(@"Set C\n%@", [setC description]);

The output when running this code is: 运行此代码时的输出为:

2011-03-03 16:35:15.041 temp[50311:207] Set A
{(
    {SomeClass propertyA:test propertyB:objectA}
)}
2011-03-03 16:35:15.041 temp[50311:207] Set B
{(
    {SomeClass propertyA:test propertyB:objectB}
)}
2011-03-03 16:35:15.042 temp[50311:207] Set C
{(
    {SomeClass propertyA:test propertyB:objectA}
)}

This demonstrates that the newly created NSSet will contain objects from the RECEIVER in the case that the parameter contains equivalent objects. 这表明在参数包含等效对象的情况下,新创建的NSSet将包含来自RECEIVER的对象。

EDIT - I'm marking this as the answer, because it directly answers the question at hand. 编辑 -我将其标记为答案,因为它直接回答了眼前的问题。 I would however, point out Peter's answer below and the concerns he voices. 但是,我将在下面指出彼得的回答以及他的关切。 This behavior is undocumented and as such, while it's extremely unlikely these core classes will change in this regard, it's worth pointing out that risk. 这种行为无证的,因此尽管这些核心类在这方面极不可能发生变化,但值得指出这一风险。 If you write code assuming this behavior it is possible that it will break in a future release. 如果您以这种行为编写代码,则可能会在将来的版本中破坏代码。 Caveat emptor. 买者自负。

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

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