从 NSSet 获取对象

如果不能从 NSSet 中获取带 objectAtIndex: 的对象,那么如何检索对象?

97838 次浏览

There are several use cases for a set. You could enumerate through (e.g. with enumerateObjectsUsingBlock or NSFastEnumeration), call containsObject to test for membership, use anyObject to get a member (not random), or convert it to an array (in no particular order) with allObjects.

A set is appropriate when you don't want duplicates, don't care about order, and want fast membership testing.

NSSet doesn't have a method objectAtIndex:

Try calling allObjects which returns an NSArray of all the objects.

NSSet uses the method isEqual: (which the objects you put into that set must override, in addition, the hash method) to determine if an object is inside of it.

So, for example if you have a data model that defines its uniqueness by an id value (say the property is:

@property NSUInteger objectID;

then you'd implement isEqual: as

- (BOOL)isEqual:(id)object
{
return (self.objectID == [object objectID]);
}

and you could implement hash:

- (NSUInteger)hash
{
return self.objectID;  // to be honest, I just do what Apple tells me to here
// because I've forgotten how Sets are implemented under the hood
}

Then, you can get an object with that ID (as well as check for whether it's in the NSSet) with:

MyObject *testObject = [[MyObject alloc] init];
testObject.objectID = 5; // for example.


// I presume your object has more properties which you don't need to set here
// because it's objectID that defines uniqueness (see isEqual: above)


MyObject *existingObject = [mySet member: testObject];


// now you've either got it or existingObject is nil

But yeah, the only way to get something out of a NSSet is by considering that which defines its uniqueness in the first place.

I haven't tested what's faster, but I avoid using enumeration because that might be linear whereas using the member: method would be much faster. That's one of the reasons to prefer the use of NSSet instead of NSArray.

NSArray *myArray = [myNSSet allObjects];

MyObject *object = [myArray objectAtIndex:(NSUInteger *)]

replace NSUInteger with the index of your desired object.

it is possible to use filteredSetUsingPredicate if you have some kind of unique identifier to select the object you need.

First create the predicate (assuming your unique id in the object is called "identifier" and it is an NSString):

NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

And then choose the object using the predicate:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
for (id currentElement in mySet)
{
// ** some actions with currentElement
}

For Swift3 & iOS10 :

//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]

Most of the time you don't care about getting one particular object from a set. You care about testing to see if a set contains an object. That's what sets are good for. When you want to see if an object is in a collection sets are much faster than arrays.

If you don't care about which object you get, use -anyObject which just gives you one object from the set, like putting your hand in a bag and grabbing something.

Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects

If you care about what object you get, use -member which gives you back the object, or nil if it's not in the set. You need to already have the object before you call it.

Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above

Here's some code you can run in Xcode to understand more

NSString *one = @"One";
NSString *two = @"Two";
NSString *three = @"Three";


NSSet *set = [NSSet setWithObjects:one, two, three, nil];


// Can't use Objective-C literals to create a set.
// Incompatible pointer types initializing 'NSSet *' with an expression of type 'NSArray *'
//  NSSet *set = @[one, two, three];


NSLog(@"Set: %@", set);
// Prints looking just like an array but is actually not in any order
//Set: {(
//     One,
//     Two,
//     Three
//     )}


// Get a random object
NSString *random = [set anyObject];
NSLog(@"Random: %@", random); // Random: One


// Iterate through objects. Again, although it prints in order, the order is a lie
for (NSString *aString in set) {
NSLog(@"A String: %@", aString);
}


// Get an array from the set
NSArray *array = [set allObjects];
NSLog(@"Array: %@", array);


// Check for an object
if ([set containsObject:two]) {
NSLog(@"Set contains two");
}


// Check whether a set contains an object and return that object if it does (nil if not)
NSString *aTwo = [set member:two];
if (aTwo) {
NSLog(@"Set contains: %@", aTwo);
}