Tôi hy vọng tất cả các bạn sẽ tha thứ cho tôi đi ra ngoài cánh một ở đây, nhưng tôi muốn giải quyết các câu hỏi tổng quát hơn của phân tích tài liệu XML trong Cocoa mà không cần các câu lệnh if-else. Câu hỏi như ban đầu được chỉ định gán văn bản phần tử hiện tại cho một biến cá thể của đối tượng ký tự. Như jmah đã chỉ ra, điều này có thể được giải quyết bằng cách sử dụng mã hóa khóa-giá trị. Tuy nhiên, trong một tài liệu XML phức tạp hơn, điều này có thể không thực hiện được. Hãy xem xét ví dụ như sau.
<xmlroot>
<corporationID>
<stockSymbol>EXAM</stockSymbol>
<uuid>31337</uuid>
</corporationID>
<companyName>Example Inc.</companyName>
</xmlroot>
Có nhiều cách để giải quyết vấn đề này. Tắt đầu của tôi, tôi có thể nghĩ đến hai sử dụng NSXMLDocument. Việc đầu tiên sử dụng NSXMLElement. Nó khá đơn giản và không liên quan đến vấn đề if-else. Bạn chỉ cần lấy phần tử gốc và đi qua từng phần tử có tên của nó.
NSXMLElement* root = [xmlDocument rootElement];
// Assuming that we only have one of each element.
[character setCorperationName:[[[root elementsForName:@"companyName"] objectAtIndex:0] stringValue]];
NSXMLElement* corperationId = [root elementsForName:@"corporationID"];
[character setCorperationStockSymbol:[[[corperationId elementsForName:@"stockSymbol"] objectAtIndex:0] stringValue]];
[character setCorperationUUID:[[[corperationId elementsForName:@"uuid"] objectAtIndex:0] stringValue]];
Tiếp theo sử dụng NSXMLNode tổng quát hơn, đi qua cây và trực tiếp sử dụng cấu trúc if-else.
// The first line is the same as the last example, because NSXMLElement inherits from NSXMLNode
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
if([[aNode name] isEqualToString:@"companyName"]){
[character setCorperationName:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"corporationID"]){
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
if([[aNode name] isEqualToString:@"stockSymbol"]){
[character setCorperationStockSymbol:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"uuid"]){
[character setCorperationUUID:[aNode stringValue]];
}
}
}
}
Đây là một ứng cử viên tốt để loại bỏ cấu trúc if-else, nhưng giống như vấn đề ban đầu, chúng tôi không thể đơn giản sử dụng chuyển đổi trường hợp ở đây. Tuy nhiên, chúng ta vẫn có thể loại bỏ if-else bằng cách sử dụng performSelector. Bước đầu tiên là xác định phương thức cho mỗi phần tử.
- (NSNode*)parse_companyName:(NSNode*)aNode
{
[character setCorperationName:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID:(NSNode*)aNode
{
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
[self invokeMethodForNode:aNode prefix:@"parse_corporationID_"];
}
return [aNode previousNode];
}
- (NSNode*)parse_corporationID_stockSymbol:(NSNode*)aNode
{
[character setCorperationStockSymbol:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID_uuid:(NSNode*)aNode
{
[character setCorperationUUID:[aNode stringValue]];
return aNode;
}
Phép thuật xảy ra trong phương thức invokeMethodForNode: prefix:. Chúng tôi tạo bộ chọn dựa trên tên của phần tử và thực hiện bộ chọn đó bằng aNode làm tham số duy nhất. Ví dụ, chúng tôi đã loại bỏ sự cần thiết cho một tuyên bố nếu khác. Đây là mã cho phương thức đó.
- (NSNode*)invokeMethodForNode:(NSNode*)aNode prefix:(NSString*)aPrefix
{
NSNode* ret = nil;
NSString* methodName = [NSString stringWithFormat:@"%@%@:", prefix, [aNode name]];
SEL selector = NSSelectorFromString(methodName);
if([self respondsToSelector:selector])
ret = [self performSelector:selector withObject:aNode];
return ret;
}
Bây giờ, thay vì lớn hơn if-else của chúng tôi (một trong đó phân biệt giữa CompanyName và corporationID), chúng ta có thể chỉ cần viết một dòng mã
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}
Bây giờ tôi xin lỗi nếu tôi có bất kỳ của điều này sai, nó đã được một thời gian kể từ khi tôi đã viết bất cứ điều gì với NSXMLDocument, nó vào cuối đêm và tôi đã không thực sự kiểm tra mã này. Vì vậy, nếu bạn thấy bất cứ điều gì sai, xin vui lòng để lại một bình luận hoặc chỉnh sửa câu trả lời này.
Tuy nhiên, tôi tin rằng tôi đã chỉ cho thấy cách chọn đúng tên có thể được sử dụng trong Cocoa để loại bỏ hoàn toàn các câu lệnh if-else trong trường hợp như thế này. Có một vài trường hợp mắc kẹt và góc. PerformSelector: họ của các phương thức chỉ nhận 0, 1, hoặc 2 phương thức đối số mà các đối số và kiểu trả về là các đối tượng, vì vậy nếu các kiểu đối số và kiểu trả về không phải là các đối tượng, hoặc nếu có nhiều hơn hai đối số, thì bạn sẽ phải sử dụng NSInvocation để gọi nó. Bạn phải đảm bảo rằng các tên phương thức bạn tạo sẽ không gọi các phương thức khác, đặc biệt nếu đích của cuộc gọi là một đối tượng khác, và lược đồ đặt tên phương thức cụ thể này sẽ không hoạt động trên các phần tử có ký tự không phải chữ số. Bạn có thể vượt qua điều đó bằng cách thoát khỏi các tên phần tử XML trong tên phương thức của bạn bằng cách nào đó, hoặc bằng cách xây dựng một NSDictionary bằng cách sử dụng các tên phương thức như các khóa và các bộ chọn làm các giá trị. Điều này có thể nhận được khá nhiều bộ nhớ và sẽ mất nhiều thời gian hơn. performSelector công văn như tôi mô tả là khá nhanh. Đối với các câu lệnh if-else rất lớn, phương thức này thậm chí có thể nhanh hơn câu lệnh if-else.
Điều này không giúp gì cả, nhưng trong VB.Net, bạn có thể sử dụng lệnh chuyển (chọn trong vb) trên bất kỳ loại giá trị nào và trình biên dịch sẽ thực hiện chuyển đổi sang dạng if-elseif-else khi biên dịch , nếu không thể thực hiện được bằng nhảy trực tiếp. Cá nhân, tôi nghĩ rằng tất cả các ngôn ngữ nên có tính năng này. – Kibbee
Câu trả lời liên quan: http://stackoverflow.com/questions/8161737/can-objective-c-switch-on-nsstring/10177956#10177956 –