Якщо ви не можете отримати об'єкт з objectAtIndex: з NSSet, то як отримати об’єкти?
Якщо ви не можете отримати об'єкт з objectAtIndex: з NSSet, то як отримати об’єкти?
Відповіді:
Існує кілька випадків використання для набору. Ви можете перерахувати через (наприклад, з enumerateObjectsUsingBlock
або NSFastEnumeration), зателефонувати containsObject
для тестування на членство, використати anyObject
для отримання члена (не випадкового) або перетворити його в масив (не в конкретному порядку) за допомогою allObjects
.
Набір підходить, коли ви не хочете дублікатів, не дбаєте про порядок і хочете швидке тестування членства.
member:
повідомлення. Якщо він повертається nil
, набір не містить об'єкта, рівного тому, який ви передали; якщо він повертає вказівник об'єкта, то вказівник, який він повертає, є на об'єкт, який вже є в наборі. Об'єкти в наборі повинні реалізовуватися, hash
і isEqual:
щоб це було корисно.
hash
потрібно впроваджувати; просто би пішло набагато швидше, якби ти зробив це.
hash
протоколу NSObject: "Якщо два об'єкти рівні (як визначено isEqual:
методом), вони повинні мати однакове хеш-значення". В даний час реалізації NSObject hash
та isEqual:
використання ідентичності об’єкта (адреси). Якщо ви переосмислюєте isEqual:
, ви встановлюєте можливість об'єктів, не однакових, але рівних, які, якщо ви також не переопрацьовуєте hash
, все одно матимуть різні хеши. Це порушує вимогу рівних хешів об'єктів.
member:
контейнер, буде виглядати лише цей bucket (саме тому набори набагато швидші, ніж масиви при тестуванні членства, а словники набагато швидші, ніж паралельні масиви при пошуку ключ-значення). Якщо об'єкт, який шукається, має неправильний хеш, контейнер буде виглядати у неправильному відрі, а не знаходити відповідність.
-[NSObject hash]
0. Це багато що пояснює. = S
NSSet не має методу objectAtIndex:
Спробуйте зателефонувати allObjects, який повертає NSArray всіх об'єктів.
можна використовувати filteredSetUsingPredicate, якщо у вас є якийсь унікальний ідентифікатор для вибору потрібного вам об’єкта.
Спочатку створіть предикат (припускаючи, що ваш унікальний ідентифікатор в об'єкті називається "ідентифікатор", і це NSString):
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];
А потім виберіть об’єкт, використовуючи присудок:
NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Для 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: as
- (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.
for (id currentElement in mySet)
{
// ** some actions with currentElement
}
Здебільшого ви не дбаєте про те, щоб отримати один конкретний об’єкт із набору. Ви дбаєте про тестування, щоб перевірити, чи містить набір об’єкт. Ось для чого гарні набори. Коли ви хочете побачити, чи є об'єкт у колекції, набори набагато швидші, ніж масиви.
Якщо вам байдуже, який предмет ви отримаєте, використовуйте, -anyObject
який просто дає вам один предмет із набору, наприклад, поклавши руку в сумку і щось схопивши.
Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects
Якщо ви дбаєте про те, який об’єкт ви отримаєте, використовуйте, -member
який повертає вам об’єкт, або нуль, якщо його немає в наборі. Потрібно вже мати об'єкт, перш ніж називати його.
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);
}
[[nsSetObjects allObjects] objectAtIndex: anyInteger]