您是否正确解析了这个?有很多问题,有些是实质性的,有些是风格上的。首先,有几个实质性的观察:
您foundCharacters
假设每个元素只会调用一次。对于通常为真的 URL 之类的东西,但您不能保证这一事实。它应该只是将字符附加到字符串,并且最终字符串的后续存储和清理应该发生在didEndElement
.
无论您foundCharacter
是否正在检索您关心的元素的数据(更糟糕的是,元素之间的字符),您都会存储结果。仅存储foundCharacter
介于 adidStartElement
和didEndElement
您关心的元素名称之间的结果。
您didStartElement
正在执行alloc
,但它也没有执行init
。你应该总是init
同时做。稍后您也可以进行额外的属性设置,但永远不要忽略调用适当的init
方法。
您didEndElement
正在从服务器检索图像。您确实应该将数据的检索限制为仅 XML 本身(例如,在图像的情况下,图像的 URL),而不是从服务器检索其他项目。当您考虑更复杂的设计时,这一点尤其重要,为了最大限度地减少内存命中并改善用户响应时间,您可能希望使用延迟加载图像,这意味着您只想从XML 中检索 URL,并让 UI 处理是否以及何时检索图像。当您将来开始进行更复杂的开发时,您会后悔在这里进行图像检索。
您在和中进行的elementName
检查之间存在不平衡。对于您要为其检索数据的所有元素,您应该平衡调用。我知道您为我们提供了一个示例,为了简单起见,您仅从 XML 中检索单个元素,但在下面的示例中,我假设我们正在检索两个元素,并且平衡这些元素的重要性将变得显而易见。didStartElement
didEndElement
更多风格观察:
按照惯例,变量名通常以小写字母开头。类名通常以大写字母开头。我假设“EGS”是一个既定的类前缀(例如您的首字母或您公司的首字母缩写词),在这种情况下,类名可能是EGSBase
and EGSParser
。顺便说一句,我不太喜欢这个名称EGSBase
,因为它是一个旨在捕获特定类型的 XML 数据的类,但有一个通用名称。要么使用像 a 这样的通用数据结构,NSMutableDictionary
要么使用自定义类并给它一个有意义的名称(例如,根据您对“产品”的引用,我假设它是一个产品,因此我EGSProduct
在下面的示例中将其重命名.) 随意命名,但如果您使用有意义的属性名称,请给它一个有意义的名称。
我注意到您正在定义实例变量来支持您的属性。虽然这是一两年前的惯例,但最新版本的编译器使这变得不必要,甚至是不可取的。你有什么工作,但它不再被认为是最佳实践,并且是不可取的(因为你可以用一个拼写错误结束重复的实例变量并得到非常奇怪的行为)。最重要的是,不要为您的属性定义实例变量。让编译器为你做这件事。
我注意到您已将img
in定义egsBase
为IBOutlet
,而我发现您极不可能将其真正链接到 Interface Builder 中的任何内容。首先,它甚至不是UIView
后代(它是 a UIImage
,而不是 a UIImageView
)。其次,egsBase
它本身并不是一个用于 Interface Builder 控件的类。底线,这不是一个 Interface Builder 出口,所以它只是令人困惑使用IBOutlet
for img
。
我注意到您正在使用retain
. 如果您正在编写 ARC 代码,那应该是strong
(即使您的目标是 iOS 4.3)。如果您不编写 ARC 代码,那么您的代码中的其他地方就有泄漏。
您正在手动合成您的属性。你可以这样做,但这不是必需的。此外,现在的最佳做法是让您的属性的实例变量有一个前导下划线。请注意,如果您省略该@synthesize
语句,它将为您合成带有前导下划线的实例变量。关于您是否应该专门使用属性(我已经在下面完成)或使用实例变量,该领域存在一些争论。我不太在意,尽管新兴的约定可能倾向于更广泛地使用属性。如果您使用属性,尽管 (a)不要在初始化程序和 dealloc 方法中使用访问器方法;(b)在设置属性时始终使用访问器方法。
我注意到您正在定义属性和实例变量(特别是对于解析器),它们是私有实现细节。这里的新兴标准是将 .h 中定义的属性限制为其他类需要访问的属性。作为类的私有实现的一部分的任何其他属性通常在 .m 文件本身的私有类扩展中定义。这是一件小事,但是如果您遵守这种做法,您会发现将来使用您的类会更容易,您(或其他开发人员)不会对公共接口的一部分以及哪些部分感到困惑的私有实现。
我注意到你loadXMLByURL
返回了一个指向类本身的指针。通常,只有init
对象或创建新对象的类才会返回指向自身的指针。
您loadXMLByURL
可能应该返回(a)指向NSMutableArray
您构造的指针(返回nil
错误);或 (b)BOOL
成功/失败值。
对于您的自定义类,例如EGSBase
,编写一个方法很有用description
,因此您可以轻松地使用NSLog
它们。
所以,让我给你举个例子。假设您有一个如下所示的 XML:
<Products>
<products id="0">
<name>name1</name>
<img id="1">http://opentestdrive.com/images/first.png</img>
</products>
<products id="1">
<name>name2</name>
<img id="2">http://opentestdrive.com/images/second.png</img>
</products>
<products id="2">
<name>name3</name>
<img id="3">http://opentestdrive.com/images/img1.png</img>
</products>
<products id="3">
<name>name4</name>
<img id="4">http://opentestdrive.com/images/img2.png</img>
<img-subproduct id="0">http://opentestdrive.com/images/img5.png</img-subproduct>
<img-subproduct id="1">http://opentestdrive.com/images/img4.png</img-subproduct>
</products>
<products id="4">
<name>name5</name>
<img id="5">http://opentestdrive.com/images/img3.png</img>
<img-subproduct id="2">http://opentestdrive.com/images/img3.png</img-subproduct>
<img-subproduct id="3">http://opentestdrive.com/images/img2.png</img-subproduct>
</products>
<products id="5">
<name>name6</name>
<img id="6">http://opentestdrive.com/images/img4.png</img>
</products>
<products id="6">
<name>name7</name>
<img id="7">http://opentestdrive.com/images/img5.png</img>
</products>
</Products>
那么你EGSProduct
的定义如下:
// EGSProduct.h
#import <Foundation/Foundation.h>
@interface EGSProduct : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *imageUrlString;
@property (nonatomic, strong) NSString *imageIdentifier;
@property (nonatomic, strong) NSMutableArray *subProducts;
@end
和
// EGSProduct.m
#import "EGSProduct.h"
@implementation EGSProduct
- (NSString *)description
{
NSMutableString *result = [NSMutableString stringWithFormat:@"<EGSProduct %p; name='%@'; imageIdentifier='%@'; imageUrlString='%@'; subProducts=", self, self.name, self.imageIdentifier, self.imageUrlString];
NSMutableArray *subProductDescriptions = [NSMutableArray array];
for (EGSProduct *subProduct in self.subProducts)
{
[subProductDescriptions addObject:[subProduct description]];
}
[result appendFormat:@"%@>", [subProductDescriptions componentsJoinedByString:@"; "]];
return result;
}
@end
你的解析器可能看起来像:
// EGSParser.h
#import <Foundation/Foundation.h>
@interface EGSParser : NSObject
@property (nonatomic, strong) NSMutableArray *products;
- (BOOL)loadXMLByURL:(NSURL *)url;
@end
和
// EGSParser.m
#import "EGSParser.h"
#import "EGSProduct.h"
@interface EGSParser () <NSXMLParserDelegate>
@property (nonatomic, strong) NSXMLParser *parser;
@property (nonatomic, strong) NSMutableString *currentElementContent;
@property (nonatomic, strong) EGSProduct *currentProduct;
@property (nonatomic, strong) EGSProduct *currentSubProduct;
@end
@implementation EGSParser
- (BOOL)loadXMLByURL:(NSURL *)url
{
self.parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
self.parser.delegate = self;
return [self.parser parse];
}
#pragma mark - NSXMLParser
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.products = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"products"])
{
self.currentProduct = [[EGSProduct alloc] init];
self.currentProduct.imageIdentifier = attributeDict[@"id"];
}
else if ([elementName isEqualToString:@"img-subproduct"])
{
self.currentSubProduct = [[EGSProduct alloc] init];
self.currentSubProduct.imageIdentifier = attributeDict[@"id"];
if (self.currentProduct.subProducts == nil)
{
self.currentProduct.subProducts = [NSMutableArray array];
}
self.currentSubProduct.imageIdentifier = attributeDict[@"id"];
self.currentElementContent = [[NSMutableString alloc] init];
}
else if ([elementName isEqualToString:@"name"] || [elementName isEqualToString:@"img"])
{
self.currentElementContent = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// note, this construct of checking to see if it's nil is actually not needed
// (since sending a message to a nil object, by definition, does nothing)
// but I wanted to draw your attention to the fact that we set `currentElementContent`
// in `didStartElement` and, after saving it in our class, we set it to nil in
// `didEndElement`
if (self.currentElementContent != nil)
[self.currentElementContent appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"name"])
{
self.currentProduct.name = self.currentElementContent; // save the product name
self.currentElementContent = nil; // reset our `currentElementContent`
}
else if ([elementName isEqualToString:@"img"])
{
self.currentProduct.imageUrlString = [self.currentElementContent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
self.currentElementContent = nil;
}
else if ([elementName isEqualToString:@"img-subproduct"])
{
self.currentSubProduct.imageUrlString = self.currentElementContent;
self.currentElementContent = nil;
[self.currentProduct.subProducts addObject:self.currentSubProduct];
self.currentSubProduct = nil;
}
else if ([elementName isEqualToString:@"products"])
{
[self.products addObject:self.currentProduct];
self.currentProduct = nil;
}
}
@end
要使用此类,您可能会执行以下操作。如果您想在解析器完成(并超出范围)后继续使用 XML 结果,您只需要一个类属性(在下面的示例中,xmlProductsResults
. 如果您希望它可以被各种不同的视图控制器,您可以将此模型数据保存在某个共享类(例如模型单例)中,作为应用程序委托的属性,或者将其作为参数从视图控制器传递到视图控制器。
NSURL *url = [NSURL URLWithString:@"http://opentestdrive.com/Products.xml"];
EGSParser *parser = [[EGSParser alloc] init];
if ([parser loadXMLByURL:url])
{
NSLog(@"success; products=%@", parser.products);
self.xmlProductsResults = parser.products;
}
else
{
NSLog(@"fail");
}
你可以用你的解析器做更多的事情(更健壮地验证格式不正确的 XML 文件,报告错误,如果有的话,返回等等)。就个人而言,我已经完成了足够多的 XML 解析,现在我使用了一个非常通用的解析器,它可以在几行内解析我正在处理的大部分 XML 文件,享受解析器类的最大重用,但那是一座太远的桥梁对于这次谈话。我在这个讨论中已经走得太远了。