Get instance count of a specific class in Objective-C/IOS - ios

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

Related

why this UIViewController method swizzling doesn't work?

I'm trying out a code example of The NSHipster Fake Book to swizzle the viewWillAppear: method of UIViewController. But It seems that it doesn't work expectedly since the xxx_viewWillAppear: method has never been invoked. I just cannot find out why. Please help me, thanks.
#import "ViewController.h"
#import <objc/runtime.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#end
#implementation UIViewController (Tracking)
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = object_getClass(self);
SEL originalSelector = #selector(viewWillAppear:);
SEL swizzledSelector = #selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(cls, originalSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
BOOL addMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (addMethod) {
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else{
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
-(void)xxx_viewWillAppear:(BOOL)animated
{
[self xxx_viewWillAppear:animated];
NSLog(#"viewWillAppear: %#",self);
}
#end
Since load is a class method, the self in Class cls = object_getClass(self); refers to the meta class of UIViewController, not the actual class you want (UIViewController).
If you change it to Class cls = [self class];, then cls will be the UIViewController class itself and it should work.

Singleton in iOS Objective C doesn't prevent more than one instance

I know there are several threads on this, but none answer my questions.
I've implemented my singleton class like this (being aware of the controversy about singletons):
+ (MyClass*) sharedInstance {
static MyClass *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[MyClass alloc] init];
});
return _sharedInstance;
}
- (instancetype)init{
self = [super init];
if (self) {
//setup code
}
return self;
}
I tried instantiating a different object and compared it to the one returned by sharedInstance with '==' and they were indeed different.
Questions:
Shouldn't creating more than one object of the singleton class be impossible? Isn't that the point? Singleton implementation in Java prevents it.
And if so, how? Should I make a setup method and call it instead of having the init implemented and doing it?
Is this correct implementation?
Your observation is correct, many of the "singleton" patterns you see in Objective-C are not singletons at all but rather a "shared instance" model where other instances can be created.
In the old MRC days Apple used to have sample code showing how to implement a true singleton.
The code you have is the recommended pattern for ARC and thread-safe singletons, you just need to place it in the init method:
- (instancetype) init
{
static MyClass *initedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
initedObject = [super init];
});
return initedObject;
}
This code will ensure that there is only ever one instance of MyClass regardless of how many [MyClass new] or [[MyClass alloc] init] calls are made.
That is all you need to do, but you can go further. First if you wish to have a class method to return the singleton it is simply:
+ (instancetype) singletonInstance
{
return [self new];
}
This method ends up calling init which returns the singleton, creating it if needed.
If MyClass implements NSCopying then you also need to implement copyWithZone: - which is the method which copy calls. As you've a singleton this is really simple:
- (instancetype) copyWithZone:(NSZone *)zone
{
return self;
}
Finally in Objective-C the operations of allocating a new object instance and initialising it are distinct. The above scheme ensures only one instance of MyClass is initialised and used, however for every call to new or alloc another instance is allocated and then promptly discarded by init and cleaned up by ARC. This is somewhat wasteful!
This is easily addressed by implementing allocWithZone: (like copy above this is the method alloc actually ends up calling) following the same pattern as for init:
+ (instancetype) allocWithZone:(NSZone *)zone
{
static MyClass *allocatedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
allocatedObject = [super allocWithZone:zone];
});
return allocatedObject;
}
The first time an instance is created then allocWithZone: will allocate it and then init will initialise it, all subsequent calls will return the already existing object. No discarded unneeded allocations.
That's it, a true singleton, and no harder than the faux-singletons that are so common.
HTH
You can't make the init method private, like you would do in Java with the constructor. So nothing stops you from calling [[MyClass alloc] init] which indeed creates a different object. As long as you don't do that, but stick to the sharedInstance method, your implementation is fine.
What you could do: have the init method raise an exception (e.g. with [self doesNotRecognizeSelector:#_cmd]) and perform the initialization in a different method (e.g. privateInit) which is not exposed in the header file.
With objective-c, you can prevent your singleton class to create more than one object. You can prevent alloc and init call with your singleton class.
#import <Foundation/Foundation.h>
#interface SingletonClass : NSObject
+ (id) sharedInstance;
- (void) someMethodCall;
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
#end
#import "SingletonClass.h"
#implementation SingletonClass
+ (id) sharedInstance{
static SingletonClass * sharedObject = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObject = [[self alloc] initPrivate];
});
return sharedObject;
}
- (instancetype)init {
#throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:#"You can't override the init call in class %#", NSStringFromClass([self class])] userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
}
return self;
}
- (void) someMethodCall{
NSLog(#"Method Call");
}
#end
1# If you will try to call init or new methods on SingletonClass, then these methods would not be available to call.
2# If you comment out mentioned below methods in header file and try to call the init on SingletonClass method then app will be crashed with reason "You can't override the init call in class SingletonClass".
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
Just use this code to create single object to Singleton Pattern and prevent alloc init call for singleton pattern from other classes. I had tested this code with xCode 7.0+ and its working fine.
To prevent creating multiple objects of single class, you need to do following things. you just fine to created singleton object. but while calling init, copy, mutable copy, you need to handle such way.
- (instancetype)init{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
- (id)copy{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
the same things for mutable copy as well. so this implementation make sure that once one instance is available throughout..
May this help you.
#VeryPoliteNerd just mark the init and new methods as unavailable on the .h:
- (instancetype)init __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
+ (instancetype)new __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
This will cause the compiler to complain if a caller tries to manually instantiate this objects
This works for me:
static AudioRecordingGraph * __strong sharedInstance;
+(instancetype)sharedInstance {
#synchronized(self) {
if(!sharedInstance) {
sharedInstance = [AudioRecordingGraph new];
}
return sharedInstance;
}
}
I wanted to add observation for a modern implementation of Objective-C singletons for optimal Swift interoperability. But let me start with the code. Let’s assume for a second that the class was to manage a series of requests, so I might call it a RequestManager:
// RequestManager.h
NS_ASSUME_NONNULL_BEGIN
#interface RequestManager : NSObject
#property (nonatomic, strong, readonly, class) RequestManager *sharedManager;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
- (void)someMethod;
#end
NS_ASSUME_NONNULL_END
And
// RequestManager.m
#implementation RequestManager
+ (instancetype)sharedManager {
static RequestManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
//setup code
}
return self;
}
- (void)someMethod { ... }
#end
Note:
Note, I made the singleton a class property rather than a class method.
From the Objective-C side, it is a distinction without difference, but from Swift, you can do things like MyClass.shared without the noise of the (). This is the pattern that Apple has adopted with all of their singletons.
One might give the singleton a more meaningful name. For example, if the class was a RequestManager, the singleton might be called sharedManager rather than sharedInstance.
If you do this, Swift will automatically detect the salient portion of the instance name and will expose it to Swift as shared, not sharedInstance or whatever.
If you really want to use the name sharedInstance in your Objective-C code, then will just need to supply an explicit NS_SWIFT_NAME of shared to adopt the nice, concise, and consistent singleton naming practice in Swift:
#property (nonatomic, strong, readonly, class) RequestManager *sharedInstance NS_SWIFT_NAME(shared);
Note the use of NS_UNAVAILABLE. This prevents callers from accidentally instantiating their own copies. E.g. from Objective-C:
Or from Swift:
Probably needless to say, I’ve audited this for nullability, namely using the NS_ASSUME_NONNULL_BEGIN/END to make the default non-null. Or, obviously, you could just mark the singleton property as nonnull.
Calling alloc / init to get a second instance of a Singleton class is considered a blatant programming error. To avoid this kind of programming error, you don't write complicated code to prevent it, you do code reviews and tell off everyone trying to do it, as you would with any programming error.
This works for me :
static DataModel *singleInstance;
+ (DataModel*)getInstance{
if (singleInstance == nil) {
singleInstance = [[super alloc] init];
}
return singleInstance;
}
You can call it with
_model = [DataModel getInstance];

Singleton not persisting across viewcontrollers/spritekit scene

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.

How to make a single shared instance of iAd banner throughout many view controllers?

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

Can my singleton be improved?

I am somewhat new to iOS 5 singletons and am using a singleton as documented here:
iOS 5 Singletons
Something like this:
MyManager.h
#import <Foundation/Foundation.h>
#interface MyManager : NSObject
//Data for section 1
#property(nonatomic,copy) NSString * section1a;
#property(nonatomic, assign) NSUInteger section1b;
//Data for section 2
#property(nonatomic,copy) NSString * section2a;
#property(nonatomic, assign) NSUInteger section2b;
+ (id)sharedInstance;
#end
MyManager.m
#implementation MyManager
#synthesize section1a, section1b, section2a; , section2b;
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
#end
So I use it as follows:
MyManager * myManager = [MyManager sharedInstance];
myManager.data = self.data
Is this how you generally use the singleton? Am I missing anything?
Sorry for the basic questions I just want to make sure I am doing it right.
Thanks
You're doing it right. This is (currently :) the way to go and works fine with ARC enabled, multiple threads etc.
It isn't strictly a singleton, as you can alloc more instances of the class, but I don't think that's relevant in your case.
This is not a singleton because you can create multiple instances of this class with alloc/init methods.
A right answer is here
I usually make my singleton as such (suppose we are making a singleton for Manager):
static (Manager *) instance;
+ (Manager *) getInstance
{
if(!instance) instance = [[Manager alloc] init];
return instance;
}
This is a very basic singleton (it doesn't take into account multithreading or other advanced features) but this is the basic format.
Refer to this: Singleton in iOS 5? You should override allowWithZone to prevent rogue code from creating a new instance.

Resources