Singleton pattern for sharing data between objects - objective-c

I'm trying to pass data around objects using singleton pattern. Here is my code
SearchData.m
#implementation SearchData
#synthesize theName = _theName;
-(id)init
{
if(self = [super init])
{
_theName = #"Default";
}
return self;
}
static SearchData *sharedSingleton = NULL;
+(SearchData *)sharedSearchData
{
#synchronized(self)
{
if (sharedSingleton == NULL)
{
sharedSingleton = [[self alloc]init];
}
return sharedSingleton;
}
}
#end
FirstView.m
...
-(id)init
{
if (self = [super init])
{
SearchData *data = [SearchData sharedSearchData];
self.aName = [data theName];
}
return self;
}
...
The problem is that I get
Incompatible pointer types sending NSString to parameter of type NSStream.
What is wrong here ?
How to pass data to aName ivar ?

In your declaration of aName, did you mistype NSString as NSStream? Stranger things have happened.

Because searchdata (NSStream) doesn't go into an NSString.
Try:
[[NSString alloc] initWithData:searchData ...

Related

Chain multiple method calls and wait until all are finished in Objective-C

Say I had the following method call to make:
[[[[Foo init] addString:#"one"] addString:#"one"] addString:#"three"]
Where Foo contains:
#implementation Foo {
NSString *string;
}
- (void)addString:(NSString *)text {
string = [string stringByAppendingString:text];
}
Say I wanted to wait until all the addString methods were completed and wanted to print "onetwothree", but I had no idea how many times addString was going to be called. How would I achieve this? I tried to do this using dispatch_group as in:
#implementation Foo {
NSString *string;
}
- (id)init {
// setup
[self complete];
string = #"";
}
- (void)addString:(NSString *)text {
dispatch_group_async(group, queue, ^ {
string = [string stringByAppendingString:text];
NSLog(#"%#", text);
}
}
- (void)complete {
dispatch_group_notify(group,queue, 0), ^ {
NSLog(#"Final String: %#", string);
});
}
But this prints:
One
Final String:
Two
Three
Any idea how I can get this done?
If you do want to use async processing you can do it this way:
#import "Foo.h"
#implementation Foo {
NSString *_string;
dispatch_group_t _group;
dispatch_queue_t _queue;
}
- (instancetype)init
{
self = [super init];
if (self) {
_string = #"";
_group = dispatch_group_create();
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}
return self;
}
- (void)addStrings:(NSArray *)strings {
dispatch_group_async(_group, _queue, ^() {
NSMutableString *result = [NSMutableString new];
for (NSString *str in strings) {
[result appendString:str];
}
_string = [result copy];
});
}
- (void)complete {
dispatch_group_notify(_group, _queue, ^ {
NSLog(#"Final String: %#", _string);
});
}
Call this way:
Foo *foo = [[Foo alloc] init];
[foo addStrings:#[#"one", #"two", #"three"]];
[foo complete];

How to check if an object is the last object on NSArray

How can I check if an object in the last object on an NSArray?
I've tried:
if ([currentStore isEqual:[Stores lastObject]])
{
//Code
}
but it didn't work.
Any idea?
Thanks!
or try this
BOOL lastElement = false;
NSUInteger index = [stores indexOfObject:currentStore];
if (index != NSNotFound)
{
lastElement = (index == [stores count] - 1);
}
Bit modified try this:
NSUInteger index = [stores indexOfObject:currentStore];
if (index == ([stores count]-1))
{
NSLog(#"Yes its Last");
}
If you didn't override isEqual method, the base class implementation of NSObject::isEqual only check if both pointers points to the same address.
This excellent article http://nshipster.com/equality/ explain objc equality principles
The below sample logs - Testing Stores - works fine
#interface Stores : NSObject
#property (strong, nonatomic) NSString* name;
- (instancetype) initWithName:(NSString*) name;
#end
#implementation Stores
- (instancetype) initWithName:(NSString*) name;
{
_name = name;
return self;
}
- (BOOL)isEqualToStores:(Stores*) Stores
{
if (!Stores)
return NO;
if (![_name isEqualToString:Stores.name] )
return NO;
return YES;
}
- (BOOL)isEqual:(id)object
{
if (self == object)
{
return YES;
}
if (![object isKindOfClass:[Stores class]])
{
return NO;
}
return [self isEqualToStores:(Stores *)object];
}
#end
-(void) testStores
{
Stores* last = [[Stores alloc] initWithName:#"5"];
NSArray* arr = #[
[[Stores alloc] initWithName:#"1"],
[[Stores alloc] initWithName:#"2"],
[[Stores alloc] initWithName:#"3"],
[[Stores alloc] initWithName:#"4"],
[[Stores alloc] initWithName:#"5"]
//last
];
if ([last isEqual:[arr lastObject]])
{
NSLog(#"Testing Stores - works fine");
}
else
{
NSLog(#"Testing Stores - opps!?1?!?");
}
}

Class Factory Method with Multiple Parameters

I am practicing my Objective C skills and have come across a small issue, although I can't seem to find a straight answer to this issue anywhere I look. In the Apple developer guides I am reading, there is nothing in there telling me how to use a class factory method with multiple parameters (say 3 parameters) and return the initialized object via the overridden init method.
Here I have a simple class called XYZPerson.
#implementation XYZPerson
// Class Factory Method
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
// need to return [ [self alloc] init with the 3 paramaters]; here
// Or some other way to do so..
}
// Overridden init method
- (id)init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = firstName;
_lastName = lastName;
_dateOfBirth = dateOfBirth;
}
return self;
}
// Use the initialized instance variables in a greeting
- (void)sayHello {
NSLog(#"Hello %# %#", self.firstName, self.lastName);
}
And then in my main I am instantiating an XYZPerson object
XYZPerson *person = [XYZPerson person:#"John" with:#"Doe" andWith:[NSDate date]];
[person sayHello];
Can anybody give me a small pointer on how to do this correctly?
If I understand your question, you want the following:
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
XYZPerson *result = [[self alloc] init:firstName with:lastName andWith:dateOfBirth];
return result;
}
If you aren't using ARC, add an autorelease to the return.
BTW - change the return type of the init method to instancetype instead of id.
#implementation XYZPerson
// Class Factory Method
+ (instanceType ) person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
return [[[self class] alloc]init: firstName with: lastName andWith:dateOfBirth];
}
- (instanceType ) init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = [firstName copy];
_lastName = [lastName copy];
_dateOfBirth = [dateOfBirth copy];
//nb added copy to each of these, we do not own these objects, they could be lost to us..
/// or you could do this instead..
//assuming you synthesised getters/setters for (strong) properties..
[self setFirstName:firstName];
[self setLastName:lastName];
[self setDateOfBirth: dateOfBirth];
}
return self;
}

iOS NSKeyedUnarchiver calling singleton getter

I have a singleton class that will initialize it's data from a web service then save itself to a file (using NSCoding and NSKeyedUnarchiver) so I can just initialize this file instead of pulling all the data from the web again. Because it's a singleton instance that is being saved, I want to be able to call the singleton getter like you normally would, check to see if there's an archived copy and if not, pull the data down and archive it. Archiving is working fine, but when I try to call [[NSKeyedUnarchiver unarchiveObjectWithFile: filePath] retain] it calls the sharedInstance getter before sharedInstance is initialized. This causes init to be called and the app then downloads all the data again, just to be subsequently overwritten by the unarchiver.
Am I doing something wrong with my setup, or is there another way of Serializing this data?
Here's some (simplified) code:
#implementation Helmets
#synthesize helmetList, helmetListVersion;
//Class Fields
static Helmets *sharedInstance = nil;
// Get the shared instance and create it if necessary.
+ (Helmets *)sharedInstance {
//if(sharedInstance == nil){
//[Helmets unarchive]; //This does not work! Calls sharedInstance() again (recursion)
if(sharedInstance == nil){
sharedInstance = [[super allocWithZone:NULL] init]; //Pull from web service
[Helmets archive]; //Save instance
//}
//}
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
helmetList = [[NSMutableArray alloc]init];
//Get our data from the web service and save it (excluded)
}
}
return self;
}
//This works!
+(void)archive{
if([NSKeyedArchiver archiveRootObject:sharedInstance toFile:[Helmets getFilePath]]){
NSLog(#"Archiving Successful");
}
else{
NSLog(#"Archiving Failed");
}
}
//This works, but calls getInstance causing data to be downloaded anyways!
+(void)unarchive{
// Check if the file already exists
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *filePath = [Helmets getFilePath];
if ([filemgr fileExistsAtPath: filePath])
{
sharedInstance = [[NSKeyedUnarchiver unarchiveObjectWithFile: filePath] retain];
}
[filemgr release];
}
Instance is initialized like:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
...
[Helmets unarchive]; //This calls sharedInstance() too soon!
[Helmets sharedInstance];
}
The class implements NSCoding in the .h and overrides initWithCoder and encodeWithCoder (Archiving is working).
Thanks in advance!
In the end you need a private method to set your shared instance in addition to the one you have, and you need a different init, again private to the implementation.
- (id)initAndFetch:(BOOL)fetch
{
if((self = [super init])) {
...
if(fetch) { do the web fetch };
...
}
}
In the +sharedInstance method, you will pass YES.
Then your decode will look like:
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [self initAndFetch:NO])) {
title = [decoder decodeObjectForKey:#"title"];
...
sharedInstance = self;
}
return self;
}
I have got to make it work the following manner.
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (!self) {
return nil;
}
UserInfo *user =[UserInfo sharedInstance];
return user;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
UserInfo *user =[UserInfo sharedInstance];
[aCoder encodeObject: user.phoneNumber forKey:#"phoneNumber"];
}
-(void)save
{
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:[UserInfo sharedInstance]] forKey:#"USER_DATA"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
-(UserInfo*)currentUser
{
NSData *data =[[NSUserDefaults standardUserDefaults] objectForKey:#"USER_DATA"];
UserInfo *savedUser =[NSKeyedUnarchiver unarchiveObjectWithData:data];
UserInfo *user =[UserInfo sharedInstance];
return user;
}

Objective-C: why my NSString is not retaining its value?

The problem lies within the 'initWithCoder' method. When I want to retrieve "Coins_Key" from where I saved it by calling the 'saveData' method in my 'main' class and I pass in the key "self.keyName," the value of keyName is 0.
//Class coins.h
#property (retain) NSString* keyName;
#property (retain) NSString* keyValue;
//Class coins.m
#synthesize keyName;
-(void) saveData:(NSString *)number: (NSString *)keyID
{
self.keyName = keyID;
self.keyValue = number;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
NSLog(#"Encoded keyName: %#", keyName);
[encoder encodeObject:keyValue forKey:keyName];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.keyValue = [decoder decodeObjectForKey:self.keyName];
NSLog(#"Decoded Coins: %#", self.keyValue);
return self;
}
//Class main
[Coins *coin3 = [[Coins alloc] init];
[coin3 saveData:#"6" :#"Coins_Key"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:coin3];
coin3 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
You're not quite grasping the encoder/decoder workflow.
Using the encodeObjectForKey: and decodeObjectForKey: methods properly, you should be passing as an argument the key that should be used to store the value. This key must remain constant.
You should also not require callers to provide the key your Coin object uses to store data. Take this simple example as a more correct/efficient method (assuming I understand the purpose of your class):
// Class Coins.h
#property (assign) int numberOfCoins;
// Class Coins.m
#define NUM_COINS_KEY #"NUM_COINS_KEY"
#synthesize numberOfCoins;
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) { // Use [super initWithCoder:decoder] here if your superclass supports it
self.numberOfCoins = [decoder decodeIntForKey:NUM_COINS_KEY];
NSLog(#"Decoded Coins: %d", self.numberOfCoins);
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
NSLog(#"Encoded keyName: %#", keyName);
[encoder encodeInt:self.numberOfCoins forKey:NUM_COINS_KEY];
}
// Class main
Coins *coin = [[Coins alloc] init];
coin.numberOfCoins = 6;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:coin];
[coin release]; // If you're just playing around, this is probably overkill, but a good habit
coin = [NSKeyedUnarchiver unarchiveObjectWithData:data];