My question is the following: I have a singleton type object (I'm using ARC) that has this code in the implementation file
+(id)sharedInstance
{
static DataManager *sharedInstance;
if (sharedInstance == nil) {
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
+(NSManagedObjectContext *)getManagedContext
{
AppDelegate *applicationDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
return [applicationDelegate managedObjectContext];
}
+(void)saveContext:(NSManagedObjectContext *)context
{
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
#pragma mark - Data management methods
+(void)addPersonWithName:(NSString *)name andPicture:(UIImage *)picture
{
NSManagedObjectContext *context = [self getManagedContext]; //no problem here
//some code
[self saveContex:context]; // no known class method for selector saveContext:
}
Why is that? The method is declared in the .h file with + ... the getManagedContext model doesn't give this error????
The keyword self inside a method references the owner of the method, which is the instance of the object for instance methods, and the class for class methods. However, the message saveContex is missing a t at the end (saveContext).
dispatch_once singleton
And here is a better singleton idiom compatible with ARC:
+(MySingleton *)sharedInstance {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
});
return shared;
}
Same code as Xcode template
Same code as a Xcode template with placeholders:
+ (<#class#> *)shared<#name#> {
static dispatch_once_t onceToken;
static <#class#> *shared<#name#> = nil;
dispatch_once(&onceToken, ^{
shared<#name#> = <#initializer#>;
});
return shared<#name#>;
}
Same code + disabled alloc/init/new
Want to clue users that they should call sharedInstance instead alloc/init/new? You can disable methods with attribute unavailable. This will cause a compiler error if any of those methods is called on the class.
#import <Foundation/Foundation.h>
#interface MySingleton : NSObject
+(instancetype) sharedInstance;
// clue for improper use (produces compile time error)
+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype) init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype) new __attribute__((unavailable("new not available, call sharedInstance instead")));
#end
#import "MySingleton.h"
#implementation MySingleton
+(instancetype) sharedInstance {
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [[super alloc] initUniqueInstance];
});
return shared;
}
-(instancetype) initUniqueInstance {
return [super init];
}
#end
Warning: dispatch_once is not reentrant
Don't make a recursive call to sharedInstance from inside the dispatch_once block.
If you call dispatch_once from several threads it will act as a barrier preventing concurrent access. But if you call it again in the same thread from inside the block it will deadlock the thread. This example illustrates the problem:
#import <Foundation/Foundation.h>
static NSRecursiveLock *_lock = nil;
// constructor = run before main. used = emit code even if the function is not referenced.
// See http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
static void runBeforeMain(void) __attribute__ ((constructor, used));
static void runBeforeMain(void) {
_lock = [NSRecursiveLock new];
}
static void test(void)
{
static NSUInteger count = 0;
NSLog(#"iteration #%lu", ++count);
// WRONG: deadlock!
//static dispatch_once_t token;
//dispatch_once(&token, ^{
// test();
//});
// OK
[_lock lock];
test();
[_lock unlock];
--count;
}
int main(int argc, char **argv) {
#autoreleasepool {
test();
}
return EXIT_SUCCESS;
}
+initialize singleton
Using +initialize is an alternative idiom to create a singleton. Pros: It is several times faster than dispatch_once. Cons: +initialize is called once per class, so if you subclass the singleton, an instance will be created for each parent class too. Use it only if you know the singleton will not be subclassed.
static id sharedInstance;
+ (void) initialize {
// subclassing would result in an instance per class, probably not what we want
NSAssert([MySingleton class] == self, #"Subclassing is not welcome");
sharedInstance = [[super alloc] initUniqueInstance];
}
+(instancetype) sharedInstance {
return sharedInstance;
}
Related
I have singleton to use websocket connection, when _srWebSocket = nil, will returned new _srWebSocket, but when _srWebSocket connected, will returned nil
#interface SocketManager : NSObject
#property (nonatomic, weak) SRWebSocket *srWebSocket;
+ (SocketManager *) sharedInstance;
#end
#implementation SocketManager
#synthesize srWebSocket = _srWebSocket;
+ (SocketManager *) sharedInstance {
static SocketManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SocketManager alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
NSLog(#"In SocketKeeperSingleton init");
}
return self;
}
- (SRWebSocket *) srWebSocket {
if (!_srWebSocket || _srWebSocket.readyState == SR_CLOSED) {
_srWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://...:8000"]];
[_srWebSocket open];
}
return _srWebSocket;
}
#end
Call singleton
srWebSocket = [SocketManager sharedInstance].srWebSocket;
because you have write 'static SocketManager *sharedInstance = nil;' in shareInstance Method .
when this method call every time they send nil .
+ (SocketManager *) sharedInstance {
static SocketManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SocketManager alloc] init];
});
return sharedInstance;
}
write down this line outside shareInstance method "static SocketManager *sharedInstance = nil;"
You are missing srWebSocket method call,
First way,
Declare one method in singleton.h file
-(void) doInitialSetupOnLaunch;
Add this method somewhere in your singleton.m file,
-(void) doInitialSetupOnLaunch {
//Put your initialized code i mean you can call your method from here & define your properties here.
_srWebSocket = [self srWebSocket];
}
- (SRWebSocket *) srWebSocket {
if (!_srWebSocket || _srWebSocket.readyState == SR_CLOSED) {
_srWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://...:8000"]];
[_srWebSocket open];
}
return _srWebSocket;
}
Call it from somewhere,best way you can call from appDelegate.m
Now you are ready to access it by property,
Call singleton
srWebSocket = [SocketManager sharedInstance].srWebSocket;
Or Second way
SRWebSocket * srWebSocket = [[SocketManager sharedInstance] srWebSocket];
I am very new to Objective C and stumbled upon this problem.
How is it possible to create a global instance of a class in Objective C which is accessible from multiple classes and the main function?
You can do it in this way:
+(MyClass *) sharedInstance
{
static id sharedInstance = nil;
#synchronized(self)
{
if (!sharedInstance)
{
sharedInstance = [[MyClass alloc] init];
}
return sharedInstance;
}
}
Just access the object by this single line of code:
[MyClass sharedInstance]
Hope it helps!! Happy Coding :)
Add a method to your class that returns this specific instance.
The most common case is the singleton. You can also look up "shared instance".
You can use singleton, import the .h file in your .pch file, by doing that every file will have this imported :
YourClass.h :
#interface YourClass : NSObject
/**
* Method to get the singleton instance
* #return Instance of YourClass
*/
+ (YourClass *)instance;
#end
YourClass.m :
#import "YourClass.h"
#interface YourClass(){
}
#end
#pragma mark -
#implementation YourClass
- (id)init{
//prevents normal inits
return nil;
}
+ (YourClass *)instance
{
static YourClass *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] initSingelton];
});
return instance;
}
- (id)initSingelton{
if (self = [super init]) {
//do stuff here
return self;
}
return nil;
}
In Objective-C, how to write a singleton with ARC? In ARC, it is not allowed to overwrite the release, autorelease, retain, retainCount methods, how to avoid a object to be released? I know without ARC, a classic singleton would like below:
#interface SingletonObject
+ (SingletonObject*)sharedObject;
#end
SingletonObject *sharedObj;
#implementation SingletonObject
+ (id)allocWithZone:(NSZone *)zone
{
if (sharedObj == nil) {
//So the code [[SingletonObject alloc] init] is equal with [SingletonObject sharedObject]
sharedObj = [super allocWithZone:zone];
}
return sharedObj;
}
+ (void)initialize
{
if (self == [SingletonObject class]) {
sharedObj = [[SingletonObject alloc] init];
}
}
+ (SingletonObject*)sharedObject
{
return sharedObj;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (oneway void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
- (id)init {
self = [super init];
if (self) {
//...
}
return self;
}
#end
Is it safe to just remove the retain, retainCount, release, autorelease methods? Thanks!
You only need one method to implement a class that supports the singleton pattern:
+ (instancetype)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
Copy/paste that into any class and it'll have a shared instance. Any code beyond that is just added complexity that really isn't necessary. I'll sometimes add:
- (void)dealloc
{
*(char*)0x42 = 'b';
// no super, ARC all the way
}
That'll cause a very specific crash if my shared instance is ever deallocated due to a bug. (Yes, hex 0x42 is not 42, but it leaves a nice 0x000000042 in a register in the crash log, making it immediately identifiable what happened.)
Yes.
-sharedObject should be +sharedObject, though (class method). Just make sure you always use that.
My question is quite similar to this one: Use Singleton In Interface Builder?
The only difference is that I use ARC. So, if simplified, my singleton looks like that:
Manager.m
#implementation Manager
+ (instancetype)sharedManager {
__strong static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#end
So the question is if it's possible to adopt it for Interface Builder still being with ARC?
Of course, I understand that it might be simpler just to rewrite that class without ARC so the question is rather academic. :)
When the nib is unarchived, it'll attempt to either alloc/init or alloc/initWithCoder: a new instance of the class.
So, what you could do is intercept that call and re-route it to return your singleton:
+ (id)sharedInstance {
static Singleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self actualAlloc] actualInit];
});
return sharedInstance;
}
+ (id)actualAlloc {
return [super alloc];
}
+ (id)alloc {
return [Singleton sharedInstance];
}
- (id)actualInit {
self = [super init];
if (self) {
// singleton setup...
}
return self;
}
- (id)init {
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
return self;
}
This allows -init and -initWithCoder: to be safely called multiple times on the same object. It's generally not recommended to allow this, but given that singletons are already cases of "a place where things can get really wonky", this isn't the worst you could do.
Just to be complete, here's an implementation of Singleton which might be used from Interface Builder. The difference is in actualAlloc method. As [super alloc] would still call [self allocWithZone:] – it wouldn't allocate the object.
Singleton.h
#interface Singleton : NSObject
+ (instancetype)sharedInstance;
#end
Singleton.m
#implementation Singleton
+ (instancetype)sharedInstance {
__strong static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self _alloc] _init];
});
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
+ (id)alloc {
return [self sharedInstance];
}
- (id)init {
return self;
}
+ (id)_alloc {
return [super allocWithZone:NULL]; //this is important, because otherwise the object wouldn't be allocated
}
- (id)_init {
return [super init];
}
#end
#Eugene, from iOS doc set, "For historical reasons, alloc invokes allocWithZone:.", so, there is no need to reimplement the alloc method.
- (BOOL)coolMethod:(NSString*)str
{
//do some stuff
Webservice *ws = [[WebService alloc] init];
NSString *result = [ws startSynchronous:url];
if ([result isEqual:#"Something"])
{
//More calculation
return YES;
}
return NO;
}
I am using OCUnit
In the following method how can i mock my WebService Object, or the result to the method "startSynchronous" to be able to write an independent unit test?
Is it possible to inject some code in there to either create a mock web service or return a mock data on startSynchronous call?
One way is to use categories and override methods you want, you can even override the init method to return a mock object:
#interface Webservice (Mock)
- (id)init;
#end
#implementation Webservice (Mock)
- (id)init
{
//WebServiceMock is a subclass of WebService
WebServiceMock *moc = [[WebServiceMock alloc] init];
return (Webservice*)moc;
}
#end
The problem with this is that if you want to make the object return different results in different tests in 1 test file you cannot do that. (You can override each method once per test page)
EDIT:
This is an old question I posted, I thought I would update the answer to how I write testable code and unit test it nowadays :)
ViewController Code
#implementation MyViewController
#synthesize webService;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webService sendSomeMessage:#"Some_Message"];
}
- (WebService *)webService
{
if (!_webService)
_webService = [[WebService alloc] init];
return _webService;
}
#end
Test Code
#implementation MyViewControllerTest
- (void)testCorrectMessageIsSentToServer
{
MyViewController *vc = [[MyViewController alloc] init];
vc.webService = [OCMock niceMockForClass:[WebService class]];
[[(OCMockObject *)vc.webService expect] sendSomeMessage#"Some_Message"];
[vc view]; /* triggers viewDidLoad */
[[(OCMockObject *)vc.webService verify];
}
#end
Building on top of the WebService answer from aryaxt, here's a little trick to be able to get different results in different test.
First, you need a singleton object which will be used to store the desired answer, right before the test
TestConfiguration.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
void MethodSwizzle(Class c, SEL orig, SEL new);
#interface TestConfiguration : NSObject
#property(nonatomic,strong) NSMutableDictionary *results;
+ (TestConfiguration *)sharedInstance;
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector;
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector;
#end
TestConfiguration.m
#import "TestConfiguration.h"
void MethodSwizzle(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
#implementation TestConfiguration
- (id)init
{
self = [super init];
if (self) {
self.results = [[NSMutableDictionary alloc] init];
}
return self;
}
+ (TestConfiguration *)sharedInstance
{
static TestConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[TestConfiguration alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
[self.results setObject:result
forKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
return [self.results objectForKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
#end
Then you would define your "Mock" category to define mock methods , such as :
#import "MyWebService+Mock.h"
#import "TestConfiguration.h"
#implementation MyWebService (Mock)
-(void)mockFetchEntityWithId:(NSNumber *)entityId
success:(void (^)(Entity *entity))success
failure:(void (^)(NSError *error))failure
{
Entity *response = (Entity *)[[TestConfiguration sharedInstance] getResultForCallToObject:self selector:#selector(fetchEntityWithId:success:failure:)];
if (response == nil)
{
failure([NSError errorWithDomain:#"entity not found" code:1 userInfo:nil]);
}
else{
success(response);
}
}
#end
And finally, in the tests themselves, you would swizzle the mock method in the setup , and define the expected answer in each test, before the call
MyServiceTest.m
- (void)setUp
{
[super setUp];
//swizzle webservice method call to mock object call
MethodSwizzle([MyWebService class], #selector(fetchEntityWithId:success:failure:), #selector(mockFetchEntityWithId:success:failure:));
}
- (void)testWSMockedEntity
{
/* mock an entity response from the server */
[[TestConfiguration sharedInstance] setNextResult:[Entity entityWithId:1]
forCallToObject:[MyWebService sharedInstance]
selector:#selector(fetchEntityWithId:success:failure:)];
// now perform the call. You should be able to call STAssert in the blocks directly, since the success/error block should now be called completely synchronously.
}
Remark : in my example, the TestConfiguration uses class/selector as a key instead of object/selector. That means every object of the class will use the same answer for the selector. That is most likely your case, as webservice are often singleton. But it should be improved to an object/selector maybe using the objet's memory address instead of its class