Отримання об'єкта з NSSet


Відповіді:


139

Існує кілька випадків використання для набору. Ви можете перерахувати через (наприклад, з 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, і вам буде добре
Jason

Якщо вас цікавить лише один конкретний об'єкт, краще скористайтеся підходом filteredSetUsingPredicate.
akw

17

можна використовувати filteredSetUsingPredicate, якщо у вас є якийсь унікальний ідентифікатор для вибору потрібного вам об’єкта.

Спочатку створіть предикат (припускаючи, що ваш унікальний ідентифікатор в об'єкті називається "ідентифікатор", і це 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 *)]
John D.

8

Для Swift3 та iOS10:

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

6

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.



0

Здебільшого ви не дбаєте про те, щоб отримати один конкретний об’єкт із набору. Ви дбаєте про тестування, щоб перевірити, чи містить набір об’єкт. Ось для чого гарні набори. Коли ви хочете побачити, чи є об'єкт у колекції, набори набагато швидші, ніж масиви.

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