This is a SpriteKit game.
I am using a singleton to store a single 'Show' object to be accessible throughout the game. The 'Show' class has an NSString property 'showTitle'.
In ViewController1, I set the 'Show' property of the singleton. To test.. I then printed out a string property of 'Show' (showTitle) from the singleton and it prints the string correctly.
After segueing to ViewController2, I again print out the same string property of 'Show' (showTitle) from the singleton and it again prints the string correctly.
THEN, the spritekit scene is initialized from ViewController2. I attempt to print the same string property
of 'Show' from the singleton, and it prints null instead of the string. I went further and segued to ViewController3, tried to print the showTitle from the singleton..... NULL.
Why am I able to access the 'Show' property of the singleton in ViewControllers 1 & 2, but not from within the sprite kit scene or ViewController3. Where am I going wrong?
ShowSingleton.h
#import <Foundation/Foundation.h>
#import "Show.h"
#interface ShowSingleton : NSObject
#property (nonatomic, strong) Show* currentShow;
+(ShowSingleton *)singleton;
#end
ShowSingleton.m
#import "ShowSingleton.h"
#implementation ShowSingleton
#synthesize currentShow;
+(ShowSingleton *)singleton {
static dispatch_once_t pred;
static ShowSingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[ShowSingleton alloc] init];
});
return shared; }
#end
ViewController1:
- (IBAction)openShow:(UIButton*)sender
{
//showsarray is an array of 'Show' objects retrieved through core data in another class
[ShowSingleton singleton].currentShow = [showsarray objectAtIndex:sender.tag];
NSLog(#"Opened show: %#", [ShowSingleton singleton].currentShow.showTitle);
//The above line correctly prints the selected showTitle from the singleton
}
After openShow: completes, a segue opens ViewController2. ViewController2 initializes the SpriteKit scene.
ViewController2:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene)
{
skView.showsFPS = YES;
skView.showsNodeCount = YES;
SKScene * scene = [iMarchMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
}
- (void)viewDidLoad
{
//The following line correctly prints the showTitle from ViewController2
NSLog(#"processing show: %#", [ShowSingleton singleton].currentShow.showTitle);
}
myScene.m:
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
if ([ShowSingleton singleton].currentShow)
{
//This always gets called, which tells me the object exists, but null is printed for showTitle
NSLog(#"show title from scene: %#", [ShowSingleton singleton].currentShow.showTitle);
}
}
return self;
}
Consider using this singleton implementation:
// ShowSingleton.h
#import <Foundation/Foundation.h>
#import "Show.h"
#interface ShowSingleton : NSObject
+ (instancetype)singleton;
#end
// ShowSingleton.m
#import "ShowSingleton.h"
#implementation ShowSingleton
+ (instancetype)singleton {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#end
ShowSingleton *shared is scoped to the method, not the class.
Try declaring it as a class property of your AppDelegate, and then override the getter with something like this:
#property (strong, nonatomic) Show *currentShow;
and then override the getter as:
+(Show*)currentShow
{
static id _currentShow = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
currentShow = [showsarray objectAtIndex:sender.tag]; //probably not this, not sure where in your app flow this info is ready...
});
return _currentShow;
}
Now you can leverage the proper singleton UIApplicationDelegate that Apple provided, and end up with an unchanging instance of Show that is accessible anywhere in your app by calling (YourApplicationClass*)[[UIApplication sharedApplication] currentShow]
Keep in mind that dispatch_once is tied to the App Lifecycle. It will be purged when and only when the app terminates, which may be while your app is in the background.
Singleton's are fiddly to implement correctly, and even more difficult to know when their use is actually warranted, so you may want to take a look at NSUserDefaults to see if it has something you could bend to your purposes instead.
Related
For QA purposes I want to ensure that instances of specific classes have been deallocated properly (so the actual count of instances is sufficient). I looked into the Objective-C runtime reference, but I could not find the appropriate function. I have checked similar questions but did not find a satisfying answer.
Edit
I took TheCodingArt's mockup and completed it, the result can be obtained at
https://www.generomobile.de/gmi/allocchecker.m
The difficulty was to swizzle dealloc because ARC forbids passing the selector of dealloc for swizzling. I stumbled across this interesting swizzling tutorial at http://defagos.github.io/yet_another_article_about_method_swizzling/
NSString and other class clusters obviously are not freed by dealloc as one can see in the sample. But for my own classes it works our current IOS project and gives some interesting insights.
One solution is to setup a static counter in your .m file. Increment the counter in the designated init method and decrement the counter in the dealloc method. Provide a class method to read the count value.
Don't do this as a rule. This should only be done for testing.
Lets say you want to track the instance count of SomeClass. You can do:
SomeClass.h
#interface SomeClass : NSObject
+ (NSInteger)instanceCount;
// everything else you need
#end
SomeClass.m
#import "SomeClass.h"
static NSInteger instanceCount = 0;
#implementation SomeClass
- (instancetype)init {
self = [super init];
if (self) {
instanceCount++;
// everything else you need
}
return self;
}
// all your other code
+ (NSInteger)instanceCount {
return instanceCount;
}
- (void)dealloc {
instanceCount--;
}
#end
Per request, I've mocked up a category that will keep count of allocated objects using method swizzling and a singleton. This was a quick mock up, so there are a few issues with it (one being that initializing anything that is contained within the storage class will cause an infinite loop). Mind you, this is for keeping track of many objects and should not be used in a production environment. The best methodology overall is to use the instruments tool.
#import "NSObject+Initializer.h"
#import <objc/runtime.h>
#interface ObjectCounter : NSObject
+ (instancetype)sharedObjectCounter;
#property (strong, nonatomic) NSMutableDictionary *objectCounterDictionary;
#end
#implementation ObjectCounter
+ (instancetype)sharedObjectCounter
{
static ObjectCounter *objectCounter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
objectCounter = [ObjectCounter new];
objectCounter.objectCounterDictionary = [NSMutableDictionary new];
});
return objectCounter;
}
#end
#implementation NSObject (Initializer)
+ (NSNumber *)initializedCount
{
NSLog(#"Dict: %#", [ObjectCounter sharedObjectCounter].objectCounterDictionary);
return [ObjectCounter sharedObjectCounter].objectCounterDictionary[NSStringFromClass([self class])];
}
+ (id)alloc_swizzled
{
NSLog(#"Swizzled");
NSString *className = NSStringFromClass([self class]);
if (![className isEqualToString:NSStringFromClass([NSMutableDictionary class])] && ![className isEqualToString:NSStringFromClass([ObjectCounter class])]) {
ObjectCounter *counter = [ObjectCounter sharedObjectCounter];
NSMutableDictionary *objectDictionary = counter.objectCounterDictionary;
NSNumber *count = objectDictionary[className];
count = count ? #(count.integerValue + 1) : #0;
objectDictionary[className] = count;
}
return [self alloc_swizzled];
}
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = #selector(alloc);
SEL swizzledSelector = #selector(alloc_swizzled);
Method originalMethod = class_getClassMethod(class, originalSelector);
Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
You can get a count without subclassing by adding a category and overriding alloc, This is really hacky but it's fine for testing and good for classes you don't own. I have no idea if this would work without ARC.
#implementation UILabel (LableCounter)
static NSInteger labelCount = 0;
+ (id)alloc
{
labelCount++;
return [super alloc];
}
-(void)dealloc{
labelCount--;
}
#end
ClassA.h
...
#property (weak, nonatomic) NSString *myVariable;
- (id) setMyVariable:(NSString *)string;
- (id) getMyVariable;
ClassA.m
...
#synthezise myVariable = _myVariable;
... some inits
- (id) setMyVariable:(NSString *)string {
_myVariable = string;
NSLog(#"here nslog success return new value: ", _myVariable);
return _myVariable;
}
- (id) getMyVariable {
NSLog(#"here nslog return nil", _myVariable);
return _myVariable;
}
ClassB.m
#import ClassA.h
...
ClassA *classA = [[ClassA alloc] init];
[classA setMyVariable:#"some"];
ClassC.m
#import ClassA.h
...
ClassA *classA = [[ClassA alloc] init];
NSLog(#"here nslog returns nil: #%", [classA getMyVariable]);
Why does [ClassC getMyVariable] return nil? Same result when I try to set value directly without setter and getter. I already read other topics on StackOverflow and Google, but have not idea why it doesn't work.
Your whole code is a bit of a mess really. Why are you using a weak property? Why are you using a #synthezise since this is is automatically done by xcode for you along with the getters and setters so you don't need to create them ever.
The reason why your [classA getMyVariable]; is nil in ClassC is because you create a new instance of it on the line above. By the looks of what you are trying to do is you want to set the variable for instance of a class in one class and access that variable on the same instance in a different class. So one method of doing this is to use a singleton, these are sometimes not liked but I think they work well and don't see a reason why some (not all) developers don't like them.
So lets do some cleaning up and try implementing a singleton
ClassA.h
#interface ClassA : NSObject
#property (nonatomic, strong) NSString *myVariable;
// No need for you to create any getters or setters.
// This is the method we will call to get the shared instance of the class.
+ (id)sharedInstance;
#end
ClassA.m
#import "ClassA.h"
#implementation ClassA
// No need to add a #synthezise as this is automatically done by xcode for you.
+ (id)sharedInstance
{
static ClassA *sharedClassA = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// If there isn't already an instance created then alloc init one.
sharedClassA = [[self alloc] init];
});
// Return the sharedInstance of our class.
return sharedClassA;
}
#end
Right so we have cleaned our ClassA code up and added a method for getting a shared instance of ClassA so now to ClassB
ClassB.m
// Other code in ClassB
// Get the shared instance
ClassA *classA = [ClassA sharedInstance];
// Set the value to the property on our instance.
[classA setMyVariable:#"Some String Value"];
//........
Now that ClassB has set the variable we can go to ClassC now and look at it.
// Other code in ClassC
// We still need to have an instance of classA but we are getting the sharedInstance
// and not creating a new one.
ClassA *classA = [ClassA sharedInstance];
NSLog(#"My variable on my shared instance = %#", [classA myVariable]);
//........
Might help if you read this and this for help on understanding different design patterns
because you don't set a value after creating an object. i should be like this:
ClassA *classA = [ClassA alloc] init];
[classA setMyVariable:#"some"];
NSLog(#"not nil anymore: #%", [classA getMyVariable]);
BTW: the #property tag provides two keywords to set getter and setter methods.
#property (weak, nonatomic, getter=myVariable, setter=setMyVariable:) NSString *myVariable;
and apple avoids the word "get" in getter-methods...
I don't know if I'm doing this properly. I'm working on a large application in which a user must log in and and interact with a variety of functionality and data. There are many view controllers that need to have access to this user object.
The following snippet is the moment when the user logs in and now I have a user object to use across my app. Just in this case I'm using dummy data.
User *user = [User new];
[user setupTempOfflineData];
self.newViewController.user = user;
[self containerAddChildViewController:self.newViewController];
In the newViewController is the property:
#property (nonatomic, strong) User *user;
Now NewViewController may have many children and those children have view controllers of their own. All of them given a strong reference to the user. Additional information such as a list of registered groups or content that the user had created remains as well. And sometimes I'll either access downloaded information via the user object, or just store and share references to the arrays/data themselves.
Something in my head is telling me I should be using a singleton or some other design pattern I'm just not familiar with. Thus bringing me here to ask the question:
Am I doing this right?
Edit: Informative link on KVO
You can use singleton pattern on User class.
e.g.
User.h file
#interface User : NSObject
+ (User*)currentUser;
//...
// Some properties
//...
//...
// Some methods
//...
#end
User.m file
//...
+ (User*)currentUser
{
static User *user = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
user = [[self alloc] init];
});
return user;
}
//...
Import "User.h" file in your .pch file.
Now you can access to your user object by calling
[User currentUser];
I'm using the Singleton pattern in my applications, and use lazy instantiation to load that user.
So in your newViewController.m
/**
* lazy instantiation of User
*/
- (User *) user {
if (!_user){
_user = [User getUser];
}
return _user;
}
in User.h
#interface User : NSObject
/**
* Singleton
*/
+ (User *) getUser;
#end
And finally in your User.m
#import "User.h"
/*
* Singleton
*/
static User *singletonUser = nil;
#implementation User
/**
* Designated initializer
*/
- (id) init {
self = [super init];
if (self != nil) {
// Load your user from storage / CoreData / etc.
[self setupTempOfflineData];
}
return self;
}
/**
* Singleton
*/
+ (User *) getUser {
if (singletonUser == nil) {
singletonUser = [[self alloc] init];
}
return singletonUser;
}
#end
So now you can use self.user in your NewViewController.
What you're doing should work. Also have you thought about protocol and delegate? You may want to consider this design pattern in the event that you want the NewViewController (or other view controllers) to be notified when the User object has changed (KVO/Notificaiton are another design patterns).
User.h
#class User;
#protocol userProtocol <NSObject>
-(void) userObjectDidGetUpdated:(User*) u;
#end
#interface User:NSObject {}
#property (nonatomic,weak) id <userProtocol> delegate; // use weak here to prevent reference cycle
#end
User.m -- call notifyDelegatesUserObjectHasChanged when you want to notify the delegates to get the updated User object
#implementation
#synthesize delegate;
-(void) notifyDelegatesUserObjectHasChanged {
[[self delegate] userObjectDidGetUpdated:self];
}
#end
Now, you can register the view controller to get the updated User object as follow...
NewViewController.h
#import "User.h"
#interface NewViewController:UIViewController <userProtocol> {}
NewViewController.m
#implementation
-(void) userObjectDidGetUpdated:(User*) u {
// this callback method will get called when the User object changes
}
#end
I know that this question has been already asked many times.
I use Socket.IO client for Xcode (https://github.com/pkyeck/socket.IO-objc). And problem is that I need socketIO connection in many view controllers.
In ViewControllerA (.h file) I have this:
#import "SocketIO.h"
#inteface ViewControllerA : UIViewController <SocketIODelegate> {
SocketIO *socketIO;
}
(.m file):
- (void) viewDidLoad {
...
[socketIO connectToHot:#"localhost" onPort:1234];
}
And ViewControllerB is made for chat room, I need to get this socketIO object to use for communicate with server. Am I right that I need to do this things?
1) In ViewControllerB make the same as in ViewControllerA
#import "SocketIO.h"
#inteface ViewControllerA : UIViewController <SocketIODelegate> {
SocketIO *socketIO;
}
2) In ViewControllerA before seque make this:
#import "viewControllerB.h"
...
viewControllerB *viewControllerB = (viewControllerB *)[storyboard instantiateViewControllerWithIdentifier:#"viewControllerB"];
viewControllerB.socketIO = socketIO;
[self pushViewController:viewControllerB animated:YES];
Or maybe there are some easy methods to access data from many view controllers?
Even though some people don't like Singletons, I find them very useful in these types of situation, where there is no obvious "owner" of a piece of data. A Singleton is an object that will only exist as one and only one instance. It's accessed through a static method in the singleton class itself:
SocketKeeperSingleton.h
#interface SocketKeeperSingleton : NSObject
#property (nonatomic, strong) SocketIO *socketIO;
+ (SocketKeeperSingleton *) sharedInstance;
#end
SocketKeeperSingleton.m
#import "SocketKeeperSingleton.h"
#implementation SocketKeeperSingleton
// The synthesize will automatically generate a getter and setter
#synthesize socketIO = _socketIO;
+ (SocketKeeperSingleton *)sharedInstance {
static SocketKeeperSingleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
NSLog(#"In SocketKeeperSingleton init");
}
return self;
}
// Override the getter for your socket
- (SocketIO *)socketIO {
if (!_socketIO || check-to-see-if-your-socket-has-been-disconnected-or-failed) {
_socketIO = code-to-create-and-connect-your-socket-goes-here
}
return _socketIO;
}
#end
Then, get the socket from anywhere in your project, like this:
SocketIO *mySocketIO = [SocketKeeperSingleton sharedInstance].socketIO;
Good luck!
I have tried using a singleton class in my app delegate but I haven't been able to get that to work. I've also checked out the iAdSuite examples (particularly the containerBanner example because it seemed to be the most relative) but I can't figure it out. If there's a better way to accomplish this without using a singleton class and you can point me in the right direction I'd really appreciate it. Some of my singleton class code is below. Thank you!
#interface App Delegate
#property (assign) iAdController *iadc;
+ (AppDelegate*) sharedApplication;
- (iAdController*)sharedAd;
#end
#implementation AppDelegate
#synthesize iadc;
+ (AppDelegate*) sharedApplication
{
return [[UIApplication sharedApplication] delegate];
}
-(iAdController*)sharedAd
{
if(iadc==nil){
iadc=[iAdController new];
}
return iadc;
}
#interface ViewController
iAdController*iadc=[[AppDelegate sharedApplication] sharedAd];
//here i get an error saying, "initializer element is not a compile-time constant.
Everything is imported correctly. If there's anything else I should post let me know.
try changing your singleton creation to this:
+ (LocationManagerSingleton*)sharedInstance {
static LocationManagerSingleton *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// PERFORM any custom initialization here
}
return self;
}
Obviously change the name of the class.
Whenever you want to use your singleton in any of your viewcontrollers just call it like this:
locationManager = [LocationManagerSingleton sharedInstance];
Dont forget to add
+ (LocationManagerSingleton*) sharedInstance;
on the header.
EDIT
well it seems i misunderstood your code (forget my answer, you simply want to be able to access your iAdController from everywhere. so just place
Add inside the .m of the ViewController
#interface ViewController()
{
iAdController *iadc;
}
And inside the
-(void)viewDidLoad
{
iadc=[[AppDelegate sharedApplication] sharedAd];
}
but import the app delegate.h on whichever viewcontroller you want to use it in.
#import "AppDelegate.h"
also there shouldnt be a space in the AppDelegate on the #interface