Objective-C - EXC_BAD_ACCESS error with ALAsset and XmlWriter - ios

I'm using the ALAssetLibrary to retrieve information about the images on the device (IOS Simulator) for the moment.
Now that I have the needed information, I want to write them like an xml file. To do this I'm using the XmlStreamWriter (https://github.com/skjolber/xswi) simple and easy to use. My problem is that I'm having a EXC_BAD_ACCESS (code=1) error when I run the application. I know that it is related to the streamWriter because if I comment this lines of code the program work perfectly.
Here there is my code (I'm using ARC):
XMLWriter* xmlWriter = [[XMLWriter alloc]init];
[xmlWriter writeStartDocumentWithEncodingAndVersion:#"UTF-8" version:#"1.0"];
[xmlWriter writeStartElement:#"Photos"];
NSMutableArray *list = [[NSMutableArray alloc] init];
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
if (asset){
[xmlWriter writeStartElement:#"Photos"];
NSString *description = [asset description];
NSRange first = [description rangeOfString:#"URLs:"];
NSRange second = [description rangeOfString:#"?id="];
NSString *path = [description substringWithRange: NSMakeRange(first.location + first.length, second.location - (first.location + first.length))];
[xmlWriter writeAttribute:#"id" value:path];
[xmlWriter writeEndElement:#"Photos"];
}
}];
}
} failureBlock:^(NSError *error) {
NSLog(#"error enumerating AssetLibrary groups %#\n", error);
}];
[xmlWriter writeEndElement];
[xmlWriter writeEndDocument];
NSString* xml = [xmlWriter toString];
NSLog(#"XML: %#", xml);
Any idea of what can be the problem?
I also have an image with the related error:
Thanks

enumerateGroupsWithTypes calls the block to process the found information asynchronously. So, you are calling [xmlWriter writeEndDocument before you have ever actually written any real content into the writer.
You need to change how you complete the write operation so that it is done inside the block and when group is passed as nil. Add an else block to your existing check and put
[xmlWriter writeEndElement];
[xmlWriter writeEndDocument];
NSString* xml = [xmlWriter toString];
NSLog(#"XML: %#", xml);
In it (and whatever you subsequently do).

Related

See the variable in the same method=null ios

I use library soap the return of this function in on nsdictionary i change it into nsarray after i close ];} and type nslog it give that nsarray =null but if i put it before close of the method in the method give data
- (void)showlinks
{
SYSoapClient *show_links_obj = [SYSoapClient new];
links_tags = [[NSMutableArray alloc] initWithObjects:#"empid", #"type", nil];
links_vars = [[NSMutableArray alloc] initWithObjects:txt_username, type_user, nil];
[show_links_obj callSoapServiceWithParameters__functionName:#"getlinks"
tags:links_tags
vars:links_vars
callback:^(NSDictionary *result,
BOOL response)
{
link_raw_Data = [result allValues];
link_con_Data = [link_raw_Data componentsJoinedByString:#""];
//NSArray *links = [con_Data componentsSeparatedByString:#"#"];
links = [link_con_Data componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"#^"]];
NSUInteger x = links.count;
NSLog(#"%#",link_raw_Data);
//NSLog(#"%d",x);
//NSLog(#"%#",links[5]);
}];
NSLog(#"%#",links[5]);
}
Judging by the callback argument to the soap service, it's an asynchronous call. This means that it will return right away, do the networking on a background thread and call the callback sometime later when it is done.
Since it's asynchronous and doesn't wait/block, the log statement just after it will be executed before getting any data back from the soap service.

Saving 1 million records to core data taking so much time

I am saving 1 million records from CSV to the core database. It takes near about 35min to save all the records.Is there anyway to reduce the insertion time. Here is the my code which i used to insert the records :
NSString *resourceFileName = #"npidata_20140209_reduced2";
NSString *pathToFile =[[NSBundle mainBundle] pathForResource: resourceFileName ofType: #"csv"];
NSString* content = [NSString stringWithContentsOfFile:pathToFile
encoding:NSUTF8StringEncoding
error:NULL];
__block int count = 0;
[content enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) {
NSLog(#"LINE==%#",line);
NSArray *lineComponents=[line componentsSeparatedByString:#","];
if(lineComponents){
float f=[[lineComponents objectAtIndex:0] floatValue];
NSString *strNPI=[lineComponents objectAtIndex:0];
NSString *strProviderLastName=[lineComponents objectAtIndex:1];
NSString *strProviderFirstName=[lineComponents objectAtIndex:2];
NSString *strLicenseNumber=[lineComponents objectAtIndex:6];
NSString *strLicenseState=[lineComponents objectAtIndex:7];
NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:#"AttendeeInformation" inManagedObjectContext:[DataBaseManager sharedInstance].managedObjectContext];
NSString *strFullName = [NSString stringWithFormat:#"%# %#",strProviderFirstName,strProviderLastName];
[object setValue:strNPI forKey:#"npiNumber"];
[object setValue:strProviderFirstName forKey:#"attendeeFirstName"];
[object setValue:strProviderLastName forKey:#"attendeeLastName"];
[object setValue:strFullName forKey:#"attendeeName"];
[object setValue:strLicenseNumber forKey:#"licnesedNumber"];
[object setValue:strLicenseState forKey:#"stateLicense"];
NSError *error;
count++;
if(count%100==0)
{
if (![[DataBaseManager sharedInstance].managedObjectContext save:&error])
{
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
[[DataBaseManager sharedInstance].managedObjectContext refreshObject:object mergeChanges:YES];
}
if (count==lineComponents.count)
{
completionHandler();
}
}
}];
You should run Instruments with Time Profile and Core Data to see what your bottlenecks are. It's the best way to validate your performance issues. Core Data might or might not be your problem.
#Desdenova had it right. You need to get all the records in your context and save the context once at the end, or at least in very large batches. Under the API, sqlite is doing a separate transaction for each context save which is very expensive. If you do all the operations as a single transaction it's orders of magnitude faster. Do a test run where you comment out:
if (![[DataBaseManager sharedInstance].managedObjectContext save:&error])
{
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
and add these same 3 lines after the loop.

What will be the best way to update my NSString with appropriate NSArray value

I am trying to update my NSString value with the following code.
NSString *string =[NSString stringWithFormat:#"array[0]%#",[array componentsJoinedByString:#"[0]"]];
This is the result I got.
array[0]1array[0]2array[0]3
However, I need this result, basically the number inside the brackets will be updated based on the number of objects in the NSMutablearray.
array[0]1array[1]2array[2]3
I tried using a for loop with [NSString stringWithFormat:#"[%i]", i], but it seem to not have worked.
Untested:
NSMutableString *finalString = [[NSMutableString alloc] init];
[array enumerateObjectsUsingBlock:^(NSString *str, NSUInteger idx, BOOL *stop) {
[finalString appendFormat:#"[%lu]%#", (unsigned long)idx, str];
}];
// do stuff with finalString

Reading video metadata on iOS 7 - always empty

I am exporting a Quicktime video using AVExporterSession and setting the metadata on it as follows:
AVMutableMetadataItem *newMetaDataCommentItem = [[AVMutableMetadataItem alloc] init];
[newMetaDataCommentItem setKeySpace:AVMetadataKeySpaceQuickTimeMetadata];
[newMetaDataCommentItem setKey:AVMetadataQuickTimeMetadataKeyComment];
[newMetaDataCommentItem setValue:#"Test metadata value"];
NSMutableArray *metaData = [NSMutableArray array];
[metaData addObject:newMetaDataCommentItem];
exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition
presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL=[[SNMovieManager instance] urlForFinalMovie];
exporter.metadata = metaData;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = video;
I then import the file video to my Mac and run mdls on it and see the value has been set correctly: kMDItemComment = "Test metadata value"
The bit I can't do is read that value back. I am using the following to read the file. The asset is correct but the metadata property is always an empty dictionary.
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if([[result valueForProperty:#"ALAssetPropertyType"] isEqualToString:#"ALAssetTypeVideo"])
{
ALAssetRepresentation *rep = result.defaultRepresentation;
NSDictionary *metadata = rep.metadata;
[images addObject:(id)rep.fullScreenImage];
}
Does anyone know if I am taking the correct approach here and if not let me know what the correct approach to read this comment back out is?
Thanks
Simon
I would be highly appreciated if you can provide more code base related to the PhotoLibrary save process.
Otherwise only one answer, Metadata will return nil if the representation is one that the system cannot interpret.
The returned dictionary holds the properties of the video at a specified location in an file source.
I'think your problem is on getting metadata script
You should get an AVURLAsset first and get metadata from it ALAssetRepresentation metadata is different
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if([[result valueForProperty:#"ALAssetPropertyType"] isEqualToString:#"ALAssetTypeVideo"])
{
AVURLAsset *videoAset = [AVURLAsset assetWithURL:[[asset defaultRepresentation] url]];
if ([[videoAset metadataForFormat:AVMetadataFormatQuickTimeMetadata] count]) {
AVMutableMetadataItem *meta = [[videoAset metadataForFormat:AVMetadataFormatQuickTimeUserData] objectAtIndex:0];
NSLog(#"%#",meta);
NSLog(#"%lu",(unsigned long)[[videoAset metadataForFormat:AVMetadataFormatQuickTimeMetadata] count]);
}
}

Writing array contents to a file

I've struggled with this all day using answers from here, but I cannot find a solution that is working for me, so I thought I'd ask for help:
I have an array of objects that I've extracted from an entity.
All I want to do is to write the objects to a file.
What I've come up with so far is this:
NSLog(#" Exported Records: %i", [exportArray count]);
// the count here is 4 records.
//Each record has about 8 elements.
//I'm just trying to get this working with the first two elements right now
NSString *writeString = nil;
NSError *error = nil;
int i = 0;
NSString *key = nil;
NSString *tempString = nil;
for (i=0; i<[exportArray count]; i++)
{
tempString = [tempString stringByAppendingString: #" \n "];
for (int j=0; j<3; j++)
{
if (j == 0)
{
key = [[exportArray objectAtIndex:i] valueForKey:#"title"];
//[NSString stringWithFormat:tempString2, [[exportArray objectAtIndex:i] valueForKey:#"title"]];
}
if (j == 1)
{
key = [[exportArray objectAtIndex:i] valueForKey:#"username"];
//[NSString stringWithFormat:tempString2, [[exportArray objectAtIndex:i] valueForKey:#"username"]];
}
writeString = [NSString stringWithFormat:tempString, key];
}
}
// Write to the file
[writeString writeToFile:dataFile atomically:YES
encoding:NSUTF8StringEncoding error:&error];
if (error)
{
NSLog(#"%#", error);
}
Right now, all I'm getting the the last item, so I'm overwriting the string. But, there must be a better method to achieve this. Please let me know if there is another answer here matches my question, or please, post some ideas.
UPDATE
When I log the exportArray, I get this:
"<ItemEntity: 0x1ddf6560> (entity: ItemEntity; id: 0x1ddf46d0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p2> ; data: <fault>)",
"<ItemEntity: 0x1ddf6800> (entity: ItemEntity; id: 0x1ddf46e0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p2051> ; data: <fault>)",
"<ItemEntity: 0x1ddf6860> (entity: ItemEntity; id: 0x1ddf45d0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p3075> ; data: <fault>)",
"<ItemEntity: 0x1ddf68c0> (entity: ItemEntity; id: 0x1ddf45e0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p5124> ; data: <fault>)"
)
When I log the actual values:
NSLog(#" Exported titles: %#", [exportArray valueForKey:#"title"]);
NSLog(#" Exported usernames: %#", [exportArray valueForKey:#"username"]);
I get correct results. I just don't know how to tie these and the other attributes together..
Exported titles: (
1,
2,
4,
6
)
Exported usernames: (
ellis,
david,
bea,
ian
)
If you just have NSDictionaries in your array, you can use writeToFile:atomically: which will save a file in .plist format (which is much easier to read than a CSV).
Check out the Apple Docs for NSArray, and a good write up on plists.
So you would end up having:
[exportArray writeToFile:fileName atomically:YES];
Edit: You cannot save a core data object to a file. So what you want to do is create an interim array with just the data you want, then write that to the file.
NSMutableArray* arrayToSave = [NSMutableArray arrayWithCapacity:exportArray.count];
[exportArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary* exportDict = [NSDictionary dictionaryWithObjectsAndKeys:
[obj objectForKey:#"username"], #"username",
[obj objectForKey:#"title"], #"title", nil];
[arrayToSave addObject:exportDict];
}];
[arrayToSave writeToFile:fileName atomically:YES];
Use this,
[exportArray writeToFile:dataFile atomically:YES];
NSArray has the method writeToFile: to do this. Check the documentation here. This works fine if there are no custom objects inside the array.
In case you want to save only title or username(Even if it has custom objects in array), you can do it as:
NSArray *array1 = [exportArray valueForKey:#"title"];
[array1 writeToFile:dataFile atomically:YES];
or
NSArray *array2 = [exportArray valueForKey:#"username"];
[array2 writeToFile:dataFile atomically:YES];
[exportArray writeToFile: dataFile atomically:YES];
Creates and writes a .plist file format.
Unless you have objects in the array that are not supported by a plist.
The supported object are: the property list objects `NSString, NSNumber, NSDate, NSData, NSArray and NSDictionary.
UItextField and UItextView are not supported by the plist format so this will not work. If what you want to save is just the text from them put the text in the array.
Try this:
NSMutableString *string = [NSMutableString string];
for (NSDictionary* dict in exportArray) {
for (NSString* key in [dict allKeys]) {
NSObject* value = [dict objectForKey:key];
[string appendFormat:#"%# = %#\n", key, value];
}
}
// Write to the file
NSError* error = nil;
[string writeToFile:dataFile atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (error)
NSLog(#"%#", error);
I would consider converting it to NSData first and then saving it as a binary plist rather than a human-readable plist as binary plists are much faster to read (2-3x I believe but I can't remember which WWDC video it was explained in). To do this, you first need to convert the NSArray to an NSData object:
(Source: Rob Napier's answer to this SO question)
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
// "Note that all the objects in array must conform to the NSCoding protocol."
NSString *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:array
format:NSPropertyListBinaryFormat_v1_0 options:NULL error:&error];
Then you can write the NSData to file using:
[data writeToFile:path atomically:YorN];
To read it back, you then use:
NSData *rawdata = [[NSData alloc] initWithContentsOfFile:/*file*/];
NSData *arrayData = [NSPropertyListSerialization propertyListWithData:rawdata
options:NULL format:NULL error:&error];
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
You only really need to use the binary process for extremely large .plist files, but it is handy to know about.

Resources