NSCoding with a singleton subclass - objective-c

I have a number of derived classes whose common base-class conforms to NSCoding. I want to be able to easily encode an NSArray holding instances of the various deriving classes.
#interface Base : NSObject <NSCoding>
#end
#interface DerivedSingleton : Base
+(instancetype) sharedInstance;
#end
#interface DerivedNonSingleton : Base
#end
The deriving singleton should have only one instance in the running system. It doesn't actually have any state to encode in the coder. It's instance is created with the +(void) initialize class method.
DerivedSingleton *sharedInstance;
#implementation DerivedSingleton
+(void) initialize
{
sharedInstance = [DerivedSingleton new];
}
+(instancetype) sharedInstance
{
return sharedInstance;
}
#end
So, if I now make an array holding instances of the classes, and encode it:
NSArray *const array = #[
[DerivedSingleton sharedInstance],
[DerivedNonSingleton new],
[DerivedNonSingleton new]];
NSData *const arrayData = [NSKeyedArchiver archivedDataWithRootObject: array];
When I later decode it, I require references to the shared singleton to decode as references to the shared singleton…
NSArray *const decodedArray = [NSKeyedUnarchiver unarchiveObjectWithData: arrayData];
[decodedArray objectAtIndex: 0] == [DerivedSingleton sharedInstance];
I note that the NSCoding protocol wants me to implement initWithCoder: for the singleton, but during decode, I want this class to provide the shared instance, not a newly alloced object.

Something like this should work
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [super init];
return [self.class shared];
}

I would do something similar with the singleton:
.h
#import <Foundation/Foundation.h>
#pragma mark - Interface
#interface SerializableSingleton : NSObject <NSCoding>
#pragma mark - Class methods
#pragma mark - Shared instance
+ (instancetype)sharedInstance;
#end
.m
#import "SerializableSingleton.h"
#pragma mark - Reference
static id _serializableSingletonSharedInstance = nil;
#pragma mark - Implementation
#implementation SerializableSingleton
#pragma mark - Class methods
#pragma mark - Shared instance
+ (instancetype)sharedInstance {
#synchronized([self class]) {
if (_serializableSingletonSharedInstance == nil) {
_serializableSingletonSharedInstance = [[[self class] alloc] init];
}
}
return _serializableSingletonSharedInstance;
}
#pragma mark - Instance methods
#pragma mark - <NSCoding>
- (id)initWithCoder:(NSCoder *)aDecoder {
#synchronized([self class]) {
if (_serializableSingletonSharedInstance == nil) {
if (self = [super init]) {
// decode the desired data...
}
_serializableSingletonSharedInstance = self;
}
}
return _serializableSingletonSharedInstance;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
// encode the desired data
}
#end

Related

"Missing Context for method declaration" with the constructor

Im getting only this failure after having built the project.
Im using the XCode 4.6.3
class.m
#import "Car.h"
//constructor
-(id)init //<----- ***MISSING CONTEXT FOR METHOD DECLARATION***
{
self = [super init];
if(self){
self.brand = #"";
self.model = #"";
self.vin = 0;
}
return self;
class.h contains no error.
#import <Foundation/Foundation.h>
#interface Car : NSObject
{
NSString *brand, *model;
NSNumber *vin;
}
//set
-(void) setBrand:(NSString *) newBrand;
-(void) setModel:(NSString *) newModel;
-(void) setVIN:(NSNumber *) newVIN;
//get
-(NSString *) getBrand;
-(NSString *) getModel;
-(NSNumber *) getVIN;
//methods
-(void) accelerateTo100;
-(void) fuelConsuming;
-(void) hardStop;
#end
Can you help me with this. Thanks alot.
Answer is what #CodaFi explained. Try this
#import "Car.h"
#implementation Car
-(id)init
{
self = [super init];
if(self){
[self setBrand : #""];
[self setMode1 : #""];
[self setVIN : #""];
}
return self;
}
#end
Implementations of methods related to the Car class are always wrapped in #implementation Car and terminated with an #end. You're declaring and implementing methods without telling the compiler which class they belong to.
Check that you don't have an #import "..." within the #implementation section.

Objective C methods [make release] issue [duplicate]

This question already has answers here:
Under automatic reference counting, why are retain, release, and dealloc not allowed?
(4 answers)
Closed 9 years ago.
I'm using the latest xcode.
implementation file:
SimpleCar.m:
#import "SimpleCar.h"
#implementation SimpleCar
// set methods
- (void) setVin: (NSNumber*)newVin {
[vin release];
vin = [[NSNumber alloc] init];
vin = newVin;
}
- (void) setMake: (NSString*)newMake {
[make release];
make = [[NSString alloc] initWithString:newMake];
}
- (void) setModel: (NSString*)newModel {
[model release];
model = [[NSString alloc] initWithString:newModel];
}
// convenience method
- (void) setMake: (NSString*)newMake
andModel: (NSString*)newModel {
// Reuse our methods from earlier
[self setMake:newMake];
[self setModel:newModel];
}
//get methods
- (NSString*) make; {
return make;
}
- (NSString*) model;{
return model;
}
- (NSNumber*) vin;{
return vin;
}
-(void) dealloc
{
[vin release];
[make release];
[model release];
[super dealloc];
}
#end
interface file:
SimpleCar.h:
#import <Foundation/Foundation.h>
#interface SimpleCar : NSObject {
NSString* make;
NSString* model;
NSNumber* vin;
}
// set methods
- (void) setVin: (NSNumber*)newVin;
- (void) setMake: (NSString*)newMake;
- (void) setModel: (NSString*)newModel;
// convenience method
- (void) setMake: (NSString*)newMake
andModel: (NSString*)newModel;
// get methods
- (NSString*) make;
- (NSString*) model;
- (NSNumber*) vin;
#end
I get an error in the implementation file when I type "[vin release], [model release], [make release]" and I cannot run the program.
ARC is turned on, therefore memory management is automatic.
A modern definition of that class would be declared as:
#interface SimpleCar : NSObject
#property(copy) NSString *make;
#property(copy) NSString *model;
#property(copy) NSNumber *vin;
- initWithMake:(NSString*)make model:(NSString*)model vin:(NSNumber*)vin;
#end
And would be implemented as:
#implementation SimpleCar
- initWithMake:(NSString*)make model:(NSString*)model vin:(NSNumber*)vin
{
if (self = [super init]) {
_make = [make copy];
_model = [model copy];
_vin = [vin copy];
}
return self;
}
#end
You wouldn't typically implement a convenience method like setMake:andModel:. It just adds API footprint without really buying much in the way of convenience. It also raises questions like "What happens when I observe either make or model?".

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.

How to create a class for a type of object?

Ok, I'm still getting used to how Objective-c works.
Lets suppose I'm making a todo list app. Instead of just reading from a plist and loading it into a table, some people say that you should create a class, lets call it ToDo that contains for example:
NSString *title;
NSString *description;
Ok, fine. Now how would I use such a class to load my data from a plist or something? I don't understand how creating a little class helps. Can anyone explain to me how this works?
Just creat a subclass of NSObject with NSCoding protocol(if you want to save it in a plist)
Example: *.h file
#interface ToDo : NSObject <NSCoding> {
NSString *title;
NSString *toDoDescription;
}
#property (copy) NSString *title;
#property (copy) NSString *toDoDescription;
#end
Example: *.m file
#implementation ToDo
#synthesize title, toDoDescription;
- (id)init
{
if ((self = [super init])) {
[self setTitle:#"none"];
[self setToDoDescription:#"none"];
}
return self;
}
- (void)dealloc
{
[title release];
[toDoDescription release];
[super dealloc];
}
// Next two methods and coding protocol are needed to save your custom object into plist
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super init])) {
title = [[aDecoder decodeObjectForKey:#"title"] copy];
toDoDescription = [[aDecoder decodeObjectForKey:#"toDoDescription"] copy];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:title forKey:#"title"];
[aCoder encodeObject:toDoDescription forKey:#"toDoDescription"];
}
#end
Use NSKeyedArchiver to covert your object into NSData. And then add it to a plist.

how to declare a global variable in Objective-C?

// MyClass.h
#interface MyClass : NSObject
{
NSDictionary *dictobj;
}
#end
//MyClass.m
#implementation MyClass
-(void)applicationDiDFinishlaunching:(UIApplication *)application
{
}
-(void)methodA
{
// Here i need to add objects into the dictionary
}
-(void)methodB
{
//here i need to retrive the key and objects of Dictionary into array
}
My question is since both methodA and methodB are using the NSDictionary object [i.e dictobj] In which method should i write this code:
dictobj = [[NSDictionary alloc]init];
I can't do it twice in both methods, hence how to do it golbally?
First of all, if you need to modify contents of the dictionary, it should be mutable:
#interface MyClass : NSObject
{
NSMutableDictionary *dictobj;
}
#end
You typically create instance variables like dictobj in the designated initializer like this:
- (id) init
{
[super init];
dictobj = [[NSMutableDictionary alloc] init];
return self;
}
and free the memory in -dealloc:
- (void) dealloc
{
[dictobj release];
[super dealloc];
}
You can access your instance variables anywhere in your instance implementation (as opposed to class methods):
-(void) methodA
{
// don't declare dictobj here, otherwise it will shadow your ivar
[dictobj setObject: #"Some value" forKey: #"Some key"];
}
-(void) methodB
{
// this will print "Some value" to the console if methodA has been performed
NSLog(#"%#", [dictobj objectForKey: #"Some key"]);
}
-----AClass.h-----
extern int myInt; // Anybody who imports AClass.h can access myInt.
#interface AClass.h : SomeSuperClass
{
// ...
}
// ...
#end
-----end AClass.h-----
-----AClass.h-----
int myInt;
#implementation AClass.h
//...
#end
-----end AClass.h-----