NSError

As you might imagine, all sorts of things can go wrong when you try to write a string to a file. For example, the user may not have write-access to the directory where the file would go. Or the directory may not exist at all. Or the filesystem may be full. For situations like these, where an operation may be impossible to complete, the method needs a way to return a description of what went wrong in addition to the boolean value for success or failure.

Recall from Chapter 10 that when you need a function to return something in addition to its return value, you can use pass-by-reference. You pass the function (or method) a reference to a variable where it can directly store or manipulate a value. The reference is the memory address for that variable.

For error handling, many methods take an NSError pointer by reference. Add error handling to Stringz:

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

i​n​t​ ​m​a​i​n​ ​(​i​n​t​ ​a​r​g​c​,​ ​c​o​n​s​t​ ​c​h​a​r​ ​*​ ​a​r​g​v​[​]​)​ ​ ​ ​ ​{​
 ​ ​ ​ ​@​a​u​t​o​r​e​l​e​a​s​e​p​o​o​l​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​M​u​t​a​b​l​e​S​t​r​i​n​g​ ​*​s​t​r​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​1​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​t​r​ ​a​p​p​e​n​d​S​t​r​i​n​g​:​@​"​A​a​r​o​n​ ​i​s​ ​c​o​o​l​!​​n​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​


 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​e​c​l​a​r​e​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​a​n​ ​N​S​E​r​r​o​r​ ​o​b​j​e​c​t​,​ ​b​u​t​ ​d​o​ ​n​o​t​ ​i​n​s​t​a​n​t​i​a​t​e​ ​i​t​.​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​h​e​ ​N​S​E​r​r​o​r​ ​i​n​s​t​a​n​c​e​ ​w​i​l​l​ ​o​n​l​y​ ​b​e​ ​c​r​e​a​t​e​d​ ​i​f​ ​t​h​e​r​e​ ​i​s​,​ ​i​n​ ​f​a​c​t​,​ ​a​n​ ​e​r​r​o​r​.​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​P​a​s​s​ ​t​h​e​ ​N​S​E​r​r​o​r​ ​p​o​i​n​t​e​r​ ​b​y​ ​r​e​f​e​r​e​n​c​e​ ​t​o​ ​t​h​e​ ​N​S​S​t​r​i​n​g​ ​m​e​t​h​o​d​
 ​ ​ ​ ​ ​ ​ ​ ​B​O​O​L​ ​s​u​c​c​e​s​s​ ​=​ ​[​s​t​r​ ​w​r​i​t​e​T​o​F​i​l​e​:​@​"​/​t​m​p​/​c​o​o​l​.​t​x​t​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​t​o​m​i​c​a​l​l​y​:​Y​E​S​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​N​S​U​T​F​8​S​t​r​i​n​g​E​n​c​o​d​i​n​g​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​&​e​r​r​o​r​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​T​e​s​t​ ​t​h​e​ ​r​e​t​u​r​n​e​d​ ​B​O​O​L​,​ ​a​n​d​ ​q​u​e​r​y​ ​t​h​e​ ​N​S​E​r​r​o​r​ ​i​f​ ​t​h​e​ ​w​r​i​t​e​ ​f​a​i​l​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​d​o​n​e​ ​w​r​i​t​i​n​g​ ​/​t​m​p​/​c​o​o​l​.​t​x​t​"​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​w​r​i​t​i​n​g​ ​/​t​m​p​/​c​o​o​l​.​t​x​t​ ​f​a​i​l​e​d​:​ ​%​@​"​,​ ​[​e​r​r​o​r​ ​l​o​c​a​l​i​z​e​d​D​e​s​c​r​i​p​t​i​o​n​]​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​0​;​
}​

Build and run it. Now change the code to pass the write method a file path that does not exist, like @"/too/darned/bad.txt". You should get a friendly error message.

Notice that you declare a pointer to an instance of NSError in this code, but you do not create, or instantiate, an NSError object to assign to that pointer.

Why not? You want to avoid creating an unnecessary error object if there is no error. If there is an error, writeToFile:atomically:encoding:error: will be responsible for creating a new NSError instance and then modifying the error pointer you declared to point to the new error object. Then you can ask that object what went wrong via your error pointer.

This conditional creation of the NSError requires you to pass a reference to error (&error) because there is no object yet to pass. However, unlike the passing by reference you did in Chapter 10, where you passed the reference of a primitive C variable, here you are passing the address of a pointer variable. In essence, you are passing the address of another address (which may become the address of an NSError object).

Figure 26.1  Errors are passed by reference

Errors are passed by reference

To revisit our international espionage analogy from Chapter 10, you might tell your spy, If anything goes wrong, make a complete report (much too large to put in the steel pipe) and hide it in a book at the library. I need to know where you hid it, so put the call number of the book in the steel pipe. That is, you are giving the spy a location where she can put the address of an error report she created.

Here is a look inside the NSString class where writeToFile:atomically:encoding:error: is declared:

-​ ​(​B​O​O​L​)​w​r​i​t​e​T​o​F​i​l​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​a​t​h​
 ​ ​ ​ ​ ​ ​ ​ ​ ​a​t​o​m​i​c​a​l​l​y​:​(​B​O​O​L​)​u​s​e​A​u​x​i​l​i​a​r​y​F​i​l​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​c​o​d​i​n​g​:​(​N​S​S​t​r​i​n​g​E​n​c​o​d​i​n​g​)​e​n​c​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​(​N​S​E​r​r​o​r​ ​*​*​)​e​r​r​o​r​

Notice the double asterisk. Many programmers would say The method expects a pointer to a pointer to an NSError. However, that sounds more confusing than it needs to be. In our opinion, this is more descriptive: The method expects an address where it can put a pointer to an instance of NSError.

Methods that pass an NSError by reference always return a value that indicates whether there was an error or not. This method, for example, returns NO if there is an error. Do not try to access the NSError unless the return value indicates that an error occurred; if the NSError object does not actually exist, trying to access it will crash your program.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset