views:

509

answers:

3

Hi all!

I am using Apple's MyGizmoClass Singleton class for program-wide "session variables" and loving it! However, when I run "Build and Analyze" it gives weird results. Maybe my usage is wrong (it works, but it may be working due to a flakey side effect). Here is an example.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    int ct = 0;
    MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
    ct = [[myGizmoClass searchResultsForResortLocation] count];
    [myGizmoClass release];
    NSLog(@"ct: %d",ct);
    return ct;
}

or

- (void)viewWillAppear:(BOOL)animated {


     // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
     //self.navigationItem.rightBarButtonItem = self.editButtonItem;

    NSMutableString *which_resort = [[NSMutableString alloc] init];
    NSMutableString *category_code = [[NSMutableString alloc] init];
    MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];

    ...

    which_resort = [self which_resort_location_are_we_in];

    ... 
    [myGizmoClass setWhich_resort:which_resort];

    int useDebugMode = [myGizmoClass useDebugMode]; 

    ...


    [myGizmoClass release];

    [which_resort release];
    [category_code release];

    [super viewWillAppear:animated];

}

Again, this usage may be WAY off, but I thought each method I used a value from the singleton I had to do:

MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];

and

[myGizmoClass release];

BUT i am getting these errors during Analyze:

/Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:495:2 Incorrect decrement of the reference count of an object is not owned at this point by the caller /Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:493:30 Method returns an Objective-C object with a +0 retain count (non-owning reference) /Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:495:2 Incorrect decrement of the reference count of an object is not owned at this point by the caller

Now, please take this into account: I am calling this:

MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];

at the beginning of EVERY method need a "session variable" and then calling:

[myGizmoClass release];

at the end of that method PRIOR to returning a result (if i return a result from that function.

Is this not the way I should do it?

This is the ONLY thing that the analyzer (thank goodness) is reporting wrong with the program. I do not know if i should ignore it. I do not know if I am doing the calls in the right place.

Here is another question I am worried about: Does this work or do the subsequent calls to *myGizmoClass mess anything up?

-(void) function_a {
     MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];

     [myGizmoClass setC:1];

     int result_b = [self function_b];

     printf("Addition result is: %d", result_b);

     [myGizmoClass release]
}

-(int) function_b {
     MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];

     int b = 0;

     b = b + [myGizmoClass c];

     [myGizmoClass release]

     return b;
}

(i have not tested the code above)

In other words there anything wrong with calling MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager]; from function_b when you have not released MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager]; from function_a?

A: 

You should not be releasing myGizmoClass. When [MyGizmoClass sharedManager] is called, the returned object is not being retained. Since it's a shared object, its only owner is its class other object is responsible for releasing it unless they first retain it.

Take a look at this short guide from Apple on using singleton objects in your Objective-C code.

Marc W
@Marc Sorry, forgot to tell you that Apple's MyGizmoClass Singleton silently overrides retain/release. That would make a difference as to whether or not my "release" did anything...tho still having it used in referring to a correctly-implemented singleton is incorrect according to the above statements. Thx
Jann
+5  A: 

The problem is the way that you are using a singleton. The general idea is that you don't retain or release any singleton objects, because there is only one instance that hangs around, and the Apple implementation of singletons are coded in such a way that you can't. This brings about some issues that Peter Hosey blogged about with a better implementation of a Cocoa singleton.

The warnings that the Analyzer logs are simply stating that you "shouldn't" retain or release the singleton object; by virtue of the fact that you call [MyGizmoClass sharedManager] you shouldn't be releasing anyway (following the Cocoa memory management guidelines).

Perspx
so, with the example of variable: 'c' type int (in MyGizmoClass.h) i have it defined as:@property int c;but if it were an NSString:@property (nonatomic,retain) NSString *c;is the way I would do it. Are you saying the NSString should be like:@property (nonatomic) NSString *c;and I should never call [myGizmoClass release]?Okay .. I can understand that...but when and where should I call:MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];if i want it available to the entire class that I am in?Do i still call it at the start of each function/method?
Jann
Peters notion that singletons should not override retain/release is, to my mind, totally wrong. Objects should be robust in the face of programmer abuse - if you must, have it throw an exception in release in debug mode to correct bad coding behavior. But you want to avoid the possibility where you can of having a rogue retain take down the whole app, which not protecting singleton leaves wide open. Defense AND stability in depth is key for good application development.
Kendall Helmstetter Gelner
@Jann: The idea behind singletons is that only one instance is ever created – this is done in the `-sharedManager` method where a static instance of the `MyGizmoClass` is created (only once) and returned. As an optimization the instance is likely to be created on the first call to `-sharedManager`. As of this, you can access the shared manager whenever you need to and don't need to store it in an ivar or anything, so yes.
Perspx
@Kendall I think it's pretty subjective and there's a lot of conflicting viewpoints around singletons. I can see where you're coming from, but from the point of writing bad code, if you overrelease or overretain then your application will crash or leak memory, and you should know about it – preventing retain/release removes any semantic value and can potentially introduce silent bugs from this.
Perspx
Followup: The code Peter presents as "correct" is pretty dangerous, read down to my comment for the explanation. Basically if you call alloc/init and then release not remembering it's a singleton, when you call alloc/init again as you would expect to work instead you'll get a crash. Always override release.
Kendall Helmstetter Gelner
@Perspx: It's not at all subjective to say that if you follow the memory guidelines for Objective-C correctly you should NEVER encounter a crash. A class where alloc/init does not always return a valid instance is broken. There are conflicting viewpoints around use of singletons but I don't think there are many on making them work. If a singleton is not controlling instance management of itself I would argue it's not truly a singleton.
Kendall Helmstetter Gelner
I guess I agree with the school of thought that at some level if you have people who don't understand that you don't retain/release a singleton and aren't even going to bother to find it out, they're a lost cause to some degree anyway. And that exemplifies my point – people who understand the memory management guidelines are likely to understand the way singletons work, and that you don't alloc/init a singleton but use the shared convenience initializer. I wasn't really taking sides, I was just presenting the other viewpoint which I partially agree with, but I also see the other side too.
Perspx
@Perspx: there is no "other side" to see, as I said what is going on here is not subjective the way many other coding approaches can be. A class that does not always return a valid instance from init is broken. There might be another way to make that happen without overriding release, but you are violating the objective-C development guidelines any time you return an invalid instance from init. You can return nil, but not an invalid instance which is what that example code will do if you simply follow the standard objective-C memory management rules when using with that code as a template.
Kendall Helmstetter Gelner
@Kendall You have a point in many languages, but one could argue that Objective-C is not one of those languages. It doesn't have private methods or some of the other "enforcement" features that Java devs (and others) consider absolutely essential. If your singleton is exposed for third-party code to call (like Apple's is), I'd say it's more important, but I prefer good sensible coding to restrictive safeguards. I also agree with Perspx in that if calling retain/release is wrong, it ought to cause a bug. Silent failures are the worst...
Quinn Taylor
Llet me be clear I totally believe the question of fail fast vs. robust code is subjective, and a good discussion topic. But this is NOT an example of fail fast; that would mean having the code die immediately on calling retain or release of a singleton to enforce the desired rule they should not be used. Instead Peters code will allow you to call release - which at some LATER point will cause a crash, when you make use of alloc/init which Peter explicitly DOES allow! It's not fail fast, it's buggy code. To enforce release being disallowed, you must raise an exception at that time.
Kendall Helmstetter Gelner
Summary: If you want to enforce rules that disallow use of retain/release, you must override them to indicate that to the developer at runtime. If you want to instead make the code robust and work with incorrect uses of retain/release, you must also override them. Objective-C doesn't have enforcement mechanisms for pretty much anything, being a dynamic language. That's why it is so very important not to break sets of well-understood rules without very, very clear documentation and layers of self-enforcement if you plan to do so. Code that silently corrupts pointers is simply buggy.
Kendall Helmstetter Gelner
@Quinn: not to spam this thread any more than I already have, but you said "Silent failures are the worst..." which I totally agree with. In fact that is my whole problem with the code presented, is a silent failure mode... however the way Apple presents a singleton with retain/release overidden, using retain/release is actually never a failure - it's simply irrelevant, just as it would be if you flipped the switch that turns on GC on the Mac but were still using retain/release in some code. So you are not really preventing any errors at all by overriding retain/release in a singleton.
Kendall Helmstetter Gelner
Thank you all. I have voted this solution as the "Answer" and really thank you for showing someone (new to singletons) the "ropes". @Perspx: Thx for letting me know that i did not need/want to release the myGizmoClass singleton (even tho Apple silently 'no-ops' release anyway). I removed them all and the "Analyze" step of XCode removed all the errors.
Jann
@Kendall It's true that this code doesn't fail fast, but that would be hard to do right in this case. You also have a point about silent failures versus fail-proof code. I find it strange to write code where it doesn't make a difference if the retains and releases are all out of balance; to me, that encourages sloppy coding. FWIW, Zombies can catch over-release errors, which are different from pointer corruption. You nailed it: "If you want to enforce rules that disallow use of retain/release..." I prefer to document behavior without tons of safeguards, but I can see the merit of both sides.
Quinn Taylor
A: 

I'm using the Apple's singleton implementation, my only question is, should I need to add an application will Terminate observer inside the singleton class to release the singleton?

Pablo
Apple's singleton implementation does that for you.
Jann