Lấy một đối tượng từ NSSet


Câu trả lời:


139

Có một số trường hợp sử dụng cho một tập hợp. Bạn có thể liệt kê thông qua (ví dụ với enumerateObjectsUsingBlockhoặc NSFastEnumeration), gọi containsObjectđể kiểm tra tư cách thành viên, sử dụng anyObjectđể nhận thành viên (không phải ngẫu nhiên) hoặc chuyển đổi nó thành một mảng (không theo thứ tự cụ thể) vớiallObjects .

Một bộ phù hợp khi bạn không muốn trùng lặp, không quan tâm đến thứ tự và muốn kiểm tra tư cách thành viên nhanh chóng.


7
Bạn cũng có thể tra cứu một đối tượng đã biết theo bản sao có thể xảy ra bằng cách gửi member:tin nhắn cho nhóm. Nếu nó trả về nil, tập hợp không chứa đối tượng bằng đối tượng bạn đã chuyển; nếu nó trả về một con trỏ đối tượng, thì con trỏ nó trả về là đối tượng đã có trong tập hợp. Các đối tượng trong tập hợp phải triển khai hashisEqual:để điều này trở nên hữu ích.
Peter Hosey

@PeterHosey Tôi không nghĩ hash cần phải triển khai; nó sẽ nhanh hơn rất nhiều nếu bạn làm như vậy.
fumoboy007.

2
@ fumoboy007: Để lưu trữ trong một bộ hoặc sử dụng làm khóa trong từ điển, có. Từ tài liệu hashtrong giao thức NSObject: "Nếu hai đối tượng bằng nhau (như được xác định bởi isEqual:phương pháp), chúng phải có cùng giá trị băm." Hiện tại, việc triển khai hashisEqual:sử dụng danh tính của đối tượng (địa chỉ) của NSObject . Nếu bạn ghi đè isEqual:, bạn đang thiết lập khả năng các đối tượng không giống nhau nhưng bằng nhau — mà nếu bạn không ghi đè hash, sẽ vẫn có các hàm băm khác nhau. Điều đó vi phạm yêu cầu rằng các đối tượng bằng nhau có giá trị băm bằng nhau.
Peter Hosey

1
@ fumoboy007: Hãy xem xét cách hoạt động của bảng băm: Vùng chứa có một mảng gồm một số nhóm và sử dụng hàm băm của mỗi đối tượng đến để xác định nhóm đối tượng đó sẽ ở trong. Đối với các tra cứu như vậy member:, vùng chứa sẽ chỉ tìm trong một nhóm xô (đó là lý do tại sao tập hợp nhanh hơn nhiều so với mảng khi kiểm tra tư cách thành viên và từ điển nhanh hơn nhiều so với mảng song song khi tra cứu khóa-giá trị). Nếu đối tượng được tìm kiếm có hàm băm sai, thì vùng chứa sẽ tìm sai nhóm và không tìm thấy đối tượng phù hợp.
Peter Hosey

@PeterHosey Rất tiếc… Tôi đã nhầm tưởng rằng cài đặt mặc định -[NSObject hash]là 0. Điều đó giải thích rất nhiều. = S
fumoboy007

31

NSSet không có đối tượng phương thứcAtIndex:

Thử gọi allObjects trả về một NSArray của tất cả các đối tượng.


bạn có bất kỳ ý tưởng nếu mảng được trả về có thứ tự không? nói cách khác, nếu tôi thêm các đối tượng vào tập hợp bằng cách sử dụng "setByAddingObject" và tôi đã sử dụng "allObjects", thì sao? các phần tử trong mảng được sắp xếp theo thứ tự tôi đã thêm các đối tượng?
iosMentalist

chỉ cần sắp xếp mảng kết quả bằng NSSortPredicate và bạn sẽ ổn thôi
Jason

Nếu bạn chỉ quan tâm đến một đối tượng cụ thể, tốt hơn hãy sử dụng phương pháp LọcSetUsingPredicate.
akw

17

có thể sử dụng filterSetUsingPredicate nếu bạn có một số loại mã định danh duy nhất để chọn đối tượng bạn cần.

Đầu tiên tạo vị từ (giả sử id duy nhất của bạn trong đối tượng được gọi là "định danh" và nó là một NSString):

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

Và sau đó chọn đối tượng bằng cách sử dụng vị ngữ:

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

13

NSArray *myArray = [myNSSet allObjects];

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

thay thế NSUInteger bằng chỉ mục của đối tượng mong muốn của bạn.


1
Nên là: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.

8

Đối với 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 sử dụng phương thức isEqual: (mà các đối tượng bạn đặt vào tập hợp đó phải ghi đè, ngoài ra, phương thức băm) để xác định xem một đối tượng có bên trong nó hay không.

Vì vậy, ví dụ: nếu bạn có một mô hình dữ liệu xác định tính duy nhất của nó bằng một giá trị id (giả sử thuộc tính là:

@property NSUInteger objectID;

thì bạn sẽ triển khai isEqual: as

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

và bạn có thể triển khai hàm băm:

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

Sau đó, bạn có thể lấy một đối tượng có ID đó (cũng như kiểm tra xem nó có nằm trong NSSet hay không) với:

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

Nhưng đúng vậy, cách duy nhất để lấy thứ gì đó ra khỏi NSSet là xem xét điều đó xác định tính độc đáo của nó ngay từ đầu.

Tôi chưa thử nghiệm cái gì nhanh hơn, nhưng tôi tránh sử dụng kiểu liệt kê vì điều đó có thể là tuyến tính trong khi sử dụng phương thức member: sẽ nhanh hơn nhiều. Đó là một trong những lý do để thích sử dụng NSSet thay vì NSArray.



0

Hầu hết thời gian bạn không quan tâm đến việc lấy một đối tượng cụ thể từ một tập hợp. Bạn quan tâm đến việc kiểm tra xem một tập hợp có chứa một đối tượng hay không. Đó là những gì bộ là tốt cho. Khi bạn muốn xem liệu một đối tượng có nằm trong một tập hợp hay không sẽ nhanh hơn nhiều so với mảng.

Nếu bạn không quan tâm đến đối tượng bạn nhận được, sử dụng -anyObjectmà chỉ cung cấp cho bạn một đối tượng từ bộ này, giống như đặt bàn tay của bạn trong một túi và lấy một cái gì đó.

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

Nếu bạn quan tâm đến đối tượng bạn nhận được, hãy sử dụng -memberđối tượng trả lại cho bạn, hoặc nil nếu nó không có trong tập hợp. Bạn cần phải có đối tượng trước khi gọi nó.

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

Đây là một số mã bạn có thể chạy trong Xcode để hiểu thêm

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.