I have two methods to save and load NSManagedObject (updated to original):
-(void) saveToCoreData: (TeamManagedObject *)teamSet{
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelDebug);
if(!teamSet) {
self.teamSet = [NSEntityDescription insertNewObjectForEntityForName:#"TeamManagedObject" inManagedObjectContext:self.managedObjectContext];
} else {
self.teamSet = teamSet;
}
[self.teamSettings enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[self.teamSet setValue:obj forKey:key];
}];
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
}
-(id)load:(TeamManagedObject *)managedObject {
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([TeamManagedObject class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
NSString *value = [managedObject valueForKey:key];
if (value) {
[self setObject:value forKey:key];
}
}
self.teamSet = managedObject;
free(properties);
return self;
}
1) call method [self save:nil] - object creates and saves to CoreData correctly. (value is correct after having app restarted)
2) restarting app and calling load method - it was loaded correctly
3) calling save [self save:object] method with loaded object - it looks like saved, but only before app was restarted... after app restarting, object have an old value...
Where is the mistake?
Thanks!
Related
I have a piece of code that execute a coredata update of the database, and I would like to know when that block is finished. Is there a way to get it knowing when the coredata has finished to update the tables?
Main function:
NSMutableArray* responseArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[self parseAndAddLovAll:responseArray toArray:self.objects];
});
Function used in dispatch:
- (void)parseAndAddLovAll:(NSMutableArray*)responseArray toArray:(NSMutableArray*)destinationArray
{
NSError *error;
DB_ListOfValue_manage *elements_to_store = [[DB_ListOfValue_manage alloc] init];
NSManagedObjectContext * context = [elements_to_store managedObjectContext];
for (int index=0; index < [responseArray count]; index++)
{
NSDictionary * responseArray2 = [[NSDictionary alloc] initWithDictionary:responseArray[index]];
NSString * table_to_store = [[NSString alloc] initWithString:[responseArray2 objectForKey:#"table"]];
NSArray * lignes = [[NSArray alloc] initWithObjects:[responseArray2 objectForKey:#"lignes"], nil];
id value;
// Check if LOV table or contact table
if ((([#"Table_contact" compare:table_to_store])!=NSOrderedSame)&&
(([#"Table_event" compare:table_to_store])!=NSOrderedSame))
{
for (NSDictionary * item in lignes[0])
{
value = [item objectForKey:#"codeevent"];
if ([value isEqualToNumber:[NSNumber numberWithInt:EVENT_ID]])
{//FIXME: bug to check when SYNC
elements_to_store = (DB_ListOfValue_manage*)[NSEntityDescription insertNewObjectForEntityForName:table_to_store inManagedObjectContext:context];
elements_to_store.code_event = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"id"];
elements_to_store.id = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"used"];
elements_to_store.used = [value isKindOfClass:[NSNull class]] ? #"" : value;
if (![context save:&error]) {
#ifdef DEBUG
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
#endif
}
else{
#ifdef DEBUG
NSLog(#"Data saved to DB, table %# %# %#", table_to_store, elements_to_store.label1, elements_to_store.label2);
#endif
}
}
}
}
}
}
+ (NSDictionary *)mc_inboundMapping {
static NSMutableDictionary *mappingForClassName = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mappingForClassName = [NSMutableDictionary dictionary];
});
#synchronized(mappingForClassName) {
NSDictionary *mapping = mappingForClassName[[self className]];//<----EXC BAD ACCESS here
if (!mapping) {
SEL selector = NSSelectorFromString(#"JSONInboundMappingDictionary");
if ([self respondsToSelector:selector]) {
mapping = MCValueFromInvocation(self, selector);
}
else {
mapping = [self mc_defaultInboundMapping];
}
mappingForClassName[[self className]] = mapping;
}
return mapping;
}
I'm using Realm+JSON with Realm. And got a problem with Realm+JSON.
When I attempt to call createOrUpdateInRealm:withJSONArray: multiple time in loop for multiple RLMObjects, the first RLMObject works OK, but the second RLMObject(no matter what class is) fails with EXC BAD ACCESS code1on static variable.
I think autorelease pool deallocate pointing NSMutableDictionary.Anybody got this issue? I'm using XCode 6.3.2 and Realm 0.96.3.
+ (NSArray *)createOrUpdateInRealm:(RLMRealm *)realm withJSONArray:(NSArray *)array {
NSInteger count = array.count;
NSMutableArray *result = [NSMutableArray array];
for (NSInteger index=0; index*kCreateBatchSize<count; index++) {
NSInteger size = MIN(kCreateBatchSize, count-index*kCreateBatchSize);
#autoreleasepool
{
for (NSInteger subIndex=0; subIndex<size; subIndex++) {
NSDictionary *dictionary = array[index*kCreateBatchSize+subIndex];
id object = [self createOrUpdateInRealm:realm withJSONDictionary:dictionary];
[result addObject:object];
}
}
}
return [result copy];
}
calling here...
[realm beginWriteTransaction];
if ([collection isKindOfClass:[NSDictionary class]])
{
for (NSString *key in [collection allKeys])
{
NSLog(#"Set Data == %#:%#", key, collection[key]);
id jsData = [collection[key] objectForKey:#"data"];
if ([key isEqualToString:STATIC_ERROR_MSGS])
{
[SErrorMsgs createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_EPISODE])
{
[SEpisode createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_INGAME_TUTORIAL])
{
[SIngameTutorial createOrUpdateInRealm:realm withJSONArray:jsData];
} //more cases.........edited
-(void) objectParsed_ListAllMedia:(NSDictionary *)dictionary
{
#try {
self.viewLoading.hidden=1;
[self.arrGaalleryMediaName removeAllObjects];
[self.arrMediaNames removeAllObjects];
if(self.arrOnlyServerImages == nil){
self.arrOnlyServerImages = [[NSMutableArray alloc] init];
}
if([self.arrOnlyServerImages count] >0){
[self.arrOnlyServerImages removeAllObjects];
}
if (dictionary==nil) {
[self.gridCollectionView reloadData];
return;
}
// Filter Array for Audio file
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"type != 'audio' "];
self.arrOnlyServerImages = [NSMutableArray arrayWithArray:[[dictionary objectForKey:#"objects"] filteredArrayUsingPredicate:predicate]];
// Remove duplicate Start //Read Meta Data and Duplicate from Download, Duplicate from upload START
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.memreas.myqueue", 0);
dispatch_async(backgroundQueue, ^{
NSMutableArray *arr = [NSMutableArray array];
NSMutableIndexSet * indexSet = [NSMutableIndexSet indexSet];
for (int i=0; self.assetAry.count>i; i++) {
ALAsset *result =self.assetAry[i];
ALAssetRepresentation *imageRep = [result defaultRepresentation];
NSDictionary * customMetaDic = [imageRep metadata][(NSString*)kCGImagePropertyIPTCDictionary];
if (customMetaDic) {
[self.arrMediaNames addObject:customMetaDic[(NSString*)kCGImagePropertyIPTCObjectName]?customMetaDic[(NSString*)kCGImagePropertyIPTCObjectName]:#""];
}else{
[self.arrMediaNames addObject:#""];
}
[self.arrGaalleryMediaName addObject:[self getFileNameWithExtensionFromPath:imageRep.url]];
}
for (int i=0; self.arrOnlyServerImages.count>i; i++) {
NSMutableDictionary* obj = self.arrOnlyServerImages[i];
NSMutableDictionary * dic2 = [NSMutableDictionary dictionaryWithDictionary:obj];
BOOL isArrMedia =[self.arrMediaNames containsObject:dic2[#"media_name"]];
BOOL isGallery =[self.arrGaalleryMediaName containsObject:dic2[#"media_name"]];
if (isArrMedia||isGallery) {
dic2[#"isDownloaded"] = [NSNumber numberWithBool:YES];
[indexSet addIndex: isArrMedia?[self.arrMediaNames indexOfObject:dic2[#"media_name"]] :[self.arrGaalleryMediaName indexOfObject:dic2[#"media_name"]]];
}else{
dic2[#"isDownloaded"] = [NSNumber numberWithBool:NO];
}
[arr addObject:dic2];
}
dispatch_async(dispatch_get_main_queue(), ^{
#try {
self.arrOnlyServerImages = arr;
[self.assetAry removeObjectsAtIndexes:indexSet];
[self.gridCollectionView reloadData];
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
[self.gridCollectionView reloadData];
}
});
});
// Remove duplicate END //Read Meta Data and Duplicate from Download, Duplicate from upload END
[self.gridCollectionView reloadData];
[self.gridView.collectionView reloadData];
[self.location performSelector:#selector(stopActivity) withObject:nil afterDelay:2];
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
}
I have issue with my code, While I run this code it generates memory pressure issue and crash the app.
Functionality is:
I load all the images from server and local assets and match each other with file name and remove duplicate images from list, So it will visible only once.
Any one have solution so please help.
thanks in advance.
You're accessing memory-intensive things (ALAssetRepresentations) in a tight loop. In these cases a local autoreleasepool can help ARC to keep your memory use down.
Inside the loop where you pass through self.assetAry, wrap everything in an autoreleasepool like so:
#autoreleasepool {
AlAsset *asset = ...
...
// Rest of your code
}
I'm working on cline-server app. I'm getting JSON objects as response from the server,
then I'm converting JSON to NSDictionary. Now I need to map NSDictionary to custom data object.
So I have created BasicDataObject class that has:
#pragma mark - Inits
- (id)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
if (self) {
[self setValuesForKeysWithDictionary:dictionary];
}
return self;
}
#pragma mark - Service
- (id)valueForUndefinedKey:(NSString *)key {
NSArray *allKeys = [self allKeys];
id returnObject = nil;
BOOL keyFound = NO;
for (NSString *propertyName in allKeys) {
if ([propertyName isEqualToString:key]) {
id object = [self performSelector:NSSelectorFromString(key)];
returnObject = object ? object : [NSNull null];
keyFound = YES;
break;
}
}
if (!keyFound) {
#throw [NSException exceptionWithName:NSUndefinedKeyException reason:[NSString stringWithFormat:#"key '%#' not found", key] userInfo:nil];
}
return returnObject;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
NSString *capitalizedString = [key stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[key substringToIndex:1] capitalizedString]];
NSString *setterString = [NSString stringWithFormat:#"set%#:", capitalizedString];
[self performSelector:NSSelectorFromString(setterString) withObject:value];
}
- (void)setNilValueForKey:(NSString *)key {
object_setInstanceVariable(self, key.UTF8String, 0);
}
- (NSArray *)allKeys {
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(self.class, &propertyCount);
NSMutableArray *propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Each data object is a subclass of this class, so it could be initialised from NSDictionary.
If some data object subclass needs some custom initialisation, I'm overriding it's:
- (id)initWithDictionary:(NSDictionary *)dictionary
Is it correct/good approach, or do I need to add something more?
This is an OK way to do this. Ive found that rarely are the json keys the most appropriate names for my object's properties, so most people's code that I've seen manually sets each property on their object so they can 1) name it whatever they want 2) convert to primitive values from the json dictionary, which will always contain objects. (Eg NSNumber -> float)
I'm having trouble making a mutable array of objects persistent using NSUserDefaults. I've been trying different code for days and just can't get it right. I've read everything I can find on the subject, including all the postings in StackOverflow. I know I'm close, but I would really appreciate someone pointing out what I'm doing wrong. I know arr in the following code winds up with a count of zero, and that's where I hit a roadblock. I haven't gotten past that point. I'll bet someone will laugh and point our my silly error within 2 minutes of reading my post.
Here's the relevant code, including the log:
Contacts.h:
#interface Contacts : NSObject // <NSCoding>
{
NSMutableArray *arr; // 8/21/13
}
Contacts.m
- (void)encodeWithCoder:(NSCoder *)aCoder
{
NSLog(#"In encodeWithCoder");
NSLog(#"In encodeWithCoder. arr's count is: %i", [arr count]);
{
for (int i = 0; i <[arr count]; i++)
{
NSLog(#"In encodeWithCoder %#", [arr objectAtIndex:i]);
NSLog(#"class %#", [arr class]);
}
[aCoder encodeObject:arr forKey:#"allContacts"];
}
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
NSLog(#"In initWithCoder");
arr = [aDecoder decodeObjectForKey:#"allContacts"];
return self;
}
// The following two methods might not even be needed. I copied them from some examples.
- (NSData *)dataOftype:(NSString *)typeName error:(NSError **)outError
{
return nil;
}
- (BOOL)readFromData:(NSData *)data oftype:(NSString *)typeName error: (NSError **)outError
{
arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"allContacts is ->%#", [arr mutableCopy]);
return YES;
}
ContactStore.h
#interface ContactsStore : NSObject <NSCoding>
{
NSMutableArray *arr; // 8/21/13
NSMutableArray *allContacts;
}
ContactStore.m
- (void)fetchContactsIfNecessary
{
NSLog(#"in ContactsStore.m - fetchContactsIfNecessary #1");
if (!allContacts)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"allContacts"];
allContacts = [[NSMutableArray alloc]initWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:data]];
for (int i = 0; i <[allContacts count]; i++)
{
NSLog(#"In FetchContactsIfNecessary- Gettingfrom NSUserDefaults %#", [allContacts objectAtIndex:i]);
NSLog(#"In FetchContactsIfNecessary- Gettingfrom NSUserDefaults - class %#", [allContacts class]);
}
}
// If we didn't find one in NSUserDefaults, then create a new one.
if (!allContacts)
{
allContacts = [[NSMutableArray alloc] init];
NSLog(#"In FetchContactsIfNecessary-We just allocated allContacts");
}
NSLog(#"in ContactsStore.m - Leaving fetchContactsIfNecessary2");
}
- (BOOL)saveChanges
{
for (int i = 0; i <[allContacts count]; i++)
{
NSLog(#"In saveChanges %#", [allContacts objectAtIndex:i]);
NSLog(#"In saveChanges class %#", [allContacts class]);
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
arr = allContacts; // 8/21/13
NSLog(#"In saveChanges. arr's count is: %i", [arr count]);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:#"allContacts"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"We just synchronized allContacts - I think");
return 1;
}
Log
2013-08-22 14:13:50.764 ImOK[16979:907] In saveChanges Scott Stringer, (212) 888-1234
2013-08-22 14:13:50.772 ImOK[16979:907] In saveChanges class __NSArrayM
2013-08-22 14:13:50.775 ImOK[16979:907] In saveChanges Sheldon Silver, (212) 987-4321
2013-08-22 14:13:50.781 ImOK[16979:907] In saveChanges class __NSArrayM
2013-08-22 14:13:50.793 ImOK[16979:907] In saveChanges. arr's count is: 2
2013-08-22 14:13:50.795 ImOK[16979:907] In encodeWithCoder
2013-08-22 14:13:50.796 ImOK[16979:907] In encodeWithCoder. arr's count is: 0
2013-08-22 14:13:50.798 ImOK[16979:907] In encodeWithCoder
2013-08-22 14:13:50.799 ImOK[16979:907] In encodeWithCoder. arr's count is: 0
2013-08-22 14:13:50.805 ImOK[16979:907] We just synchronized allContacts - I think
The names and phone numbers display correctly after the app returns from the background, but I get just four nulls after relaunching the app. I expected that because arr's count is zero in the method encodeWithCoder, but I can't figure out how to resolve this issue.
Your are only encoding and decoding your object. You don't store it yet. To store it you need a function like this:
- (BOOL)storeBrandOnDevice
{
NSString *path = [self itemArchivePath];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self forKey:#"Contacts"];
[archiver finishEncoding];
BOOL dataSaveSuccess = [data writeToFile:path atomically:YES];
return dataSaveSuccess;
}
to get them from the store something like this:
- (void) getBrandFromStorage
{
NSString *path = [self itemArchivePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:path];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Contacts* storedContacts = [unarchiver decodeObjectForKey:#"Contacts"];
[unarchiver finishDecoding];
}
}
and here is your missing function:
- (NSString*)itemArchivePath
{
#try {
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"items.archive"];
}
#catch (NSException *exception) {
return nil;
}
}
It may not be perfect, but that's the general concept.
For more information about NSCoding, follow Ray Wenderlich's tutorial