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];
Related
I have an app where I'm using a singleton; here is the code in the .h file:
#interface SingletonServicesType : NSObject {
}
#property (nonatomic, retain) NSNumber *globalServicesType;
+ (id)sharedInstance;
#end
Here is the code in the .m file:
//-------------------------------------------
//-- SingletonServicesType
#implementation SingletonServicesType {
}
#synthesize globalServicesType; // rename
//-- sharedInstance --
+ (id)sharedInstance {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
globalServicesType = [[NSNumber alloc] init];
}
return self;
}
#end
Here is the code where I set the initial value of singleton in AppDelegate.m:
// set services
SingletonServicesType *sharedInstance = [SingletonServicesType sharedInstance]; // initialize
if(preferenceData.aServicesType == nil) {
sharedInstance.globalServicesType = 0; // (0) is the default
preferenceData.aServicesType = 0; // here too...
[localContext MR_saveNestedContexts]; // save it...
}
else
sharedInstance.globalServicesType = preferenceData.aServicesType; // 0 = default (nails), 1 = custom
NSLog(#"\n\n1-sharedInstance.globalServicesType: %#", [NSNumber numberWithInt: (sharedInstance.globalServicesType)]); // shows a value of '0'
When I immediately examine the value of the singleton in another class, it's 'null'! Here's that code:
SingletonServicesType *sharedInstance = [SingletonServicesType sharedInstance]; // initialize
NSLog(#"\n\n2-sharedInstance.globalServicesType: %#", sharedInstance.globalServicesType); // shows a value of 'null'
I don't understand why the value is maintaining the setting? Am I missing something?
This is because you are assigning zero to NSNumber*. You need to assign [NSNumber numberWithInt:0] or #0, otherwise the integer is interpreted as an address:
sharedInstance.globalServicesType = #0; // <<== Here
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.
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;
}
How can i make sure user do not call init, instead client should call sharedSingleton to get a shared instance.
#synthesize delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (LoginController *)sharedSingleton
{
static LoginController *sharedSingleton;
#synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[LoginController alloc] init];
CdtMiscRegisterConnectionChangeListenerObjc(test_ConnectionChangeListenerCallback);
return sharedSingleton;
}
}
I've seen it done two ways.
Throw an exception inside init.
Have the object returned by init be your singleton object.
Just to be clear, though, don't do this. It's unnecessary and will make your singletons overly difficult to test and subclass.
edit to add examples
Throw an exception in init
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
+ (instancetype)sharedInstance {
static MySingleton *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
Have init return your singleton
- (instancetype)init {
return [[self class] sharedInstance];
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
+ (instancetype)sharedInstance {
static MySingleton2 *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
Use UNAVAILABLE_ATTRIBUTE abolish init method, and implement initPrivate
+ (instancetype)shareInstance;
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
implement
+ (instancetype)shareInstance {
static MyClass *shareInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[super allocWithZone:NULL] initPrivate];
});
return shareInstance;
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
// MARK: Rewrite
+ (id)allocWithZone:(struct _NSZone *)zone {
return [MyClass shareInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
Short answer: you can't; Objective-C has no concept of private methods.
Check out the answer to this similar question.
You can't make methods private in Objective-C. You could raise a NSException if the wrong initializer is invoked.
- (id)init
{
[NSException exceptionWithName:#"InvalidOperation" reason:#"Cannot invoke init." userInfo:nil];
}
- (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