@try @catch in Apple's Objective-C sample code?

I always thought using @try @catch in ObjC was considered bad practice (perhaps wrongly, or maybe rightly, but not anymore?). I've never used @try, @catch in an Objective-C app because I always thought, again perhaps wrongly, that exceptions thrown by the system (or a framework, or anyone) were to signify that you are now in an unrecoverable state? And I thought that the @try @catch stuff was kind of legacy stuff and we aren't really supposed to use that functionality of the language?


Looking at this bit of Apple sample code I see:


        @try
        {
            NSViewController *vcToRemove = splitViewItem.viewController;
            [vcToRemove removeObserver:self forKeyPath:@"touchBar" context:@"touchBar"];
        }
        @catch(id anException) {
            //Do nothing, obviously it wasn't attached because an exception was thrown.
        }


You don't see @try and @catch in Objective-C code often. Is doing this with KVO an "exception to the rule" and I was in the dark? Or have Objective-C best practices changed, and the new world order (nWo) is upon us?


Is this considered "good" now? I've seen a developer correct code like this saying something like "this is not java, please fix this." I would probably do the same. I just wanted to check in with the Apple Gods on this.


Thanks!

Hello Macho Man ***** Savage,

Exceptions have been fully supported and official considered "good practice" in Objective-C for many years now. The catch is that they are considered "bad practice" when used with any Apple Cocoa-based framework. And there is another catch to that catch. There is an absolute requirement to use exception handling for those parts of Cocoa that throw exceptions. Make sense?


Basicially, if Cocoa or any Apple framework can throw an exception, then you had better catch it and properly deal with it. There are those who will tell you that any exception is the result of "programmer error" and the only proper way to deal with it is to gracefully shutdown your app instead of allowing an uncaught exception to do that for you. That is wrong. Or, at least, partially wrong. It is definitely better to gracefully shutdown your app instead of allowing an uncaught exception to do it for you. But otherwise, you have to deal with each exception that Apple throws in the most appropriate manner. For example, when NSFileHandle throws exceptions for flow control, it is better to just handle those and keep going.


Of course, this is all about Objective-C. Due to the way Swift is designed, it should be impossible to inject a throwing method into the Apple frameworks the same way that you can do with Objective-C. That will eliminate the primary "bad practice" that people are referring to with respect to Cocoa. You still have to worry about the underlying frameworks throwing Objective-C exceptions that you now can't handle in Swift. Good luck with that one. From what I've seen, if you just avoid NSFileHandle, you should be fine in most cases. Most of the other exceptions that Apple throws would be caught by Swift with an assertion or something.

The catch is that they are considered "bad practice" when used with any Apple Cocoa-based framework. And there is another catch to that catch. There is an absolute requirement to use exception handling for those parts of Cocoa that throw exceptions. Make sense?

Hehe. Well the sample code here is using API for a "Cocoa-based" framework.

>Basicially, if Cocoa or any Apple framework can throw an exception, then you had better catch it and properly deal with it.


I was under the belief that you should take measures to avoid the exception being thrown in the first place. Like don't call objectAtIndex: on an array and pass an out of bounds index. Don't call removeObserver:forKeyPath:context: and pass in an object that was not an observer in the first place.


Like when I design an API, if passing in something in is "illegal" I sometimes use NSAssert, which I know throws an exception "under the covers" but the caller isn't really supposed to catch it.

Why are you looking at Apple code as an indicator of prefered style?


Sample code is written to illustrate technical points, and the person writing the code often ends up with a bunch of things that aren't important that get brushed aside in expediant manners.


For that matter, I'm pretty sure I've seen template code with NSAbort in it to deal with an error condition, and that's NEVER been proper practice for dealing with an error. 😮

>Why are you looking at Apple code as an indicator of prefered style?


>For that matter, I'm pretty sure I've seen template code with NSAbort in it to deal with an error condition, and that's NEVER been proper practice for dealing with an error.


Sometimes there is sample code that'll do no error handling at all and be commented

//You should handle the error.


That's fine. A sample is a sample, to demonstrate how to do something. It doesn't have to be complete production ready app. It could have an NSAbort in there, everyone knows that's not how to handle an error that's more like hey...you need to implement this part because it's beyond the scope of this sample project.


But sample code should be "correct" IMHO (not just correct enough to compile) and follow best practices. I'm not saying catching the exception in ObjC is bad practice. I'm just saying I always thought it was, andI'm just asking if catching exceptions like this should be a pattern embraced by the dev community.


It's not like I was looking for this. I just grabbed the sample for other reasons and stumbled across the code and it made me wonder.

Here's my take on this:


— It is considered "bad practice" to use Obj-C exceptions for flow control, such as for handling errors that are an expected part of normal program flow. There are some special cases, such as NSFileHandle, which uses Obj-C exceptions for reporting I/O errors, where you must use @try/@catch in order to handle the errors.


Your view controller sample falls into a sort of self-inflicted gray area. Properly written code should be able to keep track of whether there is an observation to remove, obviating the need to catch the error here. However, it may well be that there are unavoidable reasons why the code isn't properly written, such as being inherited from another developer and not worth re-writing, or if the tracking of paths through the code is too complicated to be practically attempted.


The downside of this solution to this sample problem is that truly fatal exceptions might well be ignored, with unspecified bad consequences. IOW, this piece of code is a bet against the risk of really bad things happening.


I think the proper attitude is to realize that this is sample code, and like all sample code it's a partially-incomplete recipe for writing real code. Similarly, there is much sample code that doesn't check return values for errors, or checks but doesn't handle the errors. It's not real code.


— The issue about frameworks is that it can be problematical throwing exceptions across framework boundaries. Such problems might include per-module global data that isn't cleaned up properly once control has exited the framework, and the correct establishment and/or draining of autorelease pools.


Historically, once the Obj-C language acquired blocks (closures, in Swift terminology), this problem got worse, because GCD operation boundaries got dangerous too, and that opened up a much bigger risk surface.


— Setting aside the rare cases where Obj-C exceptions are used to report errors (such as NSFileHandle), an exception can be assumed to indicate an error that shouldn't happen, and that in turn raises the question of whether the app is too scrambled to recover from.


My view (and I'm not alone in this) is that a decent recovery might happen to be possible in the majority of cases, but the danger of things going very wrong in the rarer cases is too big to take the risk. This risk is magnified if there is the possibility of (say) saving garbage data to disk, while continuing on with apparently correct data in memory.

Which sample is this code from?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Kudos to QuinceyMorris for mentioning closures. I forgot to do that.


I can't disagree with anything anyone has said on this issue. I think the bigger picture is that exceptions just aren't commonly used in Objective-C and often aren't used well elsewhere. Personally, I'm a big fan of exceptions but I know that not everyone else is. I've seen Java code reviews that were nothing but hundreds or thousands of lines of exception handling with no actual functionality. In the Apple community, there was a palpable, collective sign of relief and exception-bashing that occured when Swift was released without exceptions. That was followed by awkward silence when Apple put them back in.


In any sample code, there is always a risk that it is going to be written by some crazy programmer like me who likes exceptions and even uses them in closures. That doesn't mean it is good practice or commonly accepted. Once something is not commonly used anymore, it becomes much more difficult to tell whether or not it is being used according to "good practice" standards, because there essentially is no more practice.


It is certainly true that the programmer should avoid making any calls that could possibly throw exceptions. Even if you have good exception support in the language, you still have to deal with handling the exceptional situation in your own code. Just throwing an exception (or even an pre-check) for a value out of range may not be adequate. How did the value get out of range? What does that mean for all of the other code in the app? Maybe a graceful termination is the best, general-case approach. Personally, I like to try to do everything I can to recover and return the app to a stable state. But that usually requires thinking about how exceptions are handled throughout the entire architecture of the system.

NSTouchBar Catalog. In MasterViewController.m

NSTouchBar Catalog. In MasterViewController.m

Ta.

Honestly I think this is just a bug in the sample. AFAICT, looking at the logic of that project, there’s no way that this call to

-removeObserver:forKeyPath:context:
could ever succeed, because no one ever actually adds an observer for that key path (by calling
-addObserver:forKeyPath:options:context:
). You can search the entire project for
forKeyPath
and you’ll find that this is the only hit!

I’ve filed my own bug report about this (r. 30803512).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
@try @catch in Apple's Objective-C sample code?
 
 
Q