I need some help.
I'm using this singleton pattern within an iOS application I'm developing:
.h file
#import <Foundation/Foundation.h>
#class Item;
#interface ItemManager : NSObject
- (id)init;
+ (ItemManager *)sharedInstance;
- (int)ratingFromObjectName:(NSString *)objectName;
#property(nonatomic,strong) NSArray *itemArray;
#end
.m file
static ItemManager *sharedInstance = nil;
+ (ItemManager *)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ItemManager alloc] init];
});
return sharedInstance;
}
- (int)ratingFromObjectName:(NSString *)objectName {
for (int i = 0; i < itemArray.count; i++) {
if ([[[itemArray objectAtIndex:i] itemName] isEqualToString:objectName]) { //This is the line that throws bad access code 1
NSLog(#"Found: %# Returned: %d", [[itemArray objectAtIndex:i] ratingAverage],
[[[itemArray objectAtIndex:i] ratingAverage] intValue]);
return [[[itemArray objectAtIndex:i] ratingAverage] intValue];
}
}
return 0;
}
I get bad access when I use this in another class:
int rating = [[ItemManager sharedInstance] ratingFromObjectName:bla];
The bla object being sent is a NSString that is definitely working, it is 100% not the issue, as I have tested this. Removing the sharedInstance method and creating an array every time seems to work, however my attempt for this singleton is to avoid that, any suggestions would be greatly appreciated.
Please note that I have commented on the line returning the error.
Regards, WA
You need to work out which line of code is throwing the bad access. Is it the sharedInstance method or ratingFromObjectName:. I would first change the calling code to
ItemManager *manager = [ItemManager sharedInstance];
int rating = [manager ratingFromObjectName:bla];
As that will help with isolating the problem.
Secondly, I would also consider not using the Singleton pattern unless really necessary is iOS apps. It's been my experience that it is often overused (and in the Java world), and whilst convenient, can make writing unit tests more complicated.
Related
I have a NSString that should be constant in my class. I used the following code to accomplish this:
#interface DefinitionViewController ()
#end
static NSString *preamble;
#implementation DefinitionViewController {
}
+(void)initialize {
if (self == [DefinitionViewController class]) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"preamble" ofType:#"html"];
preamble = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding
error:nil];
}
}
It seems to work fine. I worry about using a file read inside an initialize. Is there a more appropriate means to accomplish the same goal (shared static string)? I could bury this inside my code, but it was much easier to maintain the somewhat large string as an external file.
Thanks for any advice.
"I worry about using a file read inside an initialize".
Don't (worry). The fact that it is, for example, a class method is utterly irrelevant. It is code. It runs and does its job. It is sound code, it runs coherently at a coherent time, and your app bundle is a real thing that really contains the resource. There's no problem here.
If you want to postpone creation of the string, and make assurance doubly sure that the string is not initialized twice, you could instead use a singleton pattern so that the string value is not generated until the first time it is explicitly requested:
+ (NSString*) preamble {
static NSString* preamble = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *path = [[NSBundle mainBundle] pathForResource:#"preamble" ofType:#"html"];
preamble = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
});
return preamble;
}
But there is no special need for this. (EDIT: But see, to the contrary, the comment below of #bbum, who Really Knows Stuff.)
I just solved a really weird problem I had where I needed this class to behave sort of like a singleton but not really. Here's a code snippet
#implementation HMFPicturePreviewModalPanel
__weak static UIViewController *presentingInventoryViewController = nil;
static HMFPicturePreviewModalPanel *sharedPicturePreviewModalPanel = nil;
+(void)showPopupWithImage:(UIImage *)image withStartPoint:(CGPoint)startPoint withStartView:(UIView *)startView {
//this checks if there is already a panel visible.
if (![presentingInventoryViewController.view viewWithTag:kHMFPicturePreviewModalPanelTag]) {
sharedPicturePreviewModalPanel = [[HMFPicturePreviewModalPanel alloc] initWithFrame:presentingInventoryViewController.view.bounds withimage:image];
[presentingInventoryViewController.view addSubview:sharedPicturePreviewModalPanel];
[sharedPicturePreviewModalPanel showFromPoint:[startView convertPoint:startPoint toView:presentingInventoryViewController.view]];
}
}
+(void)changePresentingInventoryViewController:(UIViewController *)newInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = newInventoryViewController;
}
+(void)removePresentingInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = nil;
}
Is this called a semi-singleton? There's only ever going to be one of these on the screen at a time. I had to recreate this each time for it to work, hence why I couldn't do it as just a singleton.
What are the cons to this solution?
Also is it okay to have a __weak static variable?
I have a custom control in OSX with a drawing routine. Every instance of this control uses the same images.
I wonder if it's a good idea to instantiate these images as static objects available for all the instances created by the class.
In case that was a good idea, how can I implement the image loader in a cleaver way?
I thought to add to my class something like
static NSImage *imageone;
static NSImage *imagetwo;
static NSImage *imagethree;
But I'm not sure about the better way to initialize the images... maybe a singleton function like this?
+ (void)setupSharedImages {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageone = [NSimage imageNamed:#"...."];
imagetwo = [NSimage imageNamed:#"...."];
image three = [NSimage imageNamed:#"...."];
});
}
You can setup you images in + (void)initialize method. This method is called once when class is loaded and it also handles synchronization.
As an option, you may think of incapsulating images into "storage" singleton class.
#interface ImageStorage : NSObject
+ (ImageStorage *)sharedStorage;
- (NSImage *)imageForKey:(NSString *)key;
#end
#implementation ImageStorage {
NSDictionary *_images;
}
+ (ImageStorage *)sharedStorage
{
static dispatch_once_t onceToken;
static ImageStorage *sharedStorage;
dispatch_once(&onceToken, ^{
sharedStorage = [[ImageStorage alloc] init];
_images = [[NSDictionary dictionaryWithObjectsAndKeys:image1, key1, image2, key2, nil] retain];
});
return sharedStorage;
}
- (NSImage *)imageForKey:(NSString *)key
{
return [_images objectForKey:key];
}
#end
You could always lazy load them.
- (NSImage *)imageOne {
static NSImage *imageone;
if(imageone == nil)
imageone = [NSImage imageNamed:#"..."];
return imageone;
}
Edit:
Just wanted to clarify, do this for each image you need then in your draw method call
[self imageOne] etc...
Long time lurker, first time poster. I'm relatively new to objective-C so my apologies if I'm asking something fairly simple. My google & stack overflow-fu has let me down here, so I figured somebody could maybe help.
I have a synchronous process executing, say, three functions in a row - call it A -> B-> C , where task A executes, followed by B, followed by C.
Now, B involves an asynchronous process with a delegate callback for completion. But B must complete before C is executed, so I need some mechanism such that C is not triggered before B has finished. I imagine there must be a common design pattern for this problem?
Initially naive solution would be -
execute A
execute B
while (!B finished) {}
execute C
...but this seems really lame.
I suspect I can do this with some kind of block, but for the life of me I just can't figure it out. Could anyone help?
appreciate any assistance!
Guillaume
Thanks for all the feeback - apologies for not responding sooner. I've now resolved this in a slightly different way to the suggestions:
Firstly, I extended NSObject to have the following method -
#import "NSObject+LTExtensions.h"
#implementation NSObject (Testing)
- (void) performSelectorWithBlock: (SEL) selector withSemaphore:(dispatch_semaphore_t)semaphore
{
[self performSelector:selector]; // This selector should complete the semaphore
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
#end
This allows me to execute a block via a selector. When the block executes, the thread on which it is executed will wait until signaled to proceed by a specific dispatch semaphore.
What we can then do is as follows:
Call A
Create a dispatch semaphore and define a selector which executes B
Call the method defined above to execute B and wait for the selector to complete
When B is completed (via a delegate callback), it signals the dispatch semaphore to suspend the wait
I then execute C
So we have
A
B -> Asynchronous with delegate callback
C
Here's a simple example of how the above is implemented
-(void) methodA {
// ... do something
// Assign your semaphore (this is a dispatch_semaphore_t)
self.semaphore = dispatch_semaphore_create(0);
[self performSelectorWithBlock:#selector(methodB) withSemaphore:semaphore];
[self methodC];
}
-(void) methodB {
// ... do whatever needs to be done asynchronously
CFRunLoopRun();
}
-(void) methodBDelegateCallBack {
// This is called when B completes
// Signal completion
dispatch_semaphore_signal(self.semaphore);
CFRunLoopStop(CFRunLoopGetCurrent());
}
-(void) methodC {
...
}
Works very well without any issues (but I am new to Obj C, so there may be glaring issues with my approach).
Another approach to this problem might be the following: create an helper object for the async task and copy a completion block when the task is called. Call the completion block using the delegate methods once the async task is finished. As a result we might execute the tasks in order like the following:
FSTask *taskA = [FSTask taskWithName:#"Task A"];
FSAsyncTask *taskB = [FSAsyncTask asyncTaskWithName:#"Task B"];
FSTask *taskC = [FSTask taskWithName:#"Task C"];
[taskA performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
[taskB performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
[taskC performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
}];
}];
}];
So how is this achieved? Well, look at the task objects below ...
FSTask.m - synchronous work on main thread ...
#interface FSTask ()
#property (nonatomic, copy) NSString *name;
#end
#implementation FSTask
#synthesize name = _name;
+ (FSTask *)taskWithName:(NSString *)name
{
FSTask *task = [[FSTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
NSString *message = [NSString stringWithFormat:#"%#: doing work on main thread ...", _name];
NSLog(#"%#", message);
if (block)
{
NSString *result = [NSString stringWithFormat:#"%#: result", _name];
block(result);
}
}
#end
FSAsyncTask.m - asynchronous work on background thread ...
#interface FSAsyncTask ()
#property (nonatomic, copy) void (^block)(NSString *taskResult);
#property (nonatomic, copy) NSString *name;
- (void)performAsyncTask;
#end
#implementation FSAsyncTask
#synthesize block = _block;
#synthesize name = _name;
+ (FSAsyncTask *)asyncTaskWithName:(NSString *)name
{
FSAsyncTask *task = [[FSAsyncTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
self.block = block;
// the call below could be e.g. a NSURLConnection that's being opened,
// in this case a NSURLConnectionDelegate method will return the result
// in this delegate method the completion block could be called ...
dispatch_queue_t queue = dispatch_queue_create("com.example.asynctask", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^ {
[self performAsyncTask];
});
}
#pragma mark - Private
- (void)performAsyncTask
{
for (int i = 0; i < 5; i++)
{
NSString *message = [NSString stringWithFormat:#"%d - %#: doing work on background thread ...", i, _name];
NSLog(#"%#", message);
[NSThread sleepForTimeInterval:1];
}
// this completion block might be called from your delegate methods ...
if (_block)
{
dispatch_async(dispatch_get_main_queue(), ^ {
NSString *result = [NSString stringWithFormat:#"%#: result", _name];
_block(result);
});
}
}
#end
You can assign a block property to B where it would be used to execute a block of code before calling the delegate method. something like:
#property (nonatomic, copy)void(^yourBlock)(id blockParameter);
So, after calling B's delegate, you could call upon this block and execute it. Inside this block, you can call C's method.
the way I handled this is.
I created a NSMutableDictionary before the async call.
Then i make the async call. and do a check for the value I am waiting for
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[AsyncCallClass asyncCall:^{
#synchronized(dictionary) {
[dictionary setValue:myValue forKey:#"result"];
}
}];
while (true){
#synchronized(dictionary){
if ([dictionary valueForKey:#"resultValue"] != nil){
break;
}
}
[NSThread sleepForTimeInterval:.25];
}
MyResultClass *result = [dictionary valueForKey:#"resultValue"];
you can add time out for this too to stop it from being an infinite loop. but this is my solution. and it seems to work pretty well.
Here is the typical code I use to do such things (adapt the completionBlock signature and method names to your needs of course)
typedef void (^BCompletionBlock)(void);
#interface B : NSObject <BDelegate>
#property(nonatomic, copy) BCompletionBlock completionBlock;
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock;
#end
#implementation B
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock
{
// Store the completion block for later use
self.completionBlock = aCompletionBlock;
// Then execute your asynchronous action, that will call some delegate method when done
[self doYourAsynchronousActionWithDelegate:self];
}
-(void)yourBDelegateMethodCalledWhenDone
{
// Upon your async task completion, call your completion block then
if (self.completionBlock) self.completionBlock();
}
#end
Then here is an example usage:
-(void)doActions
{
[a doSynchronousAction];
[b doAsynchronousActionWithCompletion:^{
[c doSynchronousAction];
// A,B,C are now done
}];
}
I do this quite all the time to "convert" actions that uses delegate methods (to tell me when they are done) to actions that uses completionBlocks (have some classes to do this for UIAlertViews, UIActionsSheets, and many more cases for example) and it works like a charm.
I find it much more easier to use completionBlocks than the delegate mechanism in such cases.
You can also pass C in a block like so...
define a custom block
typedef void(^myCompletion)(BOOL complete);
Create your B method
-(void)performBWithCompletionBlock:(myCompletion)complete;
{
// do your things
[self.delegate delegateCallback];
complete(YES);
}
then create BG / async ABC
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // now we're on a BG queue to perform our async tasks
[self performA];
[self performBWithCompletionBlock:^(BOOL complete) {
if (complete == YES)
[self performC];
}];
});
If you want C to be on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self performC];
});
So I'm still kind of new to Objective-C, and this was my first app that I'm now updating. The idea is this: The whole app is basically various lists of stuff. It asks the API for 15 posts, shows those with a Load More button. Click Load More, it loads 15 more, etc. The API that it loads these from has a token system with a timeout built in. Too long between requests, and you have to get a new token. So I want to have a singleton to use anywhere in my app so I can just do [APIMachine getToken] and behind the scenes, it checks if the time since the last request was too long (or this is the first request), if so, gets a new token, otherwise returns the one we already have. I'm following the singleton pattern I've found in so many places, but every time the Load More button uses [APIMachine getToken]it gets either nothing or something completely random. I had it print this stuff in the logs, and one time I even got a UITableViewCell as my token. Looks like variables are being overwritten somehow. But I really can't figure it out.
So here it is:
static PoorAPI2 *_instance;
#implementation PoorAPI2
#synthesize apiToken, timeOpened, tokenTTL;
+ (PoorAPI2*)sharedAPI
{
#synchronized(self) {
if (_instance == nil) {
_instance = [[super allocWithZone:NULL] init];
}
}
return _instance;
}
-(NSString *)API_open{
//boring code to get api token redacted
if ([doneness isEqualToString:#"success"]) {
NSDictionary *data = [json objectForKey:#"data"];
apiToken = [data objectForKey:#"api_token"];
tokenTTL = [data objectForKey:#"ttl"];
timeOpened = [NSDate date];
}else{
NSLog(#"FFFFFFFUUUUUUUUUUUU this error should be handled better.");
}
return apiToken;
}
-(BOOL)isConnectionOpen{
return ([timeOpened timeIntervalSinceNow] > tokenTTL);
}
-(NSString *)getToken{
if([self isConnectionOpen]){
return apiToken;
}else{
return [_instance API_open];
}
}
-(id)init{
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedAPI]retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#end
I can only hope I'm doing something seriously foolish and this will be a hilarious point-and-laugh-at-that-guy thread. Then at least my app will work.
In API_open, you store three objects in instance variables, but they're not objects you own, so they'll probably be gone by the time you need them and replaced by something unpredictable. You need to retain them or use proper setters.
You problem is:
static PoorAPI2 *_instance;
C, and by inheritance Objective-C, do not initialize variables. Just change to:
static PoorAPI2 *_instance = nil;
Also I am of the school that adding extra code to try to prevent the singleton from being used as a single is a total waste of time, and only give you more code with more possibilities for bugs.
So if I was you then I would remove every method from +[PoorApi2 allocWithZone:] and down. Objective-C is a dynamic language and if a client wanted to instantiate a second instance of your singleton then it would be able to do so despite all your wasted extra lines of code. At the most I would add a log like this:
-(id)init{
if (_instance) NSLog(#"WARNING: PoorAPI2 already has a shared instance.");
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
Creating a second instance of a singleton is a programming error and should be caught in development. Not a problem you should add extra lines of code to hide.