Just wanted to get a sanity check on this code for storing and retrieving user data for iOS 7. It's not working but I'm not seeing the issue.
GameData.h
#import <Foundation/Foundation.h>
#interface GameData : NSObject<NSCoding>
{
int HighScoreGD;
}
#property (readwrite,assign) int HighScoreGD;
#end
extern GameData *gGameData;
GameData.m
#import "GameData.h"
GameData *gGameData;
#implementation GameData
#synthesize HighScoreGD;
-(void)encodeWithCoder:(NSCoder *)coder {
[coder encodeInt:HighScoreGD forKey:#"HighScoreGD"];
}
-(id)initWithCoder:(NSCoder *)coder {
if((self = [super init])) {
HighScoreGD = [coder decodeIntForKey:#"HighScoreGD"];
}
return self;
}
-(id) init {
if((self = [super init])) {
}
return self;
}
-(void) dealloc {
//[super dealloc];
}
#end
Storing data:
gGameData.HighScoreGD = pointsHigh;
Retrieving data:
pointsHigh = gGameData.HighScoreGD;
I had used this in a bunch of old game concepts (cocos2d) in the past and it worked fine.
Not getting compile errors. Just simply isn't storing the data.
I haven't used this code in a couple years and it's not working. Did iOS7 or cocos2d v3 change how NSCoder is used?
Or am I making a dumb error somewhere?
Thanks in advance.
Related
I am trying to implement the CloudSight API into an iOS Objective C project for fun, however for some reason when I try to send an image to cloudSight the cloudSightQuery parameters are all set to null.
I have added CloudSight to my application as a Cocoapod and everything loads fine and when I execute this code below it just never returns any kind of response from the server in fact I'm not even sure it sends.
firstview.h
#import <UIKit/UIKit.h>
#import "CloudSight.h"
#import <CloudSight/CloudSightQueryDelegate.h>
#interface FirstViewController : UIViewController <CloudSightQueryDelegate>
{
CloudSightQuery *cloudSightQuery;
}
- (void)searchWithImage;
- (NSData *)imageAsJPEGWithQuality:(float)quality;
#end
firstview.m
#import "FirstViewController.h"
#import <CoreLocation/CoreLocation.h>
#import "CloudSightConnection.h"
#import "UIImage+it_Image.h"
#import <CloudSight/CloudSightQuery.h>
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
cloudSightQuery.queryDelegate = self;
[self searchWithImage];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)searchWithImage {
UIImage * myImage = [UIImage imageNamed: #"car.jpg"];
NSData *imageData = [self imageAsJPEGWithQuality:0.7 image:myImage];
// Start CloudSight
cloudSightQuery = [[CloudSightQuery alloc] initWithImage:imageData
atLocation:CGPointZero
withDelegate:self
atPlacemark:nil
withDeviceId:#""];
[cloudSightQuery start];
}
#pragma mark CloudSightQueryDelegate
- (void)cloudSightQueryDidFinishIdentifying:(CloudSightQuery *)query {
if (query.skipReason != nil) {
NSLog(#"Skipped: %#", query.skipReason);
} else {
NSLog(#"Identified: %#", query.title);
}
}
- (void)cloudSightQueryDidFail:(CloudSightQuery *)query withError:(NSError *)error {
NSLog(#"Error: %#", error);
}
#pragma mark image
- (NSData *)imageAsJPEGWithQuality:(float)quality image:(UIImage *)image
{
return UIImageJPEGRepresentation(image, quality);
}
#end
Here's the library: https://libraries.io/github/cloudsight/cloudsight-objc
We just updated the library to make this clearer. You can run a pod update CloudSight to get the new version.
The most typical cause of this sort of problem is that the delegates are never being called. Usually, that means that the delegate object is released before being called back, however in this case it looks like it's nil when assigned.
This line here:
cloudSightQuery.queryDelegate = self;
[self searchWithImage];
Should be changed to:
[self searchWithImage];
And then in the method implementation change the initialization and start to:
// Start CloudSight
cloudSightQuery = [[CloudSightQuery alloc] initWithImage:imageData
atLocation:CGPointZero
withDelegate:self
atPlacemark:nil
withDeviceId:#""];
cloudSightQuery.queryDelegate = self;
[cloudSightQuery start];
Let us know if that helps!
I need to save an NSMutableArray to disk in order to store my application data. I know there are a lot of similiar questions out there but none of them I found covers my question.
I do not want to integrate Core Data just for saving one NSMutableArray. Normally I would go for implementing the NSCoding protocol and using NSKeyedUnarchiver. Unfortunately, my data model class has some foreign classes from a library which do not implement the NSCoding protocol.
So what is the best way for me to store my array?
This is what I tried but because of the given reasons it won't work:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <ForeignFramework/ForeignFramework.h>
#interface DEModelClass : NSObject <NSCoding>
#property (nonatomic,strong) ForeignFramework *foreignFramework;
#property (nonatomic,strong) UIImage *image;
#property (nonatomic,copy) NSNumber *number1;
#property (nonatomic,copy) NSNumber *number2;
#end
#define kEncodeKeyForeign #"kEncodeKeyForeign"
#define kEncodeKeyImage #"kEncodeKeyImage"
#define kEncodeKeyNumber1 #"kEncodeKeyNumber1"
#define kEncodeKeyNumber2 #"kEncodeKeyNumber2"
#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.foreignFramework forKey:kEncodeKey];
[aCoder encodeObject:self.image forKey:kEncodeKeyImage];
[aCoder encodeObject:self.number1 forKey:kEncodeKeyNumber1];
[aCoder encodeObject:self.number2 forKey:kEncodeKeyNumber2];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super init]))
{
self.foreignFramework = [aDecoder decodeObjectForKey:kEncodeKeyForeign];
self.image = [aDecoder decodeObjectForKey:kEncodeKeyImage];
self.number1 = [aDecoder decodeObjectForKey:kEncodeKeyNumber1];
self.number2 = [aDecoder decodeObjectForKey:kEncodeKeyNumber2];
}
return self;
}
You should use the NSCoding protocol and you can use it.
A. I assume that you know, which properties of the foreign classes to store. (Probably that data that let you re-instantiate instance objects at loading.) If not, there is no way to store them. And of course, Cocoa, NSArray, $whatever cannot know it. These are generic.
B. When you are done with selecting the properties to store, simply add a category to the foreign classes that do the job for you:
#interface ForeignClass (MyCodingAddition)
- (void)encodeWithCoder:(NSCoder*)encoder;
- (id)initWithCoder:(NSCoder*)decoder;
#end
#implementation ForeignClass (MyCodingAddition)
- (void)encodeWithCoder:(NSCoder*)encoder
{
[coder encodeObject:self.property withKey:…]
…
}
- (id)initWithCoder:(NSCoder*)decoder
{
self = [super init];
if (self)
{
self.property = [decode objectForKey:…];
…
}
return self;
}
#end
I am trying to use NSCoding to save and recover application state. I haven't used it before.
In my app, the protocol methods encodeWithCoder and initWithCoder are never being called. I have prepared a simple test case with the same problem so hopefully somebody can tell me what I am doing wrong.
Here is my CodingTest.h file:
#import <Foundation/Foundation.h>
#interface CodingTest : NSObject <NSCoding>
- (void) saveData;
- (void) loadData;
- (id) init: (int) testValue;
#end
Here is CodingTest.m
#import "CodingTest.h"
#interface CodingTest()
#property int testInt;
#end
#implementation CodingTest
- (id) init: (int) testValue
{
_testInt = testValue;
return self;
}
-(void) loadData
{
CodingTest *newTestClass = [NSKeyedUnarchiver unarchiveObjectWithFile:#"testfile"];
}
-(void) saveData
{
[NSKeyedArchiver archiveRootObject:self toFile:#"testfile"];
}
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInt:_testInt forKey:#"intValue"];
}
- (id)initWithCoder:(NSCoder *)decoder {
int oldInt = [decoder decodeIntForKey:#"intValue"];
return [self init:oldInt];
}
#end
I call it as follows:
CodingTest *testCase = [[CodingTest alloc] init:27];
[testCase saveData ];
[testCase loadData];
init, saveData and loadData are all being called. But encodeWithEncoder and initWithCoder are never called. What am I doing wrong?
The problem is that "testfile" on its own is not a valid filename. If this is changed to "tmp/testfile" it works fine.
Interestingly, if you get the file name wrong on encode, it won't call the decode function, even though the decode call doesn't specify the file name.
It works if I put it in viewDidLoad but I can't imagine that's the best place to do it. I tried putting it here:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// HERE
}
return self;
}
But that didn't work. Where should I put it?
In this example I'm talking about creating the NSMutableArray (alloc and initing it) for this class.
You could go with a lazy-loading technique as A-Live suggests in the comments, such that your array will be initialized when you actually need it. The idea is that in a property getter, you first check to see if your array was initialized. If not, initialize then return it.
Example
Note: This is a crude and untested example, and you may need to make
the necessary changes depending on whether or not you use ARC.
MyViewController.h
#interface MyViewController : UITableViewController
{
NSArray *_myArray;
}
#property (nonatomic, readonly) NSArray *myArray;
-(void)doSomething;
#end
MyViewController.m
#interface MyViewController()
-(NSArray *)fetchArrayData;
#end
#implementation MyViewController
#synthesize myArray = _myArray;
#pragma mark - Property Getter
-(NSArray *)myArray
{
if (_myArray==nil)
_myArray = [[self fetchArrayData] retain];
return _myArray;
}
#pragma mark - Cleanup
-(void)dealloc
{
[_myArray release];
[super dealloc];
}
#pragma mark - Instance Methods
-(void)doSomething
{
NSLog(#"myArray: %#", self.myArray);
}
#pragma mark - Private Methods
-(NSArray *)fetchArrayData
{
NSArray *arrayData = [NSArray arrayWithObjects:#"Apples", #"Oranges", nil];
return arrayData;
}
#end
You have 4 initialization methods:
- (id)init
- (id)initWithCoder:(NSCoder *)aDecoder
- (id)initWithStyle:(UITableViewStyle)style
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
It all dependes on how you're instantiating the class.
You can initialize your instance variables in initWithStyle: if you create your controller programmaticaly or in initWithCoder:/awakeFromNib if it's loaded from nib/storyboard.
I have an ivar which is mentioned in my header
#interface MyClass : UIView{
int thistone;}
- (IBAction)toneButton:(UIButton *)sender;
#property int thistone;
#end
and I have synthesized it in the implementation:
#implementation MyClass
#synthesize thistone;
- (IBAction)toneButton:(UIButton *)sender {
if(thistone<4)
{thistone=1000;} // I hate this line.
else{thistone=thistone+1; }
}
I cannot find (or find in any manual) a way to set a nonzero initial value. I want it to start at 1000 and increase by 1 each time I press the button. The code does exactly what I intend, but I'm guessing there's a more proper way to do it that saves me the if/else statement above. Code fixes or pointers to specific lines in online documentation greatly appreciated.
Every object has a variant of the init method called at instantiation. Implement this method to do such setup. UIView in particular have initWithFrame: and initWithCoder. Best to override all and call a separate method to perform required setup.
For example:
- (void)commonSetup
{
thisTone = 1000;
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self commonSetup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super initWithCoder:coder])
{
[self commonSetup];
}
return self;
}