私は Objective-C、XCode、iPhone の開発全般に不慣れで、Core Data と NSXMLParser に問題があります。
Apples のチュートリアル SeismicXML (NSXMLParser 用) と Core Data on iPhone チュートリアルに従っていると、マネージド オブジェクト モデルのエンティティ プロパティに値を割り当てるときに問題が発生しました。
状況を説明するために、私のコードは、SeismicXML プロジェクトが使用する標準の NSObject ではなく、CoreData を使用して currentParsedCharacterData を管理対象オブジェクトに割り当てることによって、SeismicXML の例と異なるだけです。
以下は、私の管理対象オブジェクトからの説明出力です。
county = "-53.25354768,4.256547";
friendly = "-53.25354768,4.256547";
image = nil;
latitude = -53.253547684;
link = "-53.25354768,4.256547";
longitude = nil;
name = "-53.25354768,4.256547";
postcode = "-53.25354768,4.256547";
shopDescription = nil;
shopID = 0;
tag = "-53.25354768,4.256547";
tags = (
);
telephone = "-53.25354768,4.256547";
town = "-53.25354768,4.256547";
発生しているように見えるのは、すべての属性/プロパティに XML フィードの最後のノードの値が割り当てられていることです。それはたまたま経度、緯度です。しかし、プロパティの割り当て時に解析された文字データをログに記録する場合、それは期待される (そして正しい) 値ですが、このオブジェクトの説明を出力する場合、すべての文字列値は間違っており、数値/その他の値は単に 0 または nil です。
どんな提案でも大歓迎です。必要に応じて、私が使用しているのと同じ XML フィードでこの動作を示す小さなプロジェクトをノックアップできます。
編集:
これは、同じエラーが発生する管理対象オブジェクトに情報を取得するために私が行っていることの簡略化された例です。
便宜上、プロジェクトのzipを持っています http://willb.ro/CoreDataProblemExample.zip
Debug Output 2009-11-16 14:31:20.357 ShittyExample[4360:4d07] Company Description: (entity: Company; id: 0x3f6e9e0 ; data: { companyDescription = "トップ ショップは、小売部門の主要なブランドです"; companyID = 66136112; name = "Top Shop は、小売部門のトップ ブランドです"; })
//XML
<channel>
<company id="1">
<name>Top Shop</name>
<description>Top Shop are a leading brandname in the retail sector.</description>
</company>
</channel>
// FeedImporter.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class RootViewController, Company;
@interface FeedImporter : NSObject {
NSManagedObjectContext *managedObjectContext;
RootViewController *rootViewController;
NSMutableArray *companyList;
// for downloading the xml data
NSURLConnection *companyFeedConnection;
NSMutableData *companyData;
// these variables are used during parsing
Company *currentCompanyObject;
NSMutableArray *currentParseBatch;
NSUInteger parsedCompaniesCounter;
NSMutableString *currentParsedCharacterData;
BOOL accumulatingParsedCharacterData;
BOOL didAbortParsing;
}
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain) RootViewController *rootViewController;
@property (nonatomic, retain) NSMutableArray *companyList;
@property (nonatomic, retain) NSURLConnection *companyFeedConnection;
@property (nonatomic, retain) NSMutableData *companyData;
@property (nonatomic, retain) Company *currentCompanyObject;
@property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
@property (nonatomic, retain) NSMutableArray *currentParseBatch;
- (void)parseFeed;
- (void)addCompaniesToList:(NSArray *)companies;
- (void)handleError:(NSError *)error;
@end
// FeedImporter.m
#import "FeedImporter.h"
#import "RootViewController.h"
#import <CFNetwork/CFNetwork.h>
#import "Company.h"
@implementation FeedImporter
@synthesize managedObjectContext;
@synthesize rootViewController;
@synthesize companyList;
@synthesize companyFeedConnection;
@synthesize companyData;
@synthesize currentCompanyObject;
@synthesize currentParseBatch;
@synthesize currentParsedCharacterData;
- (void)dealloc {
[super dealloc];
[managedObjectContext release];
[rootViewController release];
[companyList release];
[companyFeedConnection release];
[companyData release];
[currentCompanyObject release];
[currentParseBatch release];
[currentParsedCharacterData release];
}
- (id)init {
if(self = [super init]) {
// Custom loading logic goes here..
}
return self;
}
- (void)parseFeed {
static NSString *feedURLString = @"http://willb.ro/companies.xml";
NSURLRequest *companyURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];
self.companyFeedConnection = [[[NSURLConnection alloc] initWithRequest:companyURLRequest delegate:self] autorelease];
NSAssert(self.companyFeedConnection != nil, @"Failure to create URL connection.");
// Start the status bar network activity indicator. We'll turn it off when the connection finishes or experiences an error.
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
#pragma mark NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.companyData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[companyData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if ([error code] == kCFURLErrorNotConnectedToInternet) {
// if we can identify the error, we can present a more precise message to the user.
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"No Connection Error", @"Error message displayed when not connected to the Internet.") forKey:NSLocalizedDescriptionKey];
NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:userInfo];
[self handleError:noConnectionError];
} else {
// otherwise handle the error generically
[self handleError:error];
}
self.companyFeedConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.companyFeedConnection = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[NSThread detachNewThreadSelector:@selector(parseCompanyData:) toTarget:self withObject:companyData];
self.companyData = nil;
}
- (void)parseCompanyData:(NSData *)data {
// You must create a autorelease pool for all secondary threads.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
self.currentParseBatch = [NSMutableArray array];
self.currentParsedCharacterData = [NSMutableString string];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];
if ([self.currentParseBatch count] > 0) {
[self performSelectorOnMainThread:@selector(addCompaniesToList:) withObject:self.currentParseBatch waitUntilDone:NO];
}
self.currentParseBatch = nil;
self.currentCompanyObject = nil;
self.currentParsedCharacterData = nil;
// Save to our MOC...
NSError *saveError;
if(![self.managedObjectContext save:&saveError]) {
// Handle MOM save error
NSLog(@"error while saving shop to managed object model");
NSError* error;
if(![[self managedObjectContext] save:&error]) {
NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(@" DetailedError: %@", [detailedError userInfo]);
}
}
else {
NSLog(@" %@", [error userInfo]);
}
}
}
else
{
NSLog(@"MOC saved sucessfully");
}
[parser release];
[pool release];
}
#pragma mark Parser constants
// Limit the number of parsed companies to 50.
static const const NSUInteger kMaximumNumberOfCompaniesToParse = 50;
static NSUInteger const kSizeOfCompanyBatch = 10;
static NSString * const kChannelElementName = @"channel";
static NSString * const kCompanyElementName = @"company";
static NSString * const kCompanyNameElementName = @"name";
static NSString * const kCompanyDescriptionElementName = @"description";
- (void)addCompaniesToList:(NSArray *)companies {
[self.companyList addObjectsFromArray:companies];
// The table needs to be reloaded to reflect the new content of the list.
[rootViewController.tableView reloadData];
}
#pragma mark NSXMLParser delegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (parsedCompaniesCounter >= kMaximumNumberOfCompaniesToParse) {
didAbortParsing = YES;
[parser abortParsing];
}
if ([elementName isEqualToString:kCompanyElementName]) {
Company *company = (Company *)[NSEntityDescription insertNewObjectForEntityForName:@"Company" inManagedObjectContext:self.managedObjectContext];
self.currentCompanyObject = company;
[company release];
int companyIDInt = (int)[attributeDict valueForKey:@"id"];
NSNumber *companyID = [NSNumber numberWithInt:companyIDInt];
[self.currentCompanyObject setCompanyID:companyID];
}
else if ([elementName isEqualToString:kCompanyElementName] || [elementName isEqualToString:kCompanyNameElementName] || [elementName isEqualToString:kCompanyDescriptionElementName]) {
accumulatingParsedCharacterData = YES;
[currentParsedCharacterData setString:@""];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kCompanyElementName]) {
//NSLog(@"currentEarthquakeObject: %@", currentEarthquakeObject);
[self.currentParseBatch addObject:self.currentCompanyObject];
parsedCompaniesCounter++;
if (parsedCompaniesCounter % kSizeOfCompanyBatch == 0) {
[self performSelectorOnMainThread:@selector(addCompaniesToList:) withObject:self.currentParseBatch waitUntilDone:NO];
self.currentParseBatch = [NSMutableArray array];
}
//NSLog(@"Reached end of company. Follows is a description of our company object: %@", [self.currentCompanyObject description]);
NSLog(@"Company Description: %@", [self.currentCompanyObject description]);
}
else if ([elementName isEqualToString:kCompanyNameElementName]) {
// Company Name
[self.currentCompanyObject setName:self.currentParsedCharacterData];
//NSLog(@"%@",self.currentParsedCharacterData);
}
else if ([elementName isEqualToString:kCompanyDescriptionElementName]) {
// Company Description
[self.currentCompanyObject setCompanyDescription:self.currentParsedCharacterData];
//NSLog(@"%@",self.currentParsedCharacterData);
}
accumulatingParsedCharacterData = NO;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (accumulatingParsedCharacterData) {
[self.currentParsedCharacterData appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
if (didAbortParsing == NO) {
[self performSelectorOnMainThread:@selector(handleError:) withObject:parseError waitUntilDone:NO];
}
}
- (void)handleError:(NSError *)error {
NSString *errorMessage = [error localizedDescription];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error Title", @"Title for alert displayed when download or parse error occurs.") message:errorMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
@end