ios nsCoding set but not calling encodeWithCoder - ios

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.

Related

custom deleget not responding ToSelector

Following is my code, there is no error but selector is not responding.
Code in ExampleTableviewSubProductDetail.h
#protocol EnterAmountDelegate <NSObject>
-(void)titlechange:(NSInteger)amount;
#end
#class ASIFormDataRequest;
#interface ExampleTableviewSubProductDetail : UIViewController<UIScrollViewDelegate>
{
}
#property (nonatomic, strong) id <EnterAmountDelegate>delegate;
Code in ExampleTableviewSubProductDetail.m
#implementation ExampleTableviewSubProductDetail
#synthesize delegate;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if([delegate respondsToSelector:#selector(titlechange:)])
{
//send the delegate function with the amount entered by the user
[delegate titlechange:20];
}
code in HostProductdetailViewController.h
#import "ViewPagerController.h"
#import "ExampleTableviewSubProductDetail.h"
#interface HostProductdetailViewController : ViewPagerController <ViewPagerDataSource, ViewPagerDelegate, EnterAmountDelegate>
{
}
code in HostProductdetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = self;
self.delegate = self;
}
-(void)titlechange:(NSInteger)amount
{
NSLog(#"sdfsf");
}
In the viewwillapper following Line always return false
if([delegate respondsToSelector:#selector(titlechange:)])
Please let me know if i am missing anything.
Thanks
When pushing from HostProductdetailViewController to ExampleTableviewSubProductDetail you need to set the exampleTableviewSubProductDetail.delegate = self
As I see some other potentially dangerous things in your code try checking this example. It consists of 2 simple classes which are connected via delegate. Watch out for strong references on delegates as this code of yours will produce a retain cycle and cause a memory leak.
Protocol:
// defining a custom protocol
#protocol PingProtocol <NSObject>
- (void)didPing;
#end
Ping class:
//
// This class will be able to send notifications via delegate for the protocol PingProtocol
// Any object that implements PingProtocol will be able to assign itself to the delegate property and will be notified to all protocol methods
//
#interface PingClass : NSObject
// The listener object that implements PingProtocol
// Note this should be weak or there will a retain cycle
#property (nonatomic, weak) id<PingProtocol> delegate;
#end
#implementation PingClass
// Some event that happens will check if the delegate actually implements this method and call it.
// The respondsToSelector is not necessary in this case since the method is not optional though.
- (void)onEvent:(id)sender
{
if([self.delegate respondsToSelector:#selector(didPing)])
{
[self.delegate didPing];
}
}
// Will create a timer which will call onEvent: every second.
// Note there should be some way to invalidate the timer as this will cause a memory leak for the PingClass
- (void)startPing
{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(onEvent:) userInfo:nil repeats:YES];
}
#end
Listener:
//
// This class will listen to PingProtocol methods.
// It will need to implement all non-optional methods defined by PingProtocol
//
#interface ListenerClass : NSObject<PingProtocol>
#property (nonatomic, strong) PingClass *someClass;
#end
#implementation ListenerClass
// will create a PingClass object and asign itself as a delegate to start listening to delegate methods
- (void)startListening
{
self.someClass = [[PingClass alloc] init];
self.someClass.delegate = self;
[self.someClass startPing];
}
// A protocol method
- (void)didPing
{
NSLog(#"Ping");
}
#end
Most likely you are missing self:
if([self.delegate respondsToSelector:#selector(titlechange:)])
You need to watch out for these things. The delegate in your case is closer to a function pointer then an actual object. You might also be able access it via _delegate as well.

delegate deallocated during the operation

I'm looking for a solution that solves the following problem:
I have a NSOperation which download the image in the background:
#protocol CoreImageDownloadingOperationDelegate <NSObject>
#required
-(void) handleResponse:(UIImage*) response;
-(void) handleException:(MobileServiceException*) exception;
#end
#interface CoreImageDownloadingOperation : NSOperation{
}
-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;
#property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;
When it's finish the downloading, calling the delegate method, to set the image to the imageView:
pragma mark - overridden from NSOperation
- (void) main {
if (self.isCancelled)
return;
#autoreleasepool {
#try {
UIImage* image = [[CoreEnvironment sharedInstance] getImageFromServer: [imageID stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if (self.isCancelled)
return;
if(delegate){
[delegate handleResponse:image];
}
else
NSLog(#"CachedImageView already deallocated");
}#catch (NSException *exception) {
TestLog(#"%#", exception.reason);
if (self.isCancelled)
return;
if(delegate && [exception isKindOfClass:[MobileServiceException class]])
[delegate handleException:(MobileServiceException*)exception];
}
}
}
The problem is: when I go to another page while the image is downloading, the cachedImageView is deallocated, but when the imageDownloadingOperation finishes downloading, the delegate is not nil, and it's trying to handleResponse... And of course I get message sent to deallocated...
I alloc init the operation like this in the CachedImageView:
CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
or:
-[CachedImageView isKindOfClass:]: message sent to deallocated instance 0x18868550
The problem is: when I go to another page while the image is
downloading, the cachedImageView is deallocated
The usual way to deal with this is to remove itself as a delegate in the dealloc of CachedImageView. Like
// in CachedImageView
- (void)dealloc {
// CachedImageView keeps a reference to the operation
// called imageDownloadingOperation
imageDownloadingOperation.delegate = nil;
[super dealloc];
}
Where is your protocol declaration? I'd expect to see this:
#protocol CoreImageDownloadingOperationDelegate <NSObject>
- (void) handleResponse:(UIImage *) image;
#end
#interface CoreImageDownloadingOperation : NSOperation{
}
-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;
#property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;
You are getting the warning/crash because it can't find the responder handleResponse:
Also when invoking the delegate your better off doing:
if ([self.delegate respondsToSelector:#selector(handleResponse:)])
[self.delegate handleResponse:image];
You don't need to check if (self.delegate && [self.delegate responds .... as it will return nil if the delegate is nil && if the selector is not implemented.
EDIT *
Where you create:
CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
I suspect this is being released, turn this into a property of the class it's in. Then try again (make sure to release it when you're done though) i.e
In your .h
#property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;
Then initialise with:
if (!self.imageDownloadingOperation)
self.imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
A better way of writing the code is :
if([self.delegate respondsToSelector:#selector(handleResponse:)){
[self.delegate handleResponse:image];
}
This will avoid your crash.
I've solved the problem, I used CW0007007 solution and my own solution. So I turned my operation into a retained property:
#property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;
after this I checked if the operation is still alive
if (!imageDownloadingOperation)
imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
then added to the operation queue.
In the dealloc set the delegate to nil ( ofc only if the operation is alive ), and release it:
if (imageDownloadingOperation) {
imageDownloadingOperation.delegate = nil;
[imageDownloadingOperation release];
}
in the operation: ( and now if the imageView deallocated, its delegate will be nil, and won't crash anytime )
if (delegate)
if ([delegate respondsToSelector:#selector(handleResponse:)])
[delegate handleResponse:image];

Accessing class method in objective c. Using self or classname?

I am learning iOS programming and am confused by the following code regarding the use of keyword self.
From my understanding, self is like Java's this. It refers to the current instance. When I want to call a class method, the usual way should be like [PlayingCard validSuits]; But it's also OK to invade a class method on an instance, right? Like [self validSuits]; (I am in the class so self refers to an instance of PlayingCard)
But in the following code, it gives error somewhere but looks ok elsewhere.(Pointed out by 3 comments, this is within Xcode 5.1)
Am I missing anything?
(P.S. I think I am having the similar problem as here, which no one answered yet. He got the same error even using [PlayingCard validSuits]. )
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[PlayingCard rankStrings] count] -1;
//1. [self rankStrings] works fine.**
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [PlayingCard rankStrings];
//2. if change rankStrings to self, then error:
//No visible #interface for 'PlayingCard' declares the selector 'rankStrings'
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[PlayingCard validSuits] containsObject:suit]) {
//3.error when changed to [self validsuits]
//No visible #interface for 'PlayingCard' declares the selector 'validsuits'**
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
The header file:
// PlayingCard.h
#import "Card.h"
#interface PlayingCard : Card
#property (nonatomic, strong) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
If you are calling another class method from inside a class method (of the same class) you can just use [self classMethod]. If however you are in an instance method and you need to call that classes class method you can use [[self class] classMethod]
As pointed out by #Martin R - if you subclass PlayingCard, calling self in a class method will then be that subclass and not PlayingCard.
EDIT:
For completeness you need to do:
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[self rankStrings] count] -1;
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [[self class] rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[[self class] validSuits] containsObject:suit]) {
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end

NSCoder game data storage/retrieve sanity check

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.

_delegate respondsToSelector:selector is not calling the method

I have one class and one view controller. class has _delegate respondsToSelector:selector. But it is not calling the method(selector). After viewing my code below, would you please tell what am I doing wrong. FYI, This code worked before, but suddenly stopped working.
MyClass.h:
#protocol MyClassDelegate <NSObject>
#optional
-(void) receivedEvent:(TripEvent *) event;
#end
#interface MyClass : NSObject<MyClassEngine>
#property(assign) id<MyClassDelegate> delegate;
#property BOOL delegateOnMainThread;
-(id) initWithDelegate:(id<UbiTripRecorderDelegate>) tripDelegate
onMainThread:(BOOL) onMainThread;
- (void) dispatchDelegate:(SEL)selector
withObject:(id)obj;
MyClass.m contains the following code:
#import "MyClass.h"
#implementation MyClass
....
....
-(void) startPlaying {
[self dispatchDelegate:#selector(receivedEvent:) withObject:newEvent];
}
- (void) dispatchDelegate:(SEL)selector withObject:(id)obj {
#try {
if(_delegate && [_delegate respondsToSelector:selector]) {
NSLog(#"I can't come to this point");
}
}
}
I've also added the following code in my view controller (MyViewController.h):
#interface MyViewController : BaseViewController<MyClassDelegate>
And finally implemented the method in MyViewcontroller.m :
-(void) receivedEvent : (TripEvent *) event {
NSLog(#"I'm called");
}
The problem is that the if(_delegate && [_delegate respondsToSelector:selector]) - logic never return true. Can anybody please check the code and let me know, if anything is wrong with my code?
Make sure your _delegate is not nil, otherwise the if statement is never true.
Also you can simplify
if(_delegate && [_delegate respondsToSelector:selector])
to
if([_delegate respondsToSelector:selector])
because message send to nil always return 0 or NO. In this case [_delegate respondsToSelector:selector] will return NO if _delegate is nil
Also, you did not declare a method in header will not cause any runtime difference. It may have compiler warning/error if such method not found.

Resources