Google Analytics tracking doesn't work for me on ios and my GANTrackerDelegate never gets called - iphone

I've implemented google analytics in my ios app but nothing ever gets logged to analytics. I've been running it for a month, so I know there's something that's not working.
I added a GANTrackerDelegate to trace why my dispatch calls are failing, but it never gets called. I know dispatch is getting called. Does anyone know? Here's my class.
#import <Foundation/Foundation.h>
#import "GANTracker.h"
#interface trackerDelegate : NSObject<GANTrackerDelegate> {
}
#end
#import "trackerDelegate.h"
#implementation trackerDelegate
#pragma mark GANTrackerDelegate
- (void)trackerDispatchDidComplete:(GANTracker *)tracker
eventsDispatched:(NSUInteger)eventsDispatched
eventsFailedDispatch:(NSUInteger)eventsFailedDispatch {
NSLog(#"events dispatched: %d, events failed: %d", eventsDispatched, eventsFailedDispatch);
}
#end
Here's my tracker class. Note, I can trace all of these things getting called, and I know that dispatch is called repeatedly and returns 'YES', but the delegate routine never gets called. It might be a coding thing, but I'm trying to see if the dispatch calls are failing or not. The id I'm using is valid because it works for Android.
tracker.h
#interface Tracker : NSObject {
}
+ (void) startTracking;
+ (void) endTracking;
+ (void) dispatch;
+ (void) trackPageView : (NSString *) pageId;
+ (void) trackEvent : (NSString *) categoryId
: (NSString *) actionID
: (NSString *) labelID
: (int) tvalue;
#end
tracker.m
#import "Tracker.h"
#import "trackerDelegate.h"
#implementation Tracker
static BOOL trackingOn = false;
static BOOL dirty = false;
trackerDelegate *tg = nil;
+ (void) startTracking
{
if (trackingOn){
return;
}
#try{
if (!tg)
{
tg = [[trackerDelegate alloc] init];
}
[[GANTracker sharedTracker] startTrackerWithAccountID:#"UA-VALID-GOOGLEID"
dispatchPeriod:-1
delegate:tg];
trackingOn = true;
}
#catch (NSException*)
{
trackingOn = false;
}
}
+ (void) endTracking
{
#try{
if (trackingOn)
{
[[GANTracker sharedTracker] stopTracker];
}
trackingOn = false;
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) dispatch
{
if (!dirty){
return;
}
[self startTracking];
#try
{
if (![[GANTracker sharedTracker] dispatch]) {
trackingOn = false;
NSLog(#"Google anaytics dispatch failed");
return;
}
dirty = false;
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) trackPageView : (NSString *) pageId
{
[self startTracking];
#try{
if (trackingOn){
NSError *error;
if (![[GANTracker sharedTracker] trackPageview:pageId
withError:&error]) {
trackingOn = false;
NSLog(#"Google anaytics track pageview failed");
return;
}
dirty = true;
}
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) trackEvent : (NSString *) categoryId
: (NSString *) actionID
: (NSString *) labelID
: (int) tvalue
{
[self startTracking];
#try
{
if (trackingOn){
NSError *error;
if (![[GANTracker sharedTracker] trackEvent:categoryId
action:actionID
label:labelID
value:tvalue
withError:&error])
{
trackingOn = false;
NSLog(#"Google anaytics track event failed");
return;
}
dirty = true;
}
}
#catch (NSException *){
trackingOn = false;
}
}
#end
Muchas Gracias!!

First of all you should initiate the Google Analytics account using the Web-property ID given to you at the time of creating an account in Google Analytics site.
Then you must add the following code to start the tracking and set the dispatch time. Then only you could view the analytics count in your account in Google Analytics online account. Here is the initiating code for that.
[[GANTracker sharedTracker]startTrackerWithAccountID:#"UA-xxxxxx-yy"
dispatchPeriod:10
delegate:nil];
You should also make sure, you have included the following code in the dealloc in the AppDelegate.
[GANTracker sharedTracker] stopTracker];
Hope this will work fine. Thanks.

Related

GCDAsyncSocket "didReadDataWithTag" Never called with NSOperation subclass

In a "getMyFile" method of some XIB file.
I am creating a object of class "A"(subclass of NSOperation) and adding it to a "myFileQueue"(object of NSOperationQueue).
myFileQueue.MaxConcurrentOperationCount = 1;
Problem : didReadDataWithTag" delegate NEVER called in any case.
#import "GCDAsyncSocket.h"
//and all other required classes are imported correctly
//Class : A
#interface A : NSOperation
{
{
GCDAsyncSocket* socket;
dispatch_queue_t dQueue;
BOOL isWorkDone;
}
}
#implementation A {
-main(){
#autoreleasepool {
isWorkDone = NO;
dQueue = dispatch_queue_create(#"MyDQueue", NULL);
socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dQueue];
//NOTE: Even after setting delegateQueue: dispatch_get_main_queue()
//didReadDataWithTag delegate never called
NSError *err = nil;
if (![socket connectToHost:#"192.168.1.142" onPort:12345 error:&err]) // Asynchronous!
{
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
do{
[NSThread sleep:0.2];
}while(!isWorkDone)
}
}
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{
NSString* myString= #"testing";
NSData* data=[myString dataUsingEncoding: [NSString defaultCStringEncoding] ];
[socket writeData:data withTimeout:-1 tag:1];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataToData:[GCDAsyncSocket ZeroData] withTimeout:-1 tag:TAG_RESPONSE_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSLog(#"didReadDataWithTag called...."); //this method never called in any case
isWorkDone = YES;
}
}

How Can i Know if GKAchievement is Completed?

I have built objectives for my game and everything works just fine accept the part of making the objectives not be called any more after any of them is completed.
I know there is a Property of the GKAchievement Class "completed" which is a boolean that returns yes when the Achievement is 100 percent done.
here is the method that called when a Achievement is 100 percent done it passes id which is the Achievement identifier and report the acheeee :
- (void)AchivmentDidAchive:(id)Achivment{
NSString *identifier = Achivment;
NSLog(#"%#",identifier);
self.achivment = [[GKAchievement alloc]initWithIdentifier:identifier];
self.achivment.showsCompletionBanner = YES;
if (!self.achivment.completed) {
self.achivment.percentComplete = 100;
NSLog(#"Reproting!");
[self.achivment reportAchievementWithCompletionHandler: ^(NSError *error)
{
}];
}
else {
NSLog(#"Achivment Completed!");
} }
what I am trying to do here is to set the percent completed to 100 and report it so in the next time ie want get called again.
but it always works... any better idea for how to handle this?
in interface add variable & property:
NSMutableDictionary *earnedAchievementCache;
#property (nonatomic, retain)NSMutableDictionary *earnedAchievementCache;
in .m:
#synthesize earnedAchievementCache;
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
if(self.earnedAchievementCache == NULL)
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache= tempCache;
[self submitAchievement: identifier percentComplete: percentComplete];
}
}];
}
else
{
GKAchievement* achievement= [self.earnedAchievementCache objectForKey: identifier];
if(achievement != NULL)
{
if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
{
achievement= NULL;
}
achievement.percentComplete= percentComplete;
}
else
{
achievement= [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
achievement.percentComplete= percentComplete;
[self.earnedAchievementCache setObject: achievement forKey: achievement.identifier];
}
if(achievement!= NULL)
{
//Submit the Achievement...
if (achievement.percentComplete>=100) {
//show banner
achievement.showsCompletionBanner = YES; //only in IOS 5+
}
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
if (error!=NULL){
NSLog(#"Error!!");
} else NSLog(#"all is well");
}];
}
}
}
in dealloc :
[self.earnedAchievementCache release];
i'm using the cache to not submit scores already submitted / completed
PS: the code is perfect just copy and paste it into your class and it will work
this is what I use in my helper Game Center class:
-(void) reportAchievementWithID:(NSString*) AchievementID {
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
if(error) NSLog(#"error");
for (GKAchievement *ach in achievements) {
if([ach.identifier isEqualToString:AchievementID]) { //already submitted
NSLog(#"Already submitted");
return ;
}
}
GKAchievement *achievementToSend = [[GKAchievement alloc] initWithIdentifier:AchievementID];
achievementToSend.percentComplete = 100;
achievementToSend.showsCompletionBanner = YES;
[achievementToSend reportAchievementWithCompletionHandler:NULL];
}];
}
note: I don't use percentages in my achievements, so you'd need to modify things a little bit if you do.

help with singleton's

I am trying to create a singleton User class in my app, here's the code:
#import "User.h"
#import "Login.h"
#import "SFHFKeychainUtils.h"
// Constants
static NSString* const kDBUserCurrentUserIDDefaultsKey = #"kDBUserCurrentUserIDDefaultsKey";
// Current User singleton
static User* currentUser = nil;
#implementation User
#synthesize username = _username;
#synthesize password = _password;
#synthesize delegate = _delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (NSString*)primaryKeyProperty {
return #"username";
}
+ (User*)currentUser {
if (nil == currentUser) {
id username = [[NSUserDefaults standardUserDefaults] objectForKey:#"kApplicationUserNameKey"];
if (!username) {
currentUser = [self new];
} else{
NSLog(#"CURRENT USER");
return self;
}
[currentUser retain];
}
return currentUser;
}
+ (void)setCurrentUser:(User*)user {
[user retain];
[currentUser release];
currentUser = user;
}
/**
* Implementation of a RESTful login pattern. We construct an object loader addressed to
* the /login resource path and POST the credentials. The target of the object loader is
* set so that the login response gets mapped back into this object, populating the
* properties according to the mappings declared in elementToPropertyMappings.
*/
- (void)loginWithUsername:(NSString*)username andPassword:(NSString*)password delegate:(NSObject<DBUserAuthenticationDelegate>*)delegate {
_delegate = delegate;
//[RKObjectManager sharedManager].client.username = username;
//[RKObjectManager sharedManager].client.password = password;
self.username = username;
self.password = password;
RKObjectMapping * userMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:#"LoginViewController"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/account/verify.json" objectMapping:userMapping delegate:self];
}
/**
* Implementation of a RESTful logout pattern. We POST an object loader to
* the /logout resource path. This destroys the remote session
*/
- (void)logout/*:(NSObject<DBUserAuthenticationDelegate>*)delegate */{
NSError * error = nil;
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:#"convore" error:&error];
NSLog(#"LOGGING OUT");
if ([self.delegate respondsToSelector:#selector(userDidLogout:)]) {
[self.delegate userDidLogout:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"DBUserDidLogoutNotification" object:nil];
}
- (void)loginWasSuccessful {
// Upon login, we become the current user
[User setCurrentUser:self];
NSError * error = nil;
// Persist the username for recovery later
[[NSUserDefaults standardUserDefaults] setValue:self.username forKey:#"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils storeUsername:self.username andPassword:self.password forServiceName:#"convore" updateExisting:TRUE error:&error];
// Inform the delegate
if ([self.delegate respondsToSelector:#selector(userDidLogin:)]) {
[self.delegate userDidLogin:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"DBUserDidLoginNotification" object:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
NSLog(#"Loaded payload: %#", [response bodyAsString]);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
if ([objectLoader wasSentToResourcePath:#"/account/verify.json"]) {
Login * login = (Login *) object;
if ([login.username length] > 0)
[self loginWasSuccessful];
}
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError*)error {
if ([objectLoader wasSentToResourcePath:#"/account/verify.json"]) {
NSLog(#"Encountered an error: %#", error);
// Login failed
if ([self.delegate respondsToSelector:#selector(user:didFailLoginWithError:)]) {
[self.delegate user:self didFailLoginWithError:error];
}
}
}
- (BOOL)isLoggedIn {
return self.username != nil;
//return self.singleAccessToken != nil;
}
- (void)dealloc {
_delegate = nil;
[_password release];
[_passwordConfirmation release];
[super dealloc];
}
#end
The issue is that whenever I tried to access currentUser it always breaks down. I first called the loginWithUsernameandPassword and then tried calling the currentUser, but when I call the currentUser on logout, it gives me an error:
calling this:
if ([[User currentUser] isLoggedIn])
gives me:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[User isLoggedIn]: unrecognized selector sent to class 0x1a671c'
seems that currentUser is nil, why is this?
Quick Singleton 101 (I wish I had this when I started, lol. Everyone just pointed me to the docs which didn't help much). The name of the singleton is going to be "Singleton"
//Singleton.h
#import <Foundation/Foundation.h>
#interface SingletonManager : NSObject
{
NSDictionary* randomDictionary; //just using a dictionary for demonstrative purposes. You can make this a string or whatever you want.
}
+ (Singleton*)sharedSingleton;
#property (nonatomic, retain) NSDictionary *randomDictionary;
#end
And now the .m
//Singleton.m
#import "Singleton.h"
static Singleton *sharedSingleton = nil;
#implementation Singleton
#synthesize randomDictionary;
#pragma mark Singleton Method
+ (Singleton*)sharedSingleton
{
#synchronized(self)
{
if(sharedSingleton == nil)
{
sharedSingleton = [[super allocWithZone:NULL] init];
}
}
return sharedSingleton;
}
#end
And to set/get, first import the singleton in whatever class you need: #import "Singleton.h", then grab the singleton with Singleton *singletonManager = [Singleton sharedSingleton]; and then you can do whatever you need to as necessary. i.e. to get the description of the NSDictionary you would call [[singletonManager randomDictionary] description];
Now this is using ARC, so if you are not you'd just have to make sure you manage your memory correctly. Enjoy.
You need to get the singleton object before you can call a method on it.
if ( [[User currentUser] isLoggedIn] ) {
// Magic happens here
}
You aren't coding your singleton properly.
+ (User *) currentUser {
#synchronized (self) {
if (currentUser == nil) {
currentUser = [[self alloc] init];
}
return currentUser;
}
}
The answer is really a combo of the two answers from XCodeDev and Matthieu Cormier. You need to "protect" your init the way the code sample says so new versions of the object are not created. Otherwise, its not a real singleton. More info on Singleton pattern.
Also, just because its a singleton doesn't mean you can access it with just class methods after you initialize it. You still need to get the instance you initialized, otherwise you cannot do operations that require certain values only in the instance.

Imlementation of Singleton Class

i have been using mostly the appDelegate class for global variables but recently i came to know that its not a gud way for keeping global variables so i am trying to make a singleton class as following
#implementation globalVar
static globalVar *_sharedInstance =nil;
#synthesize totalTime;
- (id) init
{
if (self = [super init])
{
}
return self;
}
+ (globalVar *) sharedInstance
{
#synchronized (self) {
if (_sharedInstance == nil) {
[[self alloc] init];
}
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (_sharedInstance == nil) {
_sharedInstance = [super allocWithZone:zone];
return _sharedInstance;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
-(NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
// do nothing
}
- (id)autorelease
{
return self;
}
- (void) setTotalTime:(NSString *)time
{
#synchronized(self) {
if (totalTime != time) {
[totalTime release];
totalTime = [NSString stringWithFormat:#"%#",time];
}
}
//NSLog(#"time %#",totalTime);
}
-(NSString *)getTotalTime
{
#synchronized(self) {
//NSLog(#"total %#",totalTime);
return totalTime;
}
}
when i set the value for totaltime in my appDelegate class and retrieve it in that class only i get the correct value. but when i only retrieve the value in some other class i get BAD EXCESS. i first create the sharedinstance and then only call this method then why am i getting this error??
globalVar *myEngine = [globalVar sharedInstance];
NSLog(#"about %#",[myEngine totalTime]);
in my app delegate
globalVar *myEngine = [globalVar sharedInstance];
[myEngine setTotalTime:totalTime];
NSLog(#"in app delegate%#",[myEngine getTotalTime]);
You're releasing totalTime but not retaining the new value, which means that when you access it it's already been released, causing a bad access exception.
You can correct this by changing the line where you set the value to include a call to retain:
totalTime = [[NSString stringWithFormat:#"%#",time] retain];
Have a look at the discussion here:
Is it good practice to use AppDelegate for data manipulation and Handling?

how do you make a "concurrent queue safe" lazy loader (singleton manager) in objective-c

I made this class that turns any object into a singleton, but I know that it's not "concurrent queue safe." Could someone please explain to me how to do this, or better yet, show me the code. To be clear I want to know how to use this with operation queues and dispatch queues (NSOperationQueue and Grand Central Dispatch) on iOS.
Thanks in advance,
Rich
EDIT: I had an idea for how to do it. If someone could confirm it for me I'll do it and post the code. The idea is that proxies make queues all on their own. So if I make a mutable proxy (like Apple does in key-value coding/observing) for any object that it's supposed to return, and always return the same proxy for the same object/identifier pair (using the same kind of lazy loading technique as I used to create the singletons), the proxies would automatically queue up the any messages to the singletons, and make it totally thread safe.
IMHO this seems like a lot of work to do, so I don't want to do it if it's not gonna work, or if it's gonna slow my apps down to a crawl.
Here's my non-thread safe code:
RMSingletonCollector.h
//
// RMSingletonCollector.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RMWeakObjectRef.h"
struct RMInitializerData {
// The method may take one argument.
// required
SEL designatedInitializer;
// data to pass to the initializer or nil.
id data;
};
typedef struct RMInitializerData RMInitializerData;
RMInitializerData RMInitializerDataMake(SEL initializer, id data);
#interface NSObject (SingletonCollector)
// Returns the selector and data to pass to it (if the selector takes an argument) for use when initializing the singleton.
// If you override this DO NOT call super.
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier;
#end
#interface RMSingletonCollector : NSObject {
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier;
+ (void)destroyCollection;
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
#end
// ==--==--==--==--==Notifications==--==--==--==--==
extern NSString *const willDestroySingletonCollection;
extern NSString *const willDestroySingletonCollectionObject;
RMSingletonCollector.m
//
// RMSingletonCollector.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMSingletonCollector.h"
#import <objc/objc-runtime.h>
NSString *const willDestroySingletonCollection = #"willDestroySingletonCollection";
NSString *const willDestroySingletonCollectionObject = #"willDestroySingletonCollectionObject";
RMInitializerData RMInitializerDataMake(SEL initializer, id data) {
RMInitializerData newData;
newData.designatedInitializer = initializer;
newData.data = data;
return newData;
}
#implementation NSObject (SingletonCollector)
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier {
return RMInitializerDataMake(#selector(init), nil);
}
#end
#interface RMSingletonCollector ()
+ (NSMutableDictionary *)singletonCollection;
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection;
#end
#implementation RMSingletonCollector
static NSMutableDictionary *singletonCollection = nil;
+ (NSMutableDictionary *)singletonCollection {
if (singletonCollection != nil) {
return singletonCollection;
}
NSMutableDictionary *collection = [[NSMutableDictionary alloc] initWithCapacity:1];
[self setSingletonCollection:collection];
[collection release];
return singletonCollection;
}
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection {
if (newSingletonCollection != singletonCollection) {
[singletonCollection release];
singletonCollection = [newSingletonCollection retain];
}
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
id obj;
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
if (obj = [[self singletonCollection] objectForKey:key]) {
return obj;
}
// dynamic creation.
// get a class for
Class classForName = NSClassFromString(className);
if (classForName) {
obj = objc_msgSend(classForName, #selector(alloc));
// if the initializer takes an argument...
RMInitializerData initializerData = [classForName designatedInitializerForIdentifier:identifier];
if (initializerData.data) {
// pass it.
obj = objc_msgSend(obj, initializerData.designatedInitializer, initializerData.data);
}
else {
obj = objc_msgSend(obj, initializerData.designatedInitializer);
}
[singletonCollection setObject:obj forKey:key];
[obj release];
}
else {
// raise an exception if there is no class for the specified name.
NSException *exception = [NSException exceptionWithName:#"com.RMDev.RMSingletonCollector.failed_to_find_class" reason:[NSString stringWithFormat:#"SingletonCollector couldn't find class for name: %#", [className description]] userInfo:nil];
[exception raise];
[exception release];
}
return obj;
}
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier {
id obj = [self collectionObjectForType:className identifier:identifier];
RMWeakObjectRef *objectRef = [[RMWeakObjectRef alloc] initWithObject:obj identifier:identifier];
return [objectRef autorelease];
}
+ (void)destroyCollection {
NSDictionary *userInfo = [singletonCollection copy];
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollection object:self userInfo:userInfo];
[userInfo release];
// release the collection and set it to nil.
[self setSingletonCollection:nil];
}
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollectionObject object:[singletonCollection objectForKey:key] userInfo:nil];
[singletonCollection removeObjectForKey:key];
}
#end
RMWeakObjectRef.h
//
// RMWeakObjectRef.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
// In order to offset the performance loss from always having to search the dictionary, I made a retainable, weak object reference class.
#import <Foundation/Foundation.h>
#protocol RMWeakObjectReference <NSObject>
#property (nonatomic, assign, readonly) id objectRef;
#property (nonatomic, retain, readonly) NSString *className;
#property (nonatomic, retain, readonly) NSString *objectIdentifier;
#end
#interface RMWeakObjectRef : NSObject <RMWeakObjectReference>
{
id objectRef;
NSString *className;
NSString *objectIdentifier;
}
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier;
- (void)objectWillBeDestroyed:(NSNotification *)notification;
#end
RMWeakObjectRef.m
//
// RMWeakObjectRef.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMWeakObjectRef.h"
#import "RMSingletonCollector.h"
#implementation RMWeakObjectRef
#dynamic objectRef;
#synthesize className, objectIdentifier;
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier {
if (self = [super init]) {
NSString *classNameForObject = NSStringFromClass([object class]);
className = classNameForObject;
objectIdentifier = identifier;
objectRef = object;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollectionObject object:object];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollection object:[RMSingletonCollector class]];
}
return self;
}
- (id)objectRef {
if (objectRef) {
return objectRef;
}
objectRef = [RMSingletonCollector collectionObjectForType:className identifier:objectIdentifier];
return objectRef;
}
- (void)objectWillBeDestroyed:(NSNotification *)notification {
objectRef = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[className release];
[super dealloc];
}
#end
The easiest (and thread safe) acces to singleton:
static SomeClass* shared_instance;
+(SomeClass*) sharedInstance {
#synchronized( shared_instance ) {
if( !shared_instance ) {
shared_instance = [[SomeClass alloc] init];
//some additional initialization here
}
}
return shared_instance;
}
Edit:
I think particular singleton class would be your best choice. Even for lazy loading. E.g. you need to access some array of users all over your app. Then you can create singleton class:
UserManager:
+(UserManager*) sharedManager;
-(NSArray*) allUsers;
-(NSArray*) recentUsers;
-(NSArray*) featuredUsers;
-(void) addUser:(User*) user;
-(void) removeUser:(User*) user;
etc...
Then you will be able to access that arrays in every view controller.
You should create singleton for other types.