Unexpected Singleton class behavior on iPhone, am I doing something wrong? - iphone

I'm implementing a singleton class as follows:
static Singleton* _singletonInstance;
#implementation Singleton
+(void)initialize
{
_singletonInstance = [[Singleton alloc] init];
}
+(Singleton*)instance
{
return(_singletonInstance);
}
initialize only gets called the first time someone calls instance. I then have a method that I can call to set up some instance variables. So it ends up looking like this.
_singleton = [Singleton instance];
[_singleton setupWithParams: blah];
When i get an instance of this singleton inside an object, it works fine the first time; However, after i dealloc and create a new copy of the object that needs an instance of the singleton, i get a BAD ACCESS error when I try to call the setup function.
Just to test things I print out the address of the instance before I make the setup call and both times they report the same address, but when i check the error log for BAD ACCESS call, it lists a completely different memory address.
Does anyone have any ideas why this pointer to the instance seems to look fine when I print it, but when I make a call to it, it is seemingly pointing to random data?

The pointer value looks valid because it used to be, but most likely the memory has been free'd, which is why what it points to looks like random data.
You've acquired one reference with your [[Singleton alloc] init] above, but is there a release somewhere else that might be executing? I bet your code is calling instance, and then release-ing later, even though your code never acquired a reference. And that shouldn't be necessary for a singleton anyways. Just a guess...

Are you deallocing your _singletonInstance somewhere?

I'm using much more complex, but very stable version of Singleton template (taken with description from Brandon "Quazie" Kwaselow Blog):
static SampleSingleton *sharedSampleSingletonDelegate = nil;
+ (SampleSingleton *)sharedInstance {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedSampleSingletonDelegate;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
sharedSampleSingletonDelegate = [super allocWithZone:zone];
// assignment and return on first allocation
return sharedSampleSingletonDelegate;
}
}
// on subsequent allocation attempts return nil
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}

Valerii's code is better for implementing a singleton, but the problem is almost certainly that the code that calls [Singleton instance] is operating as if it has ownership without actually taking ownership using retain, and then is later releasing it.
Look there for your bug, and read the Memory Managment Rules.
Also, in Xcode, enable NSZombieEnabled and the console will show you when you try to message the object after its been released.

Related

Error when using NSMutableSet

I get the error
* Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSCFSet: 0x6b66390> was mutated while being enumerated.'
when adding an new delegate to my class. Or at least, that's where I think the problem is.
This is my code: MyAppAPI.m
[...]
static NSMutableSet *_delegates = nil;
#implementation MyAppAPI
+ (void)initialize
{
if (self == [MyAppAPI class]) {
_delegates = [[NSMutableSet alloc] init];
}
}
+ (void)addDelegate:(id)delegate
{
[_delegates addObject:delegate];
}
+ (void)removeDelegate:(id)delegate
{
[_delegates removeObject:delegate];
}
[...]
#end
MyAppAPI is a singleton which I can use throughout my application. Wherever I can (or should be able to) do: [MyAppAPI addDelegate:self].
This works great, but only in the first view. This view has a UIScrollView with PageViewController which loads new views within itself. These new views register to MyAppAPI to listen to messages until they are unloaded (which in that case they do a removeDelegate).
However, it seems to me that it dies directly after I did a addDelegate on the second view in the UIScrollView.
How could I improve the code so that this doesn't happen?
Update
I'd like to clarify me a bit further.
What happens is that view controller "StartPage" has an UIScrollView with a page controller. It loads several other views (1 ahead of the current visible screen).
Each view is an instans PageViewController, which registers itself using the addDelegate function shown above to the global singleton called MyAppAPI.
However, as I understand this viewcontroller 1 is still reading from the delegate when viewcontroller 2 registers itself, hence the error shows above.
I hope I made the scenario clear. I have tried a few things but nothing helps.
I need to register to the delegate using addDelegate even while reading from the delegates. How do I do that?
Update 2
This is one of the reponder methods:
+ (void)didRecieveFeaturedItems:(NSArray*)items
{
for (id delegate in _delegates)
{
if ([delegate respondsToSelector:#selector(didRecieveFeaturedItems:)])
[delegate didRecieveFeaturedItems:items];
}
}
Scott Hunter is right. This error is thrown when you try to edit a list while iterating.
So here is an example of what you may be doing.
+ (void)iteratingToRemove:(NSArray*)items {
for (id delegate in _delegates) {
if(delegate.removeMePlease) {
[MyAppAPI removeDelegate:delegate]; //error you are editing an NSSet while enumerating
}
}
}
And here is how you should handle this correctly:
+ (void)iteratingToRemove:(NSArray*)items
{
NSMutableArray *delegatesToRemove = [[NSMutableArray alloc] init];
for (id delegate in _delegates) {
if(delegate.removeMePlease) {
[delegatesToRemove addObject:delegate];
}
}
for(id delegate in delegatesToRemove) {
[MyAppAPI removeDelegate:delegate]; //This works better
}
[delegatesToRemove release];
}
The error suggests that, while some code somewhere is in the middle of going through your list, you are modifying the list (which explains the crash after addDelegate is called). If the code doing the enumerating is the one modifying the list, then you just have to put off the modifications until the enumeration is done (say, by collecting them up in a different list). Without knowing anything about the code doing the enumerating, can't say much more than that.
A simple solution, don't use a mutable set. They are dangerous for a variety of reasons, including this one.
You can use -copy and -mutableCopy to convert between mutable and non-mutable versions of NSSet (and many other classes). Beware all copy methods return a new object with a retain count of 1 (just like alloc), so you need to release them.
Aside from having less potential for bugs, non-mutable objects are faster to work with and use less memory.
[...]
static NSSet *_delegates = nil;
#implementation MyAppAPI
+ (void)initialize
{
if (self == [MyAppAPI class]) {
_delegates = [[NSSet alloc] init];
}
}
+ (void)addDelegate:(id)delegate
{
NSMutableSet *delegatesMutable = [_delegates mutableCopy];
[delegatesMutable addObject:delegate];
[_delegates autorelease];
_delegates = [delegatesMutable copy];
[delegatesMutable release];
}
+ (void)removeDelegate:(id)delegate
{
NSMutableSet *delegatesMutable = [_delegates mutableCopy];
[delegatesMutable removeObject:delegate];
[_delegates autorelease];
_delegates = [delegatesMutable copy];
[delegatesMutable release];
}
[...]
#end
Scott Hunter is right - it's a problem with modifying the NSSet while you're enumerating over the set's items. You should have a stack trace from where the application crashes. It probably has a line where you're adding to/remove from the _delegates set. This is where you need to make the modification. It's easy to do. Instead of adding to/deleting from the set, do the following:
NSMutableSet *tempSet = [_delegates copy];
for (id delegate in _delegates)
{
//add or remove from tempSet instead
}
[_delegates release], _delegates = tempSet;
Additionally, NSMutableSet is not thread safe, so you should call your methods always from the main thread. If you haven't explicitly added any extra threads, you have nothing to worry about.
A thing to always remember about the Objective-C "fast enumeration".
There is 2 big difference between "fast enumeration" and a for loop.
"fast enumeration" is quicker than a for loop.
BUT
You can't modify the collection your enumerating over.
You can ask your NSSet for - (NSArray *)allObjects and enumerate over that array while modifying your NSSet.
You get this error when a thread tries to modify (add,delete) the array while other thread is iterating over it.
One way to solve this using NSLock or synchronizing the methods. That ways add, remove and iterate methods cannot be called in parallel.
But this will have effect on performance and/or responsiveness because any add/delete will have to wait for the thread that was iterating over the array.
A better solution inspired from Java's CopyOnWriteArrayList would be to create a copy of the array and iterate over the copy. So the only change in your code will be:-
//better solution
+ (void)didRecieveFeaturedItems:(NSArray*)items
{
NSArray *copyOfDelegates = [_delegates copy]
for (id delegate in copyOfDelegates)
{
if ([delegate respondsToSelector:#selector(didRecieveFeaturedItems:)])
[delegate didRecieveFeaturedItems:items];
}
}
Solution using locks with performance impact
//not a good solution
+ (void)addDelegate:(id)delegate
{
#synchronized(self){
[_delegates addObject:delegate];
}
}
+ (void)removeDelegate:(id)delegate
{
#synchronized(self){
[_delegates removeObject:delegate];
}
}
+ (void)didRecieveFeaturedItems:(NSArray*)items
{
#synchronized(self){
for (id delegate in _delegates)
{
if ([delegate respondsToSelector:#selector(didRecieveFeaturedItems:)])
[delegate didRecieveFeaturedItems:items];
}
}
}

default setter in iOS

- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
if (someInstance == aSomeInstanceValue)
{
return;
}
SomeClass *oldValue = someInstance;
someInstance = [aSomeInstanceValue retain];
[oldValue release];
}
ok, so setter should look like. I understand first 3 lines - prevent before situation when new object is the same as the old one.
But what about this line:
SomeClass *oldValue = someInstance;
Why system have to keep address of old object. Why can't it be simply
[someinstance release];
someinstance = [aSomeInstanceValue retain];
Actually - no reason.
It's usually just a choice.
There are three idioms for writing accessors.
Autorelease:
- (void)setFoo:(id)newFoo {
[foo autorelease];
foo = [newFoo retain];
}
Less code to write, but I think autorelease in this case is being lazy.
Retain then release
- (void)setFoo:(id)newFoo {
[newFoo retain];
[foo release];
foo = newFoo;
}
Check first
- (void)setFoo:(id)newFoo {
if ([foo isEqual:newFoo]) {
return;
}
[foo release];
foo = [newFoo retain];
}
The only difference between the last two is that the second checks to see if the new value is different to the current value before trying to set the property. At the cost of an extra if statement. So - if the new value is likely to be the same as the old value, using this construction gives better performance.
Generally, and if you're not using properties for some strange reason, use retain then release, and then if profiling shows that there's a bottleneck - use the check first method.
I would suggest the default retain setter works something like this:
- (void) setFoo:(id) foo {
if ( foo == _foo) return;
[_foo release];
_foo = [foo retain];
}
if you don't check if the old and the new foo are the same, you might end up with a reference to a deallocated object if you for some reason write something like this:
myObject.foo = myObject.foo;
Because the same object would first be released, and then retained. If the myObject is the sole owner, the object would be deallocated after the first release, leaving you with a dangling pointer.
The default retain setter works like that :
- (void)setFoo:(Foo *)aFood
{
if (_foo != nil)
[_foo release];
if (aFood != nil)
_foo = [aFood retain];
}

Obj-C: Passing pointers to initialized classes in other classes

I initialized a class in my singleton called DataModel. Now, from my UIViewController, when I click a button, I have a method that is trying to access that class so that I may add an object to one of its dictionaries. My get/set method passes back the pointer to the class from my singleton, but when I am back in my UIViewController, the class passed back doesn't respond to methods. It's like it's just not there. I think it has something to do with the difference in passing pointers around classes or something. I even tried using the copy method to throw a copy back, but no luck.
UIViewController:
ApplicationSingleton *applicationSingleton = [[ApplicationSingleton alloc] init];
DataModel *dataModel = [applicationSingleton getDataModel];
[dataModel retrieveDataCategory:dataCategory];
Singleton:
ApplicationSingleton *m_instance;
DataModel *m_dataModel;
- (id) init {
NSLog(#"ApplicationSingleton.m initialized.");
self = [super init];
if(self != nil) {
if(m_instance != nil) {
return m_instance;
}
NSLog(#"Initializing the application singleton.");
m_instance = self;
m_dataModel = [[DataModel alloc] init];
}
NSLog(#"ApplicationSingleton init method returning.");
return m_instance;
}
-(DataModel *)getDataModel {
DataModel *dataModel_COPY = [m_dataModel copy];
return dataModel_COPY;
}
For the getDataModel method, I also tried this:
-(DataModel *)getDataModel {
return m_dataModel;
}
In my DataModel retrieveDataCategory method, I couldn't get anything to work. I even just tried putting a NSLog in there but it never would come onto the console.
Any ideas?
Most likely you are sending messages that get ignored, e.g. they're being sent to objects which don't exist/aren't the one you're looking for, and for some reason aren't crashing. This occurs in the case of messaging nil, or possibly other illegitimate values. Although you seem to expect that the m_ variables will be initialized to 0, this is not good form, and furthermore you are not following a very typical objc pattern for your singletons -- m_dataModel should be an ivar of m_instance, and m_instance should probably be declared static, as you probably don't want it accessed from other files directly. In addition, the most likely source of your bug is somehow the -init method, which should never be called on a singleton -- instead do something like this:
+ (ApplicationSingleton *)sharedInstance {
static ApplicationSingleton *instance = nil;
if(!instance) {
instance = [[self alloc] init]; //or whatever custom initializer you would like, furthermore some people just put the initialization code here and leave -init empty
}
return instance;
}
the code you have now leaks because you allocate an object (self) and don't release it before returning a potentially different instance (the shared one if one already exists), such that the newly allocated one is typically lost.

Apple Singleton example query?

I am a little confused by this snippet of code (presented in the CocoaFundamentals guide) that overrides some of the methods when creating a singleton instance.
static id sharedReactor = nil;
+(id)sharedInstance {
if(sharedReactor == nil) sharedReactor = [[super allocWithZone:NULL] init];
return sharedReactor;
}
.
+(id)allocWithZone:(NSZone *)zone {
return[[self sharedInstance] retain];
}
-(id)retain {
return self;
}
In the code where the singleton instance is created the +sharedInstance method calls [super allocWithZone:NILL] from the superclass (which in my case is NSObject) The allocWithZone above is only called if you attempt to use it to create a new singleton.
The bit I am confused about is the use of retain, especially seeing as retain is also overridden to return self. Can anyone explain this, could it not be written:
+(id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
-(id)retain {
return self;
}
EDIT_001:
Based on comments and reading various posts on the web I have decided to go with the following (see below) I have chosen to go for a shared singleton approach where if needed I would have the option of creating a second or third instance. Also at this stage as I am only using the singleton for the model portion of MVC for a simple iPhone app I have decided to leave thread safety out. I am aware its important and as I get more familiar with iPhone programming I will likely use +initialize instead (keeping in mind the subclass issue where it can be called twice) Also I have added a dealloc, firstly to log a message should the singleton be released, but also to clean things up properly should the singleton be no longer required.
#interface SharedManager : NSObject
+(id)sharedInstance;
#end
#implementation SharedManager
static id myInstance = nil;
+(id)sharedInstance {
if(myInstance == nil) {
myInstance = [[self alloc] init];
}
return myInstance;
}
-(void)dealloc {
NSLog(#"_deal: %#", [self class]);
[super dealloc];
myInstance = nil;
}
#end
In testing I found that I had a set the static variable to nil in the dealloc or it maintained its pointer to the original object. I was initially a little confused by this as I was expecting the scope of the static to be the instance, I guess its the class instead, which makes sense.
cheers gary
First, don't use this code. There is almost never a reason to do all this for a simple singleton. Apple is demonstrating a "Forced Singleton," in that it is impossible to create two of them. It is very rare to really need this. You can almost always use the "shared singleton" approach used by most of the Cocoa objects that have a singleton constructor.
Here's my preferred way of implementing shared singleton:
+ (MYManager *)sharedManager
{
static MYManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[self alloc] init];
}
return sharedManager;
}
That's it. No other code is required. Callers who use +sharedManager will get the shared instance. Callers who call +alloc can create unique instances if they really want to. This is how such famous "singletons" as NSNotificationCenter work. If you really want your own private notification center, there is no reason the class should forbid it. This approach has the following advantages:
Less code.
More flexible in cases where a non-shared instance is useful.
Most importantly: the code does what it says it does. A caller who thinks he's making a unique instance with +alloc doesn't encounter surprising "spooky action at a distance" behavior that requires him to know an internal implementation detail of the object.
If you really need a forced singleton because the object in question maps to a unique resource that cannot be shared (and it's really rare to encounter such a situation), then you still shouldn't use +alloc trickery to enforce it. This just masks a programming error of trying to create a new instance. Instead, you should catch the programming error this way:
+ (MYManager *)sharedManager
{
static MYManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[self alloc] initSharedManager];
}
return sharedManager;
}
- (id)init
{
NSAssert(NO, #"Attempting to instantiate new instance. Use +sharedManager.");
return nil;
}
// Private method. Obviously don't put this in your .h
- (id)initSharedManager
{
self = [super init];
....
return self;
}
There is a good example of different singleton methods with comments here on SO:
What does your Objective-C singleton look like?
If it helps, the example has a different approach to allocWithZone: which returns nil.

How should I handle a failure in an init: method in Objective-C?

Let's say I'm building a new class for the iPhone in Objective-C. In one of my init methods I want to manually allocate some memory. So, I might have something like this:
- (id)initWithSomeObject:(SomeObject *)someObject {
self = [super init];
if (self != nil) {
myObject = someObject;
[myObject retain];
if ( (memory = calloc(1, sizeof(SomeStruct)) == NULL) {
// What should I do here to clean up
[self release];
self = nil;
}
}
return self;
}
Now, assuming that the calloc() could fail, and that failing to allocate memory is catastrophic for my object, what should I do inside the if-body to clean up properly? Is there an Objective-C idiom or pattern that I should be using?
Edit: I included the code posted by Rob Napier. But, I still have to release myObject, right? Or does the added code somehow trigger dealloc()?
Yes, you should release yourself and then return nil.
[self release];
self = nil;
See Issues with Initializers in the Concepts in Objective-C Programming guide.
You need to clean up anything you need to and then set the self reference to nil. Apple Dev Portal has an article:
Link
I just tried. -dealloc gets called due to [self release], so myObject would not need to get released in initWithSomeObject. To be sure, you might move myObject = [someObject retain]; (I prefer that style in case -retain might fail for some reason) below the call that might fail (if that's possible).