alloc-init objects within +(void)initialize method to reuse them multiple times - objective-c

I'm using a NSDateFormatter object (customized for specific purposes) multiple times. Since I'm not a objc expert, I figured out three different approaches to deal with it.
In the first one, I've created a NSDateFormatter category and customized the code there. This is common approach but each time I create such an object, this is put in the main autorelease pool. This behaviour, I think, is valid both for non-ARC and ARC code.
In the second instead, I've overridden the +(void)initialize method and put the customization code there. Here a simple example:
static NSDateFormatter* dateFormatter = nil;
+(void)initialize
{
dateFormatter = [[NSDateFormatter alloc] init];
// other code here
}
Finally, I set up a third approach using a lazy loading instantiation through properties like the following:
-(NSDateFormatter)dateFormatter
{
if(dateFormatter) return dateFormatter;
// alloc-init here
}
Said this, I would like to know what approach is the most suitable to deal with objects multiple times and if, using +(void)initialize in that manner, is correct.
Thank you in advance.

Both later methods are correct, but I believe you want to put them together!
static NSDateFormatter *sharedInstance = nil;
+ (NSDateFormatter *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[NSDateFormatter alloc] init];
}
return sharedInstance;
}
Note: Such classes are called singletons. Read more

Essentially, my favourite pattern is the one that mindw0rk has already given (I have up voted his answer). It has the advantage that you can put the method in a category on NSDateFormatter. I would modify it slightly thusly:
+ (NSDateFormatter *)sharedInstance {
static NSDateFormatter *sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[NSDateFormatter alloc] init];
}
return sharedInstance;
}
so that you can't inadvertently reference sharedInstance without initialising it first.
If you have more than one shared instance, you can have a different method to get each one e.g.
+ (NSDateFormatter *)dateFormatterForFoo {
static NSDateFormatter *sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[NSDateFormatter alloc] init];
}
return sharedInstance;
}
+ (NSDateFormatter *)dateFormatterForBar {
static NSDateFormatter *sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[NSDateFormatter alloc] init];
}
return sharedInstance;
}
Or if each customised date formatter has its own subclass, they can each implement +sharedInstance to give back an object of the right class.
Or you can have a dictionary of date formatters:
+ (NSDateFormatter *)customisedFormatterForKey: (NSString*) key {
static NSDictionary *sharedInstances = nil;
if (sharedInstance == nil) {
sharedInstance = [[NSDictionary alloc] initWithObjectsAndKeys: ....];
}
return [sharedInstance objectForKey: key];
}
I have some comments on the -initialize approach.
You have to be a little bit careful about using -initialize. It's invoked once just before first use of the class. If you never use the class, it will never get invoked. So for your code to work, you have to make sure you send a message to NSDateFormatter or an instance of NSDateFormatter before you start using the shared instance dateFormatter. Note that sending a message to dateFormatter itself doesn't count. This is because it starts out nil and therefore doesn't have a class.

Related

Objective-c singleton creation

Code sample 1:
+ (MyClass *)sharedInstance{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
code sample 2
+ (MyClass *)sharedInstance{
static MyClass *sharedInstance = nil;
if (!sharedInstance) {
sharedInstance = [[MyClass alloc] init];
}
return sharedInstance;
}
Are there any differences in the result of the above code samples ?
The first one is better as it prevents multiple threads from creating multiple instances of the Singleton class given the necessary circumstances are met.
E.g: Take the 2nd example.
+ (MyClass *)sharedInstance{
static MyClass *sharedInstance = nil;
if (!sharedInstance) {
sharedInstance = [[MyClass alloc] init];
}
return sharedInstance;
}
Suppose Theread1 executes the following LOC and then gives the handle to Thread2
if (!sharedInstance)
Now Thread2 executes the following LOC and then hands over the handle to Thread1
sharedInstance = [[MyClass alloc] init];
Now, since the if condition was met first by Thread1, Thread1 will continue and execute the following LOC as well
sharedInstance = [[MyClass alloc] init];
Now, you have 2 instances of MyClass created.
Therefore, the 1st approach is best. It will make sure the block within
dispatch_once(&onceToken, ^{
});
gets executed only Once!
However, if you ONLY access the Singleton via the Main Thread (UI thread), then the second scenario will also work.
Using dispatch_once() is faster and it performs something only once so if you access it twice from different threads there won't be any problems.

Dispatch_once singleton error objective c

I am relatively new to GCD, I am currently using it to create a singleton object in my application. After doing some research I found using GCD's dispatch_once() method is the best way to achieve the singleton design pattern. For some reason, my code is breaking and I can not figure it out for the life of me. Below, I have pasted my singleton creation/init code, and the responding error.
+(id)sharedErrorMapper {
static dispatch_once_t onceToken;
static id sharedInstance;
dispatch_once(&onceToken, ^
{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(id)init {
//creating the ErrorMap data structure
//currently loading in dummy JSON data..
self = [super init];
if (self != nil) {
NSError *error = [[NSError alloc] init];
NSData *resourceData = [[NSData alloc] initWithContentsOfFile:
#"/Users/acuram/Desktop/GitHubWorkspaces/ios-sdk-src/ios-here-sdk/PPHSDKTests/Resources"];
self.errorMap = [[NSDictionary alloc] init];
self.errorMap = [NSJSONSerialization JSONObjectWithData:resourceData options:kNilOptions error:&error];
}
return self;
}
After setting some breakpoints and doing some stack tracing, my code is breaking at the dispatch_once() function call. The error I get back is a "NSInvalidArgumentException", it complains that my data parameter is nil. I am pretty shocked because I followed a legit tutorial video on youtube to implement this, I am also looking at my companies code base and they seem to do it in a similar way....

How do I release the value I want to return?

I know this is fairly fundamental stuff.
I have a class with a function that returns the name of the month; I'm not sure how to release a value that I want to return to prevent leaks.
In the class this value is declared:
static NSDateFormatter *formatter = nil;
if (formatter == nil) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMMM yyyy"];
}
Things happen, and then:
if([exampleDate isEqualToDate:
[[self creationDate] laterDate:exampleDate]])
{ //earlierDate returns the earlier date
return(#"Examples");
}else{
return([formatter stringFromDate:[self creationDate]]);
}
I've tried using autorelease but I'm using it incorrectly because I get a crash when I try to release it. I've also tried assigning the return value to a string, but I have the same crashing problems. Sorry for asking a question that is so fundamental, but I'd appreciate knowing how to release this properly, while still returning the value - and understanding how it works.
there are no leaks in the return part. Since stringFromDate: is not copy, mutableCopy, retain, alloc or new it returns already an autoreleased object.
Your NSDateFormatter will leak when the class is deallocated. If you don't want it to leak you should create a #property for it and release it in dealloc.
Local static objects will always leak.
I would not use such objects outside of singletons. Each time you create one of your classes you will leak an NSDateFormatter. Using #property is better in almost every case.
I've seen hacks like this but in my opinion they solve a problem you shouldn't have in the first place:
- (NSString *)someDateFromString:(NSString *)str {
static NSDateFormatter *formatter = nil;
if (formatter == nil) {
formatter = [[NSDateFormatter alloc] init];
}
if (str == nil) {
[formatter release];
formatter = nil;
return nil;
}
// do something
return date;
}
- (void)dealloc {
[self someDateFromString:nil]; // will release the static date formatter.
[super dealloc];
}

addObjectsFromArray: not copying into global NSMutableArray

So here is a partial sample of the relevant code.
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
- (IBAction)lookForRadioButtons:(id)sender {
// NSLog(#"Testing");
NSError *error;
NSString *radiostr = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"getRadios" ofType:#"txt"] encoding:NSASCIIStringEncoding error: &error] ;
if (radiostr == nil)
{
NSLog (#"Error! %#", error);
}
else
{
NSLog(#"%#",radiostr);
NSString *radiotxt= [webView stringByEvaluatingJavaScriptFromString:radiostr];
NSLog(#"%#", radiotxt);
NSArray *myRadios = [radiotxt componentsSeparatedByString:#"::"];
[radioInputArray addObjectsFromArray:myRadios];
NSLog(#"%d", myRadios.count);
NSLog(#"Number of buttons in global radio array %d", radioInputArray.count);
NSLog(#"%d", scrollViewer.subviews.count);
}
}
So it throws no exceptions and seems to work properly except after addObjectsFromArray:, my count in the global NSMutableArray is 0 (the count in the myRadios = 56). I am pretty sure they should be equal at this point but are not. I have declared my NSMutableArray up near the top so that it can be globally accessed. Am I missing something such as allocating and initializing this? Does it not do that automatically like in C#? Again, this is my first foray into the Objective-C world from Windows programming so please be gentle yet feel free to be critical.
Your two global arrays are not initialized.
The lines
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
just define the two variables as pointers to NSMutableArray, so you need to get them to point at an actual instance of the class NSMutableArray.
Somewhere in your initialization code, or through an accessor (best if a class method), you should set the variables to an empty, newly allocated NSMutableArray.
Here is a way to do it:
+ (NSMutableArray*)radioInputArray
{
if (!radioInputArray) {
radioInputArray = [[NSMutableArray alloc] init];
}
return radioInputArray;
}
Then use the accessor in your code instead of the global variable.
It may happen if your radioInputArray is nil,
you didn't initialize the array
you need to add
[[radioInputArray alloc] init];
before you do anything with radioInputArray
Good place for initialising object is "init" method in Global class
Ex.
-(id)init
{
if (self=[super init]) {
self.globalAllArtworkArray=[[NSMutableArray alloc] init];
self.globalCollectionArray=[[NSMutableArray alloc] init];
self.globalLookbookArray=[[NSMutableArray alloc] init];
}
return self;
}
+(ASNGlobalClass *)shareManager
{
static ASNGlobalClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}

Initialising a static variable in Objective-C category

I was trying to create a static variable to store a dictionary of images. Unfortunately, the best way I could find to initialise it was to check in each function that used the variable. Since I am creating this variable inside a category, I can't just initialise it inside the initialiser. Is there a neater way of initialising navigationBarImages?
static NSMutableDictionary *navigationBarImages = NULL;
#implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
- (void)drawRect:(CGRect)rect
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
NSString *imageName=[navigationBarImages objectForKey:self];
if (imageName==nil) {
imageName=#"header_bg.png";
}
UIImage *image = [UIImage imageNamed: imageName];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
[navigationBarImages setObject:image forKey:self];
}
#end
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
These function will be called automatically when the program starts and ends.
Consider this approach,
static NSMutableDictionary *navigationBarImages()
{
static NSMutableDictionary *dict = NULL;
if(dict == NULL)
{
dict = [[NSMutableDictionary alloc] init];
}
return [[dict retain] autorelease];
}
then whenever you woulde use navigationBarImages, replace it with navigationBarImages(), like this:
change
NSString *imageName=[navigationBarImages objectForKey:self];
to
NSString *imageName=[navigationBarImages() objectForKey:self];
If the function call overhead bothers you, maybe use a temporary variable to catch the return of navigationBarImages(),
NSMutableDictionary *dict = navigationBarImages();
[dict doSomething];
[dict doSomething];
The drawback is once you called navigationBarImages(), the instance of NSMutableDictionary got created, then it'll never get chance to dealloc until the end of the program.
All you need is to set your static once at a known point before it is used. For example, you can set an NSApplication delegate and have it do the work in -applicationDidFinishLaunching:
One option is to use C++. Change the file's extension to .mm and replace = NULL with [[NSMutableDictionary alloc] init].
You could add +initialize in the .m file of your category — you'll just need to make sure you're not smashing an existing implementation or you'll get general wonkiness. (Obviously, you can be sure of this if you wrote the code, but with third-party code, this is probably not the best approach.)