Using NSKeyedArchiver throw Exception:[NSConcreteValue length]: unrecognized selector sent to instance - ios

I tried to use NSKeyedArchiver and NSKeyedUnarchiver.The code throw exception: "[NSConcreteValue length]: unrecognized selector sent to instance".
#interface DBTLine() <NSCoding>
#end
#implementation DBTLine
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeCGPoint:self.begin forKey:#"begin"];
[aCoder encodeCGPoint:self.end forKey:#"end"];
[aCoder encodeObject:self.color forKey:#"color"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
//Throw Exception Here.
_begin = [coder decodeCGPointForKey:#"begin"];
_end = [coder decodeCGPointForKey:#"end"];
_color = [coder decodeObjectForKey:#"color"];
}
return self;
}
#end
#interface DBTDrawView()
#property (nonatomic)NSMutableArray *finishedLines;
#end
#implementation DBTDrawView
-(instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self){
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData* dataFinishedLines = [defaults objectForKey:#"finishedLines"];
NSMutableArray *defaultLines = [NSKeyedUnarchiver unarchiveObjectWithData:dataFinishedLines];
if (defaultLines) {
self.finishedLines = defaultLines;
}
}
return self;
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//some code,"self.finishedLines" add line
NSData *dataFinishedLines = [NSKeyedArchiver archivedDataWithRootObject:self.finishedLines];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dataFinishedLines forKey:#"finishedLines"];
[self setNeedsDisplay];
}
#end
I searched the Internet for a long time,and I've tried multiple variations of my code.Finally,It works.Here is my changed code:
-(void)encodeWithCoder:(NSCoder *)aCoder {
NSValue *begin = [NSValue valueWithCGPoint:self.begin];
NSValue *end = [NSValue valueWithCGPoint:self.end];
[aCoder encodeObject:begin forKey:#"begin"];
[aCoder encodeObject:end forKey:#"end"];
[aCoder encodeObject:self.color forKey:#"color"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
NSValue *begin = [coder decodeObjectForKey:#"begin"];
NSValue *end = [coder decodeObjectForKey:#"end"];
_begin = [begin CGPointValue];
_end = [end CGPointValue];
_color = [coder decodeObjectForKey:#"color"];
}
return self;
}
But the problem is:
It seems the problem appeared in the process of using "-
encodeCGPoint:forKey: - decodeCGPointForKey:".Was there anything
wrong with what I did?
What is "[NSConcreteValue length]"?I can't find it in the document.
Thanks in advance.

Related

Make own class able to be saved in NSMutableArray

I use this code to get the path of a not existing file in documents directory of my application
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"restaurants.xml"];
getting this path:
/var/mobile/Containers/Data/Application/50BC3507-39BA-4F7A-86BA-254AB9DA6184/Documents/restaurants.xml
My question: How can I make my class being able to be saved in an array?
function to get path:
- (NSString *)restaurantListPath {
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"restaurants.xml"];
return path;
}
function to write array:
- (void)writeRestaurantListToFile:(NSMutableArray *)restaurantList {
[restaurantList writeToFile:[self restaurantListPath] atomically:YES];
}
function to load array:
- (NSMutableArray *)restaurantListOfFile {
NSMutableArray *restaurantList = [NSMutableArray arrayWithContentsOfFile:[self restaurantListPath]];
if (!restaurantList) {
restaurantList = [NSMutableArray array];
}
return restaurantList;
}
I created an array containing 1 JFRestaurant item and saved it with the function above. Then I loaded it with the function above and the array was empty (initialized but with no content).
If there would be an error while writing the file or reading the file the array would be nil not empty. So I think that the content of the array is the problem.
May my class JFRestaurant cannot be wrote to file. I looked at a tutorial and saw, that I need to implement NSCoding protocol to make my class being able to be written to file and did that, but the array is still empty. I also tried with NSKeyedArchiver and NSKeyedUnarchiver, but then I get another error
2014-08-25 16:52:23.969 Restaurants[5577:934385] -[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x170050860
2014-08-25 16:52:23.970 Restaurants[5577:934385] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x170050860'
*** First throw call stack:
(0x185866084 0x1954180e4 0x18586d094 0x185869e48 0x18576f08c 0x185741d98 0x18670c9e0 0x18676e170 0x10007d3a0 0x100077cdc 0x189eb39ec 0x189f510b8 0x189f50b0c 0x189f5082c 0x189f507ac 0x189e997a0 0x1897f22a4 0x1897ece90 0x1897ecd34 0x1897ec534 0x1897ec2b8 0x18a125b54 0x18a126a00 0x18a124b84 0x18d96c6ac 0x18581e360 0x18581d468 0x18581b668 0x185749664 0x189f07140 0x189f02164 0x10007b518 0x195a8ea08)
libc++abi.dylib: terminating with uncaught exception of type NSException
JFRestaurant.h:
//
// JFRestaurant.h
// Restaurants
//
// Created by Jonas Frey on 15.08.14.
// Copyright (c) 2014 Jonas Frey. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "JFUtils.h"
#import "JFItem.h"
#interface JFRestaurant : NSObject <NSCoding>
#property (strong, nonatomic) NSString *name;
#property float score;
- (void)addItem:(JFItem *)item;
- (void)removeItem:(JFItem *)item;
- (void)removeItemAtIndex:(NSUInteger)index;
- (void)setItem:(JFItem *)item atIndex:(NSUInteger)index;
- (JFItem *)itemAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfItem:(JFItem *)item;
- (NSInteger)itemCount;
- (NSMutableArray *)fullItemArray;
- (id)initWithName:(NSString *)name;
- (id)initWithName:(NSString *)name items:(NSMutableArray *)items;
#end
JFRestaurant.m
//
// JFRestaurant.m
// Restaurants
//
// Created by Jonas Frey on 15.08.14.
// Copyright (c) 2014 Jonas Frey. All rights reserved.
//
#import "JFRestaurant.h"
#interface JFRestaurant ()
#property (strong, nonatomic) NSMutableArray *items;
- (float)scoreOfItems;
#end
#implementation JFRestaurant
- (id)initWithCoder:(NSCoder *)aDecoder {
NSString *name = [aDecoder decodeObjectForKey:JFKeyRestaurantCoderName];
NSMutableArray *items = [aDecoder decodeObjectForKey:JFKeyRestaurantCoderItems];
return [self initWithName:name items:items];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:JFKeyRestaurantCoderName];
[aCoder encodeObject:_items forKey:JFKeyRestaurantCoderItems];
}
- (id)init {
self = [super init];
if (self) {
self.name = #"Neues Restaurant";
self.items = [[[JFUtils alloc] init] defaultItems];
self.score = [self scoreOfItems];
}
return self;
}
- (id)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.name = name;
self.items = [[[JFUtils alloc] init] defaultItems];
self.score = [self scoreOfItems];
}
return self;
}
- (id)initWithName:(NSString *)name items:(NSMutableArray *)items {
self = [super init];
if (self) {
self.name = name;
self.items = items;
self.score = [self scoreOfItems];
}
return self;
}
- (void)addItem:(JFItem *)item {
[self.items addObject:item];
self.score = [self scoreOfItems];
}
- (void)removeItem:(JFItem *)item {
[self.items removeObject:item];
self.score = [self scoreOfItems];
}
- (void)removeItemAtIndex:(NSUInteger)index {
[self.items removeObjectAtIndex:index];
self.score = [self scoreOfItems];
}
- (void)setItem:(JFItem *)item atIndex:(NSUInteger)index {
[self.items setObject:item atIndexedSubscript:index];
self.score = [self scoreOfItems];
}
- (JFItem *)itemAtIndex:(NSUInteger)index {
return [self.items objectAtIndex:index];
}
- (NSUInteger)indexOfItem:(JFItem *)item {
return [self.items indexOfObject:item];
}
- (NSInteger)itemCount {
return self.items.count;
}
- (NSMutableArray *)fullItemArray {
return self.items;
}
- (float)scoreOfItems {
float value = 0.0f;
float totalPossible = 0.0f;
for (JFItem *item in self.items) {
value += (4 - (float)item.segmentIndex) * 25.0f;
totalPossible += 100.0f;
}
return value / totalPossible * 100.0f;
}
#end
Thanks for your help
iComputerfreak

Custom NSObject iniWithCoder not called

I have a custom object, LevelContent, which contains some properties. LevelContentconforms to NSCoding, and I have implemented encodeWithCoder: and initWithCoder: methods. I save and fetch the data to Parse.com. Saving works fine like this:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.currentLevelContent];
When I fetch the data, I get the NSDatacorrectly, but when I try to initialize the LevelContentwith the downloaded data, initWithCoder: never gets called. I try to load the LevelContent with this:
LevelContent *content = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Here is the code for encoding/decoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.tiles forKey:#"tiles"];
[aCoder encodeObject:self.attributes forKey:#"attributes"];
[aCoder encodeObject:self.level forKey:#"level"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self == [super init]) {
self.tiles = [NSMutableArray array];
[self.tiles addObjectsFromArray:[aDecoder decodeObjectForKey:#"tiles"]];
self.attributes = [NSMutableDictionary dictionary];
[self.attributes addEntriesFromDictionary:[aDecoder decodeObjectForKey:#"attributes"]];
self.level = [Level levelWithTopIndex:0 detailIndex:0];
self.level = [aDecoder decodeObjectForKey:#"level"];
}
return self;
}
Change if (self == [super init]) to if (self = [super init]) in initWithCoder:
Try this,
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
.....
}
return self;
}

Singleton Array Won't Store Data Model

I'm using a singleton class (contactStorage) and a data model (contactModel) to store a list of contacts. I have created a contact object in my viewdidload of my root view controller and attempted to add it to the NSMutableArray but it will not "stick". I have logged the incoming object inside the addContact procedure and it produces accurate output, however, the addObject:c does not add it to the array. Any insight on this?
#import "contactListViewController.h"
#import "contactDetailScreenViewController.h"
#import "ContactModel.h"
#import "contactStorage.h"
#interface contactListViewController ()
#end
#implementation contactListViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ContactModel* c = [[ContactModel alloc] initWithfName:#"Mike" andlName:#"Deasy" andEmail:#"mid31#pitt.edu" andPhone:#"4127154194"];
[c logContact];
[[contactStorage shared]addContact:c];
[[contactStorage shared]saveToFile];
[c release];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The code for my singleton:
//
// contactStorage.m
// contactList
//
// Created by dev on 10/23/13.
// Copyright (c) 2013 Deasy, Michael William. All rights reserved.
//
#import "contactStorage.h"
#implementation contactStorage
{
}
#synthesize cList = _cList;
static contactStorage* _myOnlyInstance = nil;
#pragma mark Storage Methods
-(void)addContact: (ContactModel*) c
{
[c logContact];
[self.cList addObject:c];
NSLog(#"%#", _cList);
}
-(ContactModel*)getContact: (NSIndexPath*) index
{
return [self.cList objectAtIndex:index.row];
}
-(NSMutableArray*)deleteContact: (NSIndexPath*) index
{
[self.cList removeObjectAtIndex:index.row];
return self.cList;
}
-(NSMutableArray*)getAllContacts
{
return self.cList;
}
-(void)saveToFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"data.txt"];
NSLog(#"%#",path);
[_cList writeToFile:path atomically:YES];
NSLog(#"%#", self.cList);
}
#pragma mark Singleton Create
-(id)init
{
self = [super init];
if (self)
{
NSLog(#"Initing the array");
_cList = [[NSMutableArray alloc] init];
}
return self;
}
+(contactStorage*)shared
{
if (_myOnlyInstance == nil)
{
_myOnlyInstance = [[contactStorage alloc] init];
}
return _myOnlyInstance;
}
-(NSString*) documentsPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return documentsDir;
}
#end
The code for my contactModel:
//
// ContactModel.m
// contactList
//
// Created by dev on 10/23/13.
// Copyright (c) 2013 Deasy, Michael William. All rights reserved.
//
#import "ContactModel.h"
#implementation ContactModel
{
}
#synthesize fName = _fName;
#synthesize lName = _lName;
#synthesize email = _email;
#synthesize phone = _phone;
-(void)logContact
{
NSLog(#"%#", self.fName);
NSLog(#"%#", self.lName);
NSLog(#"%#", self.email);
NSLog(#"%#", self.phone);
}
-(void)dealloc
{
[_fName release];
[_lName release];
[_email release];
[_phone release];
[super dealloc];
}
-(id) initWithfName: (NSString*) fName
andlName: (NSString*) lName
andEmail: (NSString*) email
andPhone: (NSString*) phone
{
self = [super init];
_fName = [[NSString alloc] initWithString:fName];
_lName = [[NSString alloc] initWithString:lName];
_email = [[NSString alloc] initWithString:email];
_phone = [[NSString alloc] initWithString:phone];
return self;
}
#end
NSLog Output:
2013-10-24 12:50:35.573 contactList[3097:a0b] Mike
2013-10-24 12:50:35.574 contactList[3097:a0b] Deasy
2013-10-24 12:50:35.575 contactList[3097:a0b] mid31#pitt.edu
2013-10-24 12:50:35.575 contactList[3097:a0b] 4127154194
2013-10-24 12:50:35.576 contactList[3097:a0b] Initing the array
2013-10-24 12:50:35.576 contactList[3097:a0b] Mike
2013-10-24 12:50:35.576 contactList[3097:a0b] Deasy
2013-10-24 12:50:35.577 contactList[3097:a0b] mid31#pitt.edu
2013-10-24 12:50:35.577 contactList[3097:a0b] 4127154194
2013-10-24 12:50:35.578 contactList[3097:a0b] (
"<ContactModel: 0x8d72720>"
)
2013-10-24 12:50:35.578 contactList[3097:a0b] /Users/dev/Library/Application Support/iPhone Simulator/7.0/Applications/7CFD98F0-C502-49E5-953B-FD43B61EDC38/Documents/data.txt
2013-10-24 12:50:35.579 contactList[3097:a0b] (
"<ContactModel: 0x8d72720>"
)
Clearly, your singleton is successfully adding the ContactModel object to the array of your singleton (as evidenced by your NSLog statement). I assume your question stems from the fact that you're not seeing your file saved.
That's because you're trying to use writeToFile of your NSMutableArray (which tries to save a plist file). If you check the return code of writeToFile, you'll see it failed. This is because you cannot write a plist with an array consisting of custom objects. You might want to use NSKeyedArchiver instead, e.g.:
- (void)saveToFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"cList.dat"];
BOOL success = [NSKeyedArchiver archiveRootObject:_cList toFile:path];
NSAssert(success, #"write failed");
}
Anticipating the logical follow-up question, how to read the file, you would use NSKeyedUnarchiver, like so:
-(void)loadFromFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"cList.dat"];
self.cList = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSAssert(_cList, #"read failed");
}
But, for this to work, you have to make your contact model conform to the NSCoding protocol, namely adding the following methods to that class:
#pragma mark - NSCoding methods
- (NSArray *)propertyNames
{
return #[#"fName", #"lName", #"email", #"phone"];
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
// if `super` conforms to `NSCoding`, then use
//
// self = [super initWithCoder:aDecoder];
//
// in this case, `super` is `NSObject`, so just call `init`
self = [super init];
if (self) {
for (NSString *key in [self propertyNames]) {
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
// if `super` conforms to `NSCoding`, itself, then call `encodeWithCoder` for `super`:
//
// [super encodeWithCoder:aCoder];
//
// in this case, `super` is `NSObject`, so that is not needed
for (NSString *key in [self propertyNames]) {
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
For more information about using archives, see the Archives and Serializations Programming Guide.

NSCoder Not Returning Values correctly

In the following code, the properties are never set correctly in the sharedInstance method, and I can't figure out why. It seems like I have the correct values though before I save it with the archiver.
#import "User.h"
static User *sharedInstance = nil;
#define NAME #"name"
#define USER_ID #"id"
#define ACCOUNT_ID #"account_id"
#define USER_NAME #"username"
#define ADMIN #"admin"
#define CURRENT_USER #"current_user"
#implementation KSUser
+ (id)sharedInstance
{
#synchronized(self) {
if (sharedInstance == nil) {
NSData *userData = [[NSUserDefaults standardUserDefaults] objectForKey:CURRENT_USER];
if (userData) {
sharedInstance = [NSKeyedUnarchiver unarchiveObjectWithData:userData];
}
else {
sharedInstance = [[super alloc] init];
}
}
}
return sharedInstance;
}
- (void)populateFromJSON:(NSDictionary *)json
{
sharedInstance.name = json[NAME];
sharedInstance.accountId = json[ACCOUNT_ID];
sharedInstance.userId = json[USER_ID];
sharedInstance.userName = json[USER_NAME];
sharedInstance.admin = [json[ADMIN] boolValue];
sharedInstance.loggedIn = YES;
NSLog(#"values are: name: %#, %#, %#, %#", sharedInstance.name, sharedInstance.accountId, sharedInstance.userId, sharedInstance.userName);
}
- (void)logout
{
sharedInstance.name = nil;
sharedInstance.accountId = nil;
sharedInstance.userId = nil;
sharedInstance.userName = nil;
sharedInstance.admin = NO;
sharedInstance.loggedIn = NO;
[self saveState];
}
- (void)saveState
{
NSLog(#"values are: name: %#, %#, %#, %#", sharedInstance.name, sharedInstance.accountId, sharedInstance.userId, sharedInstance.userName);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:sharedInstance];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:CURRENT_USER];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:sharedInstance.userId forKey:USER_ID];
[aCoder encodeObject:sharedInstance.accountId forKey:ACCOUNT_ID];
[aCoder encodeObject:sharedInstance.name forKey:NAME];
[aCoder encodeObject:sharedInstance.userName forKey:USER_NAME];
[aCoder encodeBool:sharedInstance.admin forKey:ADMIN];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
sharedInstance.userId = [aDecoder decodeObjectForKey:USER_ID];
sharedInstance.accountId = [aDecoder decodeObjectForKey:ACCOUNT_ID];
sharedInstance.name = [aDecoder decodeObjectForKey:NAME];
sharedInstance.userName = [aDecoder decodeObjectForKey:USER_NAME];
sharedInstance.admin = [aDecoder decodeBoolForKey:ADMIN];
}
return self;
}
#end
Any help would be greatly appreciated.
It is because your global variable sharedinstance in method - (id)initWithCoder:(NSCoder *)aDecoder is always nil
In initWithCoder: don't refer to the shared instance but rather self instead. At the time it's executing sharedInstance is nil.
Also, you only call saveState after logging out, so it only saves out the nil values.

Some of NSCoding variables are NOT retrieved

I am using an NSData to store an object for later use. It has quite a few NSStrings and I need to pull it out of an object.
For some reason, only some of the NSStrings are stored and some others get zeroed out!
I was thinking it must be something with my code, that I must have forgotten to initialize some string, but for a very weird reason some of the strings lose the data!
I can't get theImportantString to get it's relevant value because it first seems like the variable got it's value, but after coming back from Unarchive, it's equal to #""!
// CMyData.h
/////////////////////
#interface CMyData : NSObject <NSCoding>
{
NSString *ID;
NSString *DIST;
.
.
}
#property (nonatomic,retain) NSString *ID;
#property (nonatomic,retain) NSString *DIST;
#end
// CMyData.m
//////////////////////
#import "CMyData.h"
#implementation CMyData
#synthesize ID;
#synthesize DIST;
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.ID = [decoder decodeObjectForKey:#"ID"];
self.DIST = [decoder decodeObjectForKey:#"DIST"];
.
.
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:ID forKey:#"ID"];
[encoder encodeObject:DIST forKey:#"DIST"];
.
.
}
- (void)dealloc {
[ID release];
[DIST release];
[super dealloc];
}
#end
MyController.m
-(void) makeObject: (NSDictionary *)dict
{
CMyData* myData=[[CMyData alloc] init];
myData.ID = [[NSString alloc] initWithString:[dict objectForKey:#"NAME"]];
myData.DIST = [[NSString alloc] initWithString:[dict objectForKey:#"DISTRIBUTOR"]];
.
.
myObject = [[[MYObject alloc] init];
myObject.data = [NSKeyedArchiver archivedDataWithRootObject:myData];
}
And then a tap on a button happens:
- (void) tapOnIcon: (MyObject*)theObject
{
CMyData *data = [NSKeyedUnarchiver unarchiveObjectWithData:theObject.data];
[delegate showData:data];
}
in the delegate Controller (Where the value can't be set anymore):
delegateController.m
/////////////////////////////////
-(void) showData:(CMyData*)theData{
self.theImportantString = [[NSString alloc] initWithString:theData.DIST];
.
.
.
}
Seems you have types mismatch:
// in - (id)initWithCoder:(NSCoder *)decoder
self.DIST = [decoder decodeIntForKey:#"DIST"];
but in declaration you have
// in CMyData.h
NSString *DIST;
This should be:
// in - (id)initWithCoder:(NSCoder *)decoder
self.DIST = [NSString stringWithFormat:#"%d", [decoder decodeIntForKey:#"DIST"]];

Resources