Work with NSUserDefault while saving your own data type

I read this topic. How to save my data type in NSUserDefault? and get this useful piece of code from there:

MyObject *myObject = [[MyObject alloc] init]; NSData *myObjectData = [NSData dataWithBytes:(void *)&myObject length:sizeof(myObject)]; [[NSUserDefaults standardUserDefaults] setObject:myObjectData forKey:@"kMyObjectData"]; 

for saving data and for reading

  NSData *getData = [[NSData alloc] initWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"kMyObjectData"]]; MyObject *getObject; [getData getBytes:&getObject]; 

Its work is very good when I save data in one ViewController and read it in another. but when I want to use it in the same class:

  - (IBAction)linkedInLog:(UIButton *)sender { NSUserDefaults *myDefaults = [[NSUserDefaults standardUserDefaults] objectForKey:@"linkedinfo"]; NSData *getData = [[NSData alloc] initWithData:myDefaults]; LinkedContainer *getObject; [getData getBytes:&getObject]; if (!myDefaults) { mLogInView = [[linkedInLoginView alloc]initWithNibName:@"linkedInLogInView" bundle:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginViewDidFinish:) name:@"loginViewDidFinish" object:mLogInView]; [self.navigationController pushViewController:mLogInView animated:YES]; if ((FBSession.activeSession.isOpen)&&(mLinkedInIsLogegOn)) { mMergeButton.hidden = NO; } } else{ mLinkedInIsLogegOn= YES; mLinkedInInfo.mConsumer = getObject.mConsumer; mLinkedInInfo.mToken = getObject.mToken; } } 

something is wrong. in @selector: loginViewDidFinish I save my data in NSUserDefaults :

  -(void) loginViewDidFinish:(NSNotification*)notification { [[NSNotificationCenter defaultCenter] removeObserver:self]; mLinkedInInfo.mConsumer = mLogInView.consumer; mLinkedInInfo.mToken = mLogInView.accessToken; NSData *myObjectData = [NSData dataWithBytes:(void *)&mLinkedInInfo length:sizeof(mLinkedInInfo)]; NSUserDefaults *lSave = [NSUserDefaults standardUserDefaults]; [lSave setObject:myObjectData forKey:@"linkedinfo"]; [lSave synchronize]; if (mLinkedInInfo.mToken) { mLinkedInIsLogegOn = YES; } } 

the program always crashes when it comes to parts. If someone knows what I'm doing wrong, help me)

error message: Thread 1: EXC_BAD_ACCESS (code = 2, address 0x8) when compiling getObject.Consumer

+4
source share
2 answers

In the vast majority of cases, this will not be a meaningful way to serialize your object into NSData:

 MyObject *myObject = [[MyObject alloc] init]; NSData *myObjectData = [NSData dataWithBytes:(void *)&myObject length:sizeof(myObject)]; [[NSUserDefaults standardUserDefaults] setObject:myObjectData forKey:@"kMyObjectData"]; 

The canonical way to do this would be for MyObject to adopt the NSCoding protocol. Based on the code you posted here, accepting NSCoding might look like this:

 - (id)initWithCoder:(NSCoder *)coder { if (self = [super init]) { mConsumer = [coder decodeObjectForKey: @"consumer"]; mToken = [coder decodeObjectForKey: @"token"]; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:mConsumer forKey: @"consumer"]; [coder encodeObject:mToken forKey:@"token"]; } 

Once you have completed this work, you should convert MyObject to and from NSData as follows:

 NSData* data = [NSKeyedArchiver archivedDataWithRootObject: myObject]; MyObject* myObject = (MyObject*)[NSKeyedUnarchiver unarchiveObjectWithData: data]; 

The code that you have here will completely destroy the stack and crash (because this line [getData getBytes:&getObject]; will cause NSData to write bytes to the getObject address that is locally declared on the stack. Therefore, stack splitting). Starting with your code, a working implementation might look something like this:

 - (IBAction)linkedInLog:(UIButton *)sender { NSData* dataFromDefaults = [[NSUserDefaults standardUserDefaults] objectForKey:@"linkedinfo"]; LinkedContainer* getObject = (LinkedContainer*)[NSKeyedUnarchiver unarchiveObjectWithData: dataFromDefaults]; if (!dataFromDefaults) { mLogInView = [[linkedInLoginView alloc]initWithNibName:@"linkedInLogInView" bundle:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginViewDidFinish:) name:@"loginViewDidFinish" object:mLogInView]; [self.navigationController pushViewController:mLogInView animated:YES]; if ((FBSession.activeSession.isOpen)&&(mLinkedInIsLogegOn)) { mMergeButton.hidden = NO; } } else{ mLinkedInIsLogegOn= YES; mLinkedInInfo.mConsumer = getObject.mConsumer; mLinkedInInfo.mToken = getObject.mToken; } } -(void) loginViewDidFinish:(NSNotification*)notification { [[NSNotificationCenter defaultCenter] removeObserver:self]; mLinkedInInfo.mConsumer = mLogInView.consumer; mLinkedInInfo.mToken = mLogInView.accessToken; NSData* objectData = [NSKeyedArchiver archivedDataWithRootObject: mLinkedInInfo]; [[NSUserDefaults standardUserDefaults] setObject: objectData forKey: @"linkedinfo"]; [[NSUserDefaults standardUserDefaults] synchronize]; if (mLinkedInInfo.mToken) { mLinkedInIsLogegOn = YES; } } 
+4
source

I agree with ipmcc's answer, another viable option is to add methods to your object to convert it to NSDictionary . You can also add methods to -initWithDictionary and should make creating instances very easy. Extract the dictionary to NSUserDefaults for use, convert to a dictionary to save.

Here is an example of these two methods with common data:

 - (id)initWithDictionary:(NSDictionary *)dict { self = [super init]; // This check serves to make sure that a non-NSDictionary object // passed into the model class doesn't break the parsing. if(self && [dict isKindOfClass:[NSDictionary class]]) { NSObject *receivedFences = [dict objectForKey:@"fences"]; NSMutableArray *parsedFences = [NSMutableArray array]; if ([receivedFences isKindOfClass:[NSArray class]]) { for (NSDictionary *item in (NSArray *)receivedFences) { if ([item isKindOfClass:[NSDictionary class]]) { [parsedFences addObject:[Fences modelObjectWithDictionary:item]]; } } } } // More checks for specific objects here return self; } - (NSDictionary *)dictionaryRepresentation { NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; NSMutableArray *tempArrayForFences = [NSMutableArray array]; for (NSObject *subArrayObject in self.fences) { if([subArrayObject respondsToSelector:@selector(dictionaryRepresentation)]) { // This class is a model object [tempArrayForFences addObject:[subArrayObject performSelector:@selector(dictionaryRepresentation)]]; } else { // Generic object [tempArrayForFences addObject:subArrayObject]; } } [mutableDict setValue:[NSArray arrayWithArray:tempArrayForFences] forKey:@"fences"]; return [NSDictionary dictionaryWithDictionary:mutableDict]; } 

This is basically boilerplate code that is generated by a program that I use called JSON Accelerator. It will read the JSON string returned by the API and generate object code for you. Not a completely new concept, but it makes it very easy to create classes for the API. And this bit of code is great for creating dictionaries for saving in NSUserDefaults . Hope this helps.

+3
source

Source: https://habr.com/ru/post/1499602/


All Articles