Helper objects

Timers are simple. They only do one thing: fire. Thus, target-action is a good fit. A lot of simple user interface controls, like buttons and sliders, also use the target-action mechanism. What about something more complex?

In Chapter 23, you used an NSURLConnection method to fetch data from a web server. It worked fine, but there are two problems with the method:

  • It blocks the main thread while waiting for all the data to arrive. If you used this method in a real application, the user interface would become unresponsive while the data was fetched.

  • It has no way to callback if, for example, the webserver demands a username and password.

For these reasons, we typically use an NSURLConnection asynchronously. That is, we start it fetching and then await callbacks as the data arrives or the web server demands credentials or the fetch fails.

How do you get these callbacks? You supply the NSURLConnection with a helper object. When things happen, the connection sends messages to the helper object. What messages? The guy who wrote NSURLConnection made up a protocol – a list of method declarations – that the helper object can implement. Here are some of the methods in that protocol:

-​ ​(​N​S​U​R​L​R​e​q​u​e​s​t​ ​*​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​c​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​l​l​S​e​n​d​R​e​q​u​e​s​t​:​(​N​S​U​R​L​R​e​q​u​e​s​t​ ​*​)​r​e​q​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​d​i​r​e​c​t​R​e​s​p​o​n​s​e​:​(​N​S​U​R​L​R​e​s​p​o​n​s​e​ ​*​)​r​e​s​;​

-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​s​e​n​d​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​d​i​d​R​e​c​e​i​v​e​A​u​t​h​e​n​t​i​c​a​t​i​o​n​C​h​a​l​l​e​n​g​e​:​(​N​S​U​R​L​A​u​t​h​e​n​t​i​c​a​t​i​o​n​C​h​a​l​l​e​n​g​e​ ​*​)​c​h​;​

-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​s​e​n​d​e​r​
 ​ ​ ​ ​d​i​d​R​e​c​e​i​v​e​D​a​t​a​:​(​N​S​D​a​t​a​ ​*​)​d​a​t​a​;​

-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​D​i​d​F​i​n​i​s​h​L​o​a​d​i​n​g​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​s​e​n​d​e​r​;​

-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​s​e​n​d​e​r​ ​d​i​d​F​a​i​l​W​i​t​h​E​r​r​o​r​:​(​N​S​E​r​r​o​r​ ​*​)​e​r​r​o​r​;​

-​ ​(​N​S​C​a​c​h​e​d​U​R​L​R​e​s​p​o​n​s​e​ ​*​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​s​e​n​d​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​l​l​C​a​c​h​e​R​e​s​p​o​n​s​e​:​(​N​S​C​a​c​h​e​d​U​R​L​R​e​s​p​o​n​s​e​ ​*​)​c​a​c​h​e​d​R​e​s​p​o​n​s​e​;​

As you can see, an NSURLConnection lives a much richer life than an NSTimer. Now you are going to create an object that implements some or all of these methods and then introduce that object to the NSURLConnection as its helper object. In particular, the NSURLConnection has a pointer called delegate.

Figure 24.4  Logger is the delegate of the NSURLConnection

Logger is the delegate of the NSURLConnection

In main(), create an NSURLConnection and set the instance of Logger to be its delegate:

#​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​m​p​o​r​t​ ​"​L​o​g​g​e​r​.​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​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​L​o​g​g​e​r​ ​*​l​o​g​g​e​r​ ​=​ ​[​[​L​o​g​g​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​ ​*​u​r​l​ ​=​ ​[​N​S​U​R​L​ ​U​R​L​W​i​t​h​S​t​r​i​n​g​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​"​h​t​t​p​:​/​/​w​w​w​.​g​u​t​e​n​b​e​r​g​.​o​r​g​/​c​a​c​h​e​/​e​p​u​b​/​2​0​5​/​p​g​2​0​5​.​t​x​t​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​U​R​L​R​e​q​u​e​s​t​ ​*​r​e​q​u​e​s​t​ ​=​ ​[​N​S​U​R​L​R​e​q​u​e​s​t​ ​r​e​q​u​e​s​t​W​i​t​h​U​R​L​:​u​r​l​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​_​_​u​n​u​s​e​d​ ​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​f​e​t​c​h​C​o​n​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​=​ ​[​[​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​R​e​q​u​e​s​t​:​r​e​q​u​e​s​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​e​l​e​g​a​t​e​:​l​o​g​g​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​t​a​r​t​I​m​m​e​d​i​a​t​e​l​y​:​Y​E​S​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​_​_​u​n​u​s​e​d​ ​N​S​T​i​m​e​r​ ​*​t​i​m​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​=​ ​[​N​S​T​i​m​e​r​ ​s​c​h​e​d​u​l​e​d​T​i​m​e​r​W​i​t​h​T​i​m​e​I​n​t​e​r​v​a​l​:​2​.​0​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​a​r​g​e​t​:​l​o​g​g​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​l​e​c​t​o​r​:​@​s​e​l​e​c​t​o​r​(​s​a​y​O​u​c​h​:​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​I​n​f​o​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​p​e​a​t​s​:​Y​E​S​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​R​u​n​L​o​o​p​ ​c​u​r​r​e​n​t​R​u​n​L​o​o​p​]​ ​r​u​n​]​;​

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

The instance of Logger will need an instance of NSMutableData to hold onto the bytes as they arrive. Add an instance variable to Logger.h:

#​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​e​r​f​a​c​e​ ​L​o​g​g​e​r​ ​:​ ​N​S​O​b​j​e​c​t​ ​{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​*​i​n​c​o​m​i​n​g​D​a​t​a​;​
}​
-​ ​(​v​o​i​d​)​s​a​y​O​u​c​h​:​(​N​S​T​i​m​e​r​ ​*​)​t​;​
@​e​n​d​

Now implement some of the delegate methods in Logger.m:

#​i​m​p​o​r​t​ ​"​L​o​g​g​e​r​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​L​o​g​g​e​r​

-​ ​(​v​o​i​d​)​s​a​y​O​u​c​h​:​(​N​S​T​i​m​e​r​ ​*​)​t​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​O​u​c​h​!​"​)​;​
}​

/​/​ ​C​a​l​l​e​d​ ​e​a​c​h​ ​t​i​m​e​ ​a​ ​c​h​u​n​k​ ​o​f​ ​d​a​t​a​ ​a​r​r​i​v​e​s​
-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​c​o​n​n​e​c​t​i​o​n​
 ​ ​ ​ ​d​i​d​R​e​c​e​i​v​e​D​a​t​a​:​(​N​S​D​a​t​a​ ​*​)​d​a​t​a​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​r​e​c​e​i​v​e​d​ ​%​l​u​ ​b​y​t​e​s​"​,​ ​[​d​a​t​a​ ​l​e​n​g​t​h​]​)​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​m​u​t​a​b​l​e​ ​d​a​t​a​ ​i​f​ ​i​t​ ​d​o​e​s​n​'​t​ ​a​l​r​e​a​d​y​ ​e​x​i​s​t​
 ​ ​ ​ ​i​f​ ​(​!​i​n​c​o​m​i​n​g​D​a​t​a​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​n​c​o​m​i​n​g​D​a​t​a​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​D​a​t​a​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​i​n​c​o​m​i​n​g​D​a​t​a​ ​a​p​p​e​n​d​D​a​t​a​:​d​a​t​a​]​;​
}​

/​/​ ​C​a​l​l​e​d​ ​w​h​e​n​ ​t​h​e​ ​l​a​s​t​ ​c​h​u​n​k​ ​h​a​s​ ​b​e​e​n​ ​p​r​o​c​e​s​s​e​d​
-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​D​i​d​F​i​n​i​s​h​L​o​a​d​i​n​g​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​c​o​n​n​e​c​t​i​o​n​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​G​o​t​ ​i​t​ ​a​l​l​!​"​)​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​s​t​r​i​n​g​ ​=​ ​[​[​N​S​S​t​r​i​n​g​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​D​a​t​a​:​i​n​c​o​m​i​n​g​D​a​t​a​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​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​]​;​
 ​ ​ ​ ​i​n​c​o​m​i​n​g​D​a​t​a​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​s​t​r​i​n​g​ ​h​a​s​ ​%​l​u​ ​c​h​a​r​a​c​t​e​r​s​"​,​ ​[​s​t​r​i​n​g​ ​l​e​n​g​t​h​]​)​;​

 ​ ​ ​ ​/​/​ ​U​n​c​o​m​m​e​n​t​ ​t​h​e​ ​n​e​x​t​ ​l​i​n​e​ ​t​o​ ​s​e​e​ ​t​h​e​ ​e​n​t​i​r​e​ ​f​e​t​c​h​e​d​ ​f​i​l​e​
 ​ ​ ​ ​/​/​ ​N​S​L​o​g​(​@​"​T​h​e​ ​w​h​o​l​e​ ​s​t​r​i​n​g​ ​i​s​ ​%​@​"​,​ ​s​t​r​i​n​g​)​;​

}​

/​/​ ​C​a​l​l​e​d​ ​i​f​ ​t​h​e​ ​f​e​t​c​h​ ​f​a​i​l​s​
-​ ​(​v​o​i​d​)​c​o​n​n​e​c​t​i​o​n​:​(​N​S​U​R​L​C​o​n​n​e​c​t​i​o​n​ ​*​)​c​o​n​n​e​c​t​i​o​n​
 ​ ​d​i​d​F​a​i​l​W​i​t​h​E​r​r​o​r​:​(​N​S​E​r​r​o​r​ ​*​)​e​r​r​o​r​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​c​o​n​n​e​c​t​i​o​n​ ​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​]​)​;​
 ​ ​ ​ ​i​n​c​o​m​i​n​g​D​a​t​a​ ​=​ ​n​i​l​;​
}​


@​e​n​d​

Notice that you didn’t implement all the methods in the protocol – just the ones that you cared about.

Build and run the program. You should see that the data comes from the web server in reasonable sized chunks. Eventually, the delegate gets informed that the fetch is complete.

Here are the rules, so far, for callbacks: When sending one callback to one object, Apple uses target-action. When sending an assortment of callbacks to one object, Apple uses a helper object with a protocol. (We’ll talk more about protocols in the next chapter.) These helper objects are typically called delegate or data source.

What if the callback needs to go to multiple objects?

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

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