1

「The Big Nerd Ranch Guide」(第 3 版) の優れた例「Parsing XML with NSXMLParser」に基づいて、NSManagedObjectsXML 解析を追加したいカテゴリを my に追加しました。これらのカテゴリは、解析機能のみを提供します。

これは、これらのカテゴリを実装した方法です: .h:

#import "IBCompany.h"
@interface IBCompany (Xml) <NSXMLParserDelegate>

- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;

@end

.m:

@implementation IBCompany (Xml) 

- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;
{
    NSData *xmlData = [xmlStr dataUsingEncoding:NSUTF8StringEncoding];
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
    parser.delegate = self;

    [parser parse];

    xmlData = nil;

    NSError *error;
    completionBlock(error);
    }


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"Issue"]) {
                IBIssue *issue = [NSEntityDescription insertNewObjectForEntityForName:@"IBIssue" inManagedObjectContext:self.managedObjectContext];
                issue.company = self;          
                issue.parentParserDelegate = self;
                parser.delegate = issue;
}

このコード スニペットでわかるように、パーサー デリゲートを他のサブクラス/XML 子要素に切り替えて、XML 要素の最後に到達し、デリゲートが戻されるまで、パーサーに属する次の XML 要素をさらに処理するようにします。親に。

これが、親デリゲートを子に格納する必要がある理由です。ただし、ivar とプロパティはカテゴリでは許可されていません。

この問題を回避すると思われるこの解決策を思いつきました:

子要素、h:

#import "IBIssue.h"

@interface IBIssue (Xml) <NSXMLParserDelegate>
@property id parentParserDelegate;
@end


#import "IBIssue+Xml.h"

@implementation IBIssue (Xml)

NSMutableString *currentString;
NSString *currentXmlDocument;

id _parentParserDelegate;

- (id)parentParserDelegate
{
    return _parentParserDelegate;
}

- (void)setParentParserDelegate:(id)parentParserDelegate;
{
    _parentParserDelegate = parentParserDelegate;
}

- (NSDateFormatter*)dateFormatter
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
    [dateFormatter setDateFormat:@"yyy-MM-dd"];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT: 0]];
    return dateFormatter;
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([elementName isEqualToString:@"IssueID"]) {
        currentString = [[NSMutableString alloc]init];

        if      ([attributeDict[@"Type"] isEqualToString:@"Ticker"])        self.ticker = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"Name"])          self.issueName = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"CUSIP"])         self.cusip = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"ISIN"])          self.isin = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"RIC"])           self.ric = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"SEDOL"])         self.sedol = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"DisplayRIC"])    self.displayRic = currentString;
        else if ([attributeDict[@"Type"] isEqualToString:@"InstrumentPI"]) ; //
        else if ([attributeDict[@"Type"] isEqualToString:@"QuotePI"])      ; //

    } else if ([elementName isEqualToString:@"Exchange"]) {
        currentString = [[NSMutableString alloc]init];

        self.exchangeCode = attributeDict[@"Code"];
        self.exchangeCountry = attributeDict[@"Country"];
        self.exchange = currentString;
    } else if ([elementName isEqualToString:@"MostRecentSplit"]) {
        currentString = [[NSMutableString alloc]init];

        self.mostRecentSplitDate = [self.dateFormatter dateFromString:attributeDict[@"Date"]];
        // self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // NSLog(@"appendString: %@", string);
    [currentString appendString:string];
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:@"Issue"]) {
        parser.delegate = self.parentParserDelegate;
    } else if ([elementName isEqualToString:@"MostRecentSplit"]) {
        self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
    }

    currentString = nil;
}

@end

_parentDelegateivar 宣言ブロックの外側で宣言され、実際の ivar ではないように見える変数にデリゲートを親に保存します。

このコードは私のテストではうまく機能し、開発プロセスの後半で問題になる何かを見逃したのか、それともこの設計は問題ないのか疑問に思っています。

それについてどう思いますか。

ありがとうございました!

4

1 に答える 1

0

コンパイラがその変数をどのように扱うかはわかりません。このタイプのすべてのオブジェクトが 1 つの変数だけを共有するように割り当てることはできますか? ある時点で複数の IBCompany が存在するように XML が解析されると、問題が発生する可能性があります。2 つの IBCompany オブジェクトを割り当てるテストを作成し、それらの両方に異なる値を _parentDelegate に書き込んでから、値が異なることをアサートします。

または、2 つの IBCompany オブジェクトが並行して解析される可能性がない場合は、問題を無視してください。XML が別の IBCompany 内に IBCompany を持つことができないこと、XML の複数の部分が並行して処理されないこと、および複数の XML ドキュメントが並行して処理されないことを確認する必要があります。

カテゴリの必要性がわかりません。カテゴリは、Cocoa フレームワークのクラスに機能を追加する場合など、既存のクラスにサブクラスを記述してはならない場合に役立ちます。カスタム サブクラスを作成しているので、サブクラスに ivar を追加してみませんか? Core Data バッキング ストアに保存されていない管理対象オブジェクトに追加の ivar を含めることができます。せいぜい拡張機能を使用して、XML 解析コードを管理対象オブジェクトの残りの部分から分離するだけです。

于 2012-10-29T14:57:20.493 に答える