2012-02-20 30 views
7

Biến thành một vấn đề nhỏ thú vị. Tôi đã viết một phương pháp để lọc một mảng các đối tượng độc đáo:Chuyển đổi NSArray thành NSSet, các cá thể lớp tùy chỉnh chuyển không nhất quán

- (NSArray*)distinctObjectsByAddress { 
    NSSet* uniqueSet = [NSSet setWithArray:self]; 
    NSArray* retArray = [uniqueSet allObjects]; 

    return retArray; 
} 

và đã viết một bài kiểm tra đơn vị để kiểm tra:

- (void)testDistinctObjectsByAddress5 { 
    Person* adam1 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil]; 
    Person* adam2 = [[Person alloc] initWithFirstName:@"adam" lastName:@"adam" andParent:nil]; 

    testPersonArray = [NSArray arrayWithObjects:adam1,adam2, nil]; 

    NSArray* checkArray = [testPersonArray distinctObjectsByAddress]; 

    STAssertEquals([checkArray count], [testPersonArray count], @"Array %@ counts should match %@ %@",checkArray,adam1,adam2); 
} 

Khá đơn giản. Phần thú vị là khoảng 80-90% thời gian thử nghiệm trôi qua và thường xuyên như vậy nó không thành công vì phương thức distinctObjectsByAddress chỉ trả về một đối tượng. Tôi đã có thể theo dõi nó để gọi [NSSet setWithArray:self] nhưng tôi cũng đã có thể xác minh rằng hai đối tượng người là hai đối tượng khác nhau (ít nhất là họ có địa chỉ khác nhau). Tôi giả định rằng setWithArray: chỉ là làm một địa chỉ cơ bản so sánh nhưng tôi không hiểu tại sao nó đôi khi sản xuất hai đối tượng như nó nên và đôi khi sản xuất chỉ có một.

Điều tôi vừa thử đã thay đổi adam2 để tên và họ không chính xác giống như adam1. Điều này dường như sửa lỗi. Liệu điểm này có phải là một số loại tối ưu hóa trình biên dịch khi các đối tượng hợp lý không?

+4

Tôi đoán rằng vấn đề là với Người và cách nó triển khai các phương thức băm và bình đẳng mà NSSet sử dụng. –

+1

Nó sẽ sử dụng 'isEqual:' như được định nghĩa bởi giao thức 'NSObject' để so sánh các đối tượng; bạn đã triển khai nó hay 'hash' trên' Person' chưa? – Tommy

+1

Ngoài vấn đề băm, bạn không kiểm tra rõ ràng adam1 và adam2 cho nil. Nếu thỉnh thoảng không thành công, điều đó sẽ giải thích thất bại kiểm tra. – bneely

Trả lời

9

Tôi giả định setWithArray mà chỉ được làm một địa chỉ cơ bản so sánh

Đó là không chính xác. NSSet sử dụng các phương thức -isEqual:-hash trên các đối tượng được thêm vào nó. Nó phụ thuộc vào cách chúng được thực hiện trong Person hoặc các lớp bậc trên của nó.

Nếu [person1 isEqual:person2] thì bạn sẽ mong đợi tập hợp chứa một đối tượng. Nếu không, thì tập hợp phải chứa hai đối tượng.

Đoán của tôi là Người không tuân theo the rules trong các phương thức -isEqual:-hash của mình. Nhiều khả năng, hai đối tượng là bằng nhau, nhưng băm của chúng không bằng nhau như chúng cần. (Ngoại trừ 10-20% thời gian mà bạn đang nhận được may mắn.)

Liệu điểm tối ưu hóa trình biên dịch này có giống nhau không?

Không, tối ưu hóa trình biên dịch sẽ hợp nhất hai đối tượng thành một.

3

Rất có thể bạn không triển khai hash cho Person và đôi khi các đối tượng giống nhau Person băm thành hai nhóm khác nhau.

+0

hmm, tôi đã ghi đè isEqual nhưng không có băm để có thể là vấn đề, tôi đã lấy mã NSSet ra vì nó có vẻ hơi 'hacky' và viết mã để so sánh địa chỉ của chính tôi. Nhưng tôi vẫn thực sự quan tâm là tại sao điều này lại xảy ra. Điều băm này có lẽ là nó, tôi sẽ kiểm tra và chấp nhận nếu nó là. – ACBurk

+0

@ACBurk trên mỗi tài liệu "Nếu hai đối tượng bằng nhau, chúng phải có cùng giá trị băm." (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html # // apple_ref/occ/intfm/NSObject/isEqual:); không thực hiện được NSObject đúng cách có thể dẫn đến bất kỳ hành vi nào. – Tommy

Các vấn đề liên quan