Если вы не можете получить объект с помощью objectAtIndex: из NSSet, то как вы извлекаете объекты?
Получение объекта из NSSet
Ответы (7)
Существует несколько вариантов использования множества. Вы можете перечислить (например, с помощью enumerateObjectsUsingBlock
или NSFastEnumeration), вызовите containsObject
для проверки членства используйте anyObject
в получить элемент (не случайный) или преобразовать его в массив (в произвольном порядке) с помощью allObjects
.
Набор подходит, когда вы не хотите дубликатов, не заботитесь о порядке и хотите быстро проверить членство.
member:
. Если он возвращает nil
, набор не содержит объекта, равного тому, который вы передали; если он возвращает указатель на объект, то указатель, который он возвращает, относится к объекту, уже находящемуся в наборе. Объекты в наборе должны реализовывать hash
и isEqual:
, чтобы это было полезно.
- person Peter Hosey; 30.09.2010
hash
нужно реализовывать; если бы вы это сделали, все бы пошло намного быстрее.
- person fumoboy007; 12.08.2013
hash
в протоколе NSObject: «Если два объекта равны (как определено методом isEqual:
), они должны иметь одинаковое хеш-значение». В настоящее время реализации NSObject hash
и isEqual:
используют идентификатор объекта (адрес). Если вы переопределяете isEqual:
, вы настраиваете возможность объектов, которые не идентичны, но равны, которые, если вы также не переопределяете hash
, по-прежнему будут иметь разные хэши. Это нарушает требование, чтобы одинаковые объекты имели одинаковые хэши.
- person Peter Hosey; 12.08.2013
member:
, контейнер будет искать только в этом одно ведро (именно поэтому наборы намного быстрее, чем массивы при тестировании на членство, а словари намного быстрее, чем параллельные массивы при поиске ключ-значение). Если искомый объект имеет неправильный хэш, то контейнер будет искать не в том сегменте и не найдет совпадения.
- person Peter Hosey; 12.08.2013
-[NSObject hash]
по умолчанию равна 0. Это многое объясняет. =С
- person fumoboy007; 13.08.2013
NSSet не имеет метода objectAtIndex:
Попробуйте вызвать allObjects, который возвращает NSArray всех объектов.
можно использовать filteredSetUsingPredicate, если у вас есть какой-то уникальный идентификатор для выбора нужного вам объекта.
Сначала создайте предикат (при условии, что ваш уникальный идентификатор в объекте называется «идентификатор» и является NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];
А затем выберите объект, используя предикат:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
NSArray *myArray = [myNSSet allObjects];
MyObject *object = [myArray objectAtIndex:(NSUInteger *)]
замените NSUInteger индексом желаемого объекта.
Для Swift3 и iOS10:
//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
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.
Большую часть времени вы не заботитесь о том, чтобы получить один конкретный объект из набора. Вы заботитесь о тестировании, чтобы увидеть, содержит ли набор объект. Вот для чего подходят наборы. Когда вы хотите увидеть, находится ли объект в коллекции, наборы работают намного быстрее, чем массивы.
Если вам не важно, какой объект вы получите, используйте -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);
}