Получение объекта из NSSet


Ответы:


140

Есть несколько вариантов использования набора. Вы можете выполнить перечисление (например, с помощью enumerateObjectsUsingBlockили NSFastEnumeration), вызвать containsObjectдля проверки членства, использовать anyObjectдля получения члена (не случайного) или преобразовать его в массив (в произвольном порядке) с помощью allObjects.

Набор подходит, когда вам не нужны дубликаты, вы не заботитесь о порядке и хотите быстро проверить членство.


7
Вы также можете найти известный объект по потенциальному дубликату, отправив набору member:сообщение. Если он возвращается nil, то набор не содержит объекта, равного тому, который вы передали; если он возвращает указатель на объект, то он возвращает указатель на объект, уже находящийся в наборе. Объекты в наборе должны быть реализованы, hashи isEqual:чтобы это было полезно.
Питер Хози,

@PeterHosey Я не думаю, что это hash нужно реализовывать; если бы вы это сделали, все пошло бы намного быстрее.
fumoboy007

2
@ fumoboy007: Для хранения в наборе или использования в качестве ключа в словаре, да, это возможно. Из документации hashпротокола NSObject: «Если два объекта равны (как определено isEqual:методом), они должны иметь одинаковое хеш-значение». В настоящее время NSObject реализует hashи isEqual:использует идентификатор (адрес) объекта. Если вы переопределите isEqual:, вы настраиваете возможность объектов, которые не идентичны, но равны - которые, если вы также не переопределите hash, все равно будут иметь разные хэши. Это нарушает требование о том, что одинаковые объекты имеют одинаковые хэши.
Питер Хози

1
@ fumoboy007: рассмотрите, как работает хеш-таблица: контейнер имеет массив из некоторого количества сегментов и использует хеш каждого входящего объекта, чтобы определить, в каком сегменте должен находиться этот объект. Для таких запросов, как member:, контейнер будет искать только в этом контейнере. bucket (именно поэтому наборы намного быстрее массивов при тестировании членства, а словари намного быстрее, чем параллельные массивы при поиске по ключу). Если искомый объект имеет неправильный хэш, тогда контейнер будет искать в неправильном ведре и не найдет совпадения.
Питер Хози

@PeterHosey Упс ... Я ошибочно подумал, что реализация по умолчанию -[NSObject hash]- 0. Это многое объясняет. = S
fumoboy007

31

NSSet не имеет метода objectAtIndex:

Попробуйте вызвать allObjects, который возвращает NSArray всех объектов.


Вы знаете, заказан ли возвращаемый массив? другими словами, если я добавляю объекты в набор с помощью «setByAddingObject», а я использовал «allObjects», являются ли? элементы в массиве упорядочены в порядке добавления объектов?
iosMentalist

просто отсортируйте полученный массив с помощью NSSortPredicate, и все будет в порядке
Джейсон

Если вас интересует только один конкретный объект, лучше использовать подход filterSetUsingPredicate.
akw 05

17

можно использовать filterSetUsingPredicate, если у вас есть какой-то уникальный идентификатор для выбора нужного вам объекта.

Сначала создайте предикат (предполагая, что ваш уникальный идентификатор в объекте называется «идентификатором», и это NSString):

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

А затем выберите объект с помощью предиката:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;

13

NSArray *myArray = [myNSSet allObjects];

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

замените NSUInteger индексом желаемого объекта.


1
Должно быть: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
Джон Д.


6

NSSet использует метод isEqual: (который объекты, которые вы помещаете в этот набор, должны переопределять, кроме того, метод хеширования), чтобы определить, находится ли объект внутри него.

Так, например, если у вас есть модель данных, которая определяет свою уникальность по значению id (скажем, свойство:

@property NSUInteger objectID;

тогда вы должны реализовать isEqual: как

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

и вы можете реализовать хеш:

- (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
}

Затем вы можете получить объект с этим идентификатором (а также проверить, есть ли он в NSSet) с помощью:

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

Но да, единственный способ получить что-то от NSSet - это рассмотреть то, что в первую очередь определяет его уникальность.

Я не тестировал, что быстрее, но я избегаю использования перечисления, потому что это может быть линейным, тогда как использование метода member: было бы намного быстрее. Это одна из причин предпочесть использование NSSet вместо NSArray.



0

В большинстве случаев вы не заботитесь о том, чтобы получить один конкретный объект из набора. Вы заботитесь о тестировании, чтобы увидеть, содержит ли набор объект. Вот для чего подходят наборы. Когда вы хотите увидеть, находится ли объект в коллекции, наборы намного быстрее, чем массивы.

Если вам все равно, какой предмет вы получите, используйте -anyObjectкоторый просто дает вам один предмет из набора, например, положить руку в сумку и что-то схватить.

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

Если вас волнует, какой объект вы получите, используйте -memberкоторый возвращает объект, или nil, если его нет в наборе. У вас уже должен быть объект, прежде чем вы его вызовете.

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

Вот код, который вы можете запустить в Xcode, чтобы понять больше

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);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.