57
Fake It ’Til You Make It: Tips and Tricks for Improving Interface Responsiveness Jonathan Saggau (@jonmarimba) Sounds broken inc

103 Optimizing Data Caching for iPhone App Responsiveness

Embed Size (px)

DESCRIPTION

103 Optimizing Data Caching for iPhone App Responsiveness Tuesday, Sept. 28, 8:30 — 9:45 am San Jose

Citation preview

Page 1: 103 Optimizing Data Caching for iPhone App Responsiveness

Fake It ’Til You Make It: Tips and Tricks for Improving

Interface Responsiveness

Jonathan Saggau (@jonmarimba)Sounds broken inc

Page 2: 103 Optimizing Data Caching for iPhone App Responsiveness

Da book

Page 3: 103 Optimizing Data Caching for iPhone App Responsiveness

The dude

Page 4: 103 Optimizing Data Caching for iPhone App Responsiveness

“Speed is money. How much do you want to spend?”

- Old auto racing adage

Page 5: 103 Optimizing Data Caching for iPhone App Responsiveness

“Speed is developer time. How much do you have left to spend before release?”

Recall the keynote: "Good cooking takes time. If you are made to wait, it is to serve you better, and to please you."

Page 6: 103 Optimizing Data Caching for iPhone App Responsiveness

“Speed is developer time. How much do you have left to spend before release?”

Recall the keynote: "Good cooking takes time. If you are made to wait, it is to serve you better, and to please you."

Yup. This is the car my dad drives. 2009 Ford Shelby GT-500. He is,

so far, still with us.

Page 7: 103 Optimizing Data Caching for iPhone App Responsiveness

“death by 1,000 paper cuts”

Displaying Large Amounts of Data Efficiently

Page 8: 103 Optimizing Data Caching for iPhone App Responsiveness

The inspiration

Page 9: 103 Optimizing Data Caching for iPhone App Responsiveness

The inspiration(UIWebView is teh suck)

Page 10: 103 Optimizing Data Caching for iPhone App Responsiveness

The inspiration(UIWebView is teh suck*)

*Actually, it’s pretty cool. It just doesn’t always stand up under heavy usage.

Page 11: 103 Optimizing Data Caching for iPhone App Responsiveness

The inspiration(UIWebView is teh suck*)

Page 12: 103 Optimizing Data Caching for iPhone App Responsiveness

Demo project: BigViewThing.app

Page 14: 103 Optimizing Data Caching for iPhone App Responsiveness

Demoor... Julia Child impression

Page 15: 103 Optimizing Data Caching for iPhone App Responsiveness

Demoor... Julia Child impression

This might stink like that fish over there.

I swear that’s on purpose.

Page 16: 103 Optimizing Data Caching for iPhone App Responsiveness

!

I’m doing everything the dumb, slow way.

TODO: Replace this with Instruments runTODO: Add a note that shark is dead.

Page 17: 103 Optimizing Data Caching for iPhone App Responsiveness

!

Drawing UI in a background thread.

Page 18: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

Danger, Will Robinson! UIKit is not thread-safe. Try to draw to screen from another thread, and bad things might happen. Ugly things are almost guaranteed to happen. You can, however, draw your images into off-screen buffers (cgContexts work here) and then grab the pixels that you need to throw on the screen once the buffer is filled with data. There is nothing stopping you from

filling that data asynchronously and reading it from the main thread in order to draw it.

Page 19: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

1. The first time one of the BigViewPageView objects is asked to draw, it will create a cgContext into which it will quickly draw a half

opaque white background as a placeholder when the BigViewPageView is inactive.

Page 20: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

II. It will draw whatever is currently in the off-screen context to the screen in drawRect:

Page 21: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

III. It will generate an NSOperation that will fill a new cgContext in a background thread with the image data as it is needed.

Page 22: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

IV. When the NSOperation finishes, it will swap the new buffer in and call setNeedsDisplay on the view in the main thread so the view

knows to draw the image data to screen. Drawing from a buffer is fast(er).

Page 23: 103 Optimizing Data Caching for iPhone App Responsiveness

Drawing UI in a background thread.

V. Any time the BigViewPageView is asked to draw, it pulls the image data from the current cgContext for drawing; it’s also filling new

cgContexts in the background if you change the expected drawing size of the image through some bizarre action like zooming. Before the new buffer is ready, your image will stretch to fill and probably

pixelate for a moment while the NSOperation is preparing new data much like UIWebView.

Page 24: 103 Optimizing Data Caching for iPhone App Responsiveness

NSOperation and NSOperationQueue

NSOperationQueue and NSOperation remove much of the pain of multithreading. NSOperationQueue is a sort of thread pool that

maintains an array of pending NSOperation objects that it schedules to run in a background thread based on a number of factors from system hardware to system state to the relative priority of a given

NSOperation.

Page 25: 103 Optimizing Data Caching for iPhone App Responsiveness

Playing with ^{blocks()}

“Cause it’s gonna be the future soon. And I won't always be this way. When the things that make me weak and strange get engineered away.”

—Lyrics for The Future Soon by Jonathan Coulton

Page 26: 103 Optimizing Data Caching for iPhone App Responsiveness

We used to need a patched Compiler Plugin For Xcode

and a runtime

!

Now we can target iOS 4.0*

Page 27: 103 Optimizing Data Caching for iPhone App Responsiveness

You kids have it so easy.

Page 28: 103 Optimizing Data Caching for iPhone App Responsiveness

Demo

Page 29: 103 Optimizing Data Caching for iPhone App Responsiveness

I’m doing everything the dumb, slow way.In a background thread.

So the user can still use the app without freezing - up.

Page 30: 103 Optimizing Data Caching for iPhone App Responsiveness

UIScrollView Zooming

!

Content Offset

ContentView

UIScrollView

When zooming, Images Get Stretched

(affine transform)read: pixellated.

Blech.

Page 31: 103 Optimizing Data Caching for iPhone App Responsiveness

As the zoom scale changes, the UIScrollView does two things:by default

1. It sets an affine transform on the view it is zooming to scale it up or down without redrawing. It’s a “square” transform that maintains aspect ratio, so there

is no distortion.

2. It resets its own contentSize, contentOffset, and zoomScale so as to hold the content in place relative to the point about which it is zooming (in the case of

pinching, that point was halfway between your fingers).

We have to do some work to get the pixellated images to redraw at their new size.

Page 32: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

Here is how I prefer to do this.It’s the only way I can get my head around.

Page 33: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

1. Take a snapshot of the current (scaled) contentSize and contentOffset.

Page 34: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

1. Take a snapshot of the current (scaled) contentSize and contentOffset.

2. Take a snapshot of the current (unscaled) content view’s frame size; it’s being scaled by an affine transform, so its actual frame size is the

same as it was before zooming.

Page 35: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

1. Take a snapshot of the current (scaled) contentSize and contentOffset.

2. Take a snapshot of the current (unscaled) content view’s frame size; it’s being scaled by an affine transform, so its actual frame size is the

same as it was before zooming.

3. Take a snapshot of the current minimum and maximum zoom scales.

Page 36: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

2. Take a snapshot of the current (unscaled) content view’s frame size; it’s being scaled by an affine transform, so its actual frame size is the

same as it was before zooming.

3. Take a snapshot of the current minimum and maximum zoom scales.

4. If your scroll view is its own delegate as it is in BigViewThing, call super to set the minimum and

maximum zoom scales both to 1.0 because setting zoom on self will eventually call updateResolution

again; infinite recursion is so last year.

Page 37: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

3. Take a snapshot of the current minimum and maximum zoom scales.

4. If your scroll view is its own delegate as it is in BigViewThing, call super to set the minimum and

maximum zoom scales both to 1.0 because setting zoom on self will eventually call updateResolution

again; infinite recursion is so last year.

5. Set the current zoom scale to 1.0, which will rescale the content size internally back to the size of the content view, and reset the affine transform

on the content view to unity.

Page 38: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

5. Set the current zoom scale to 1.0, which will rescale the content size internally back to the size of the content view, and reset the affine transform

on the content view to unity.

6. Calculate new content offset by scaling the stretched/zoomed offset you took a snapshot of in step 1. You want the new content to appear in the same place in the scroll

view:

Page 39: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

6. Calculate new content offset by scaling the stretched/zoomed offset you took a snapshot of in step 1. You want the new content to appear in the same place in the scroll

view:7. newContentOffset.x *= (oldContentSize.width /

contentViewSize.width);8. newContentOffset.y *= (oldContentSize.height /

contentViewSize.height);

Page 40: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

7. newContentOffset.x *= (oldContentSize.width / contentViewSize.width);

8. newContentOffset.y *= (oldContentSize.height / contentViewSize.height);

9. Divide the old minimum and maximum zoomScale by the new zoom scale. This scales the original minimum and

maximum zooms relative to the new content size. If minimum zoom were 1.0 and maximum zoom were 2.0,

when the user zooms to 2.0 and I reset, my new minimum zoom would be .5, and my new maximum zoom would be

1.0.

Page 41: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

9. Divide the old minimum and maximum zoomScale by the new zoom scale. This scales the original minimum and

maximum zooms relative to the new content size. If minimum zoom were 1.0 and maximum zoom were 2.0,

when the user zooms to 2.0 and I reset, my new minimum zoom would be .5, and my new maximum zoom would be

1.0.

10. Set the content view’s frame.size to the contentSize you took a snapshot of in step 1.

Page 42: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

10. Set the content view’s frame.size to the contentSize you took a snapshot of in step 1.

11. Set the scroll view’s contentSize to the scaled contentSize you took a snapshot of in step 1. This stretches the overall size of the view to match the new zoom level

(but without any affine transform applied).

Page 43: 103 Optimizing Data Caching for iPhone App Responsiveness

Resetting Resolution in a UIScrollView after a Zoom Operation

10. Set the content view’s frame.size to the contentSize you took a snapshot of in step 1.

11. Set the scroll view’s contentSize to the scaled contentSize you took a snapshot of in step 1. This stretches the overall size of the view to match the new zoom level

(but without any affine transform applied).

12. Call the setNeedsLayout method on the scroll view. This will cause layoutSubviews to be called where you can

reset the content view’s internal subview geometry.

Page 44: 103 Optimizing Data Caching for iPhone App Responsiveness

Demo

Page 45: 103 Optimizing Data Caching for iPhone App Responsiveness

Why do it this way?

Page 46: 103 Optimizing Data Caching for iPhone App Responsiveness

NSOperation Cancellation

- (void)cancelOperationsFilteredByPredicate:(NSPredicate *)predicate;{ NSArray *ops = [[self operations] filteredArrayUsingPredicate:predicate]; for (NSOperation *op in ops) { if(![op isExecuting] && ![op isFinished] && ![op isCancelled]) { [op cancel]; } }}

In a category on NSOperationQueue:

We can use this to cancel old, not yet performed, redraws for when the user is zooming in and out rapidly.

Page 47: 103 Optimizing Data Caching for iPhone App Responsiveness

Demo

Page 48: 103 Optimizing Data Caching for iPhone App Responsiveness

You can find more information on NSPredicate in Apple’s Predicate Programming Guide available at http://

developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html.

Page 49: 103 Optimizing Data Caching for iPhone App Responsiveness

Caching tradeoffs

• Staleness (cached data is old data)

• User interface responsiveness (cached data is faster data)

• Memory usage (cached data takes up space somewhere)

Page 50: 103 Optimizing Data Caching for iPhone App Responsiveness

Different types of iPhone apps require different strategies

• Where does your data live?

• On-disk read-only

• On-disk read-write

• Client-server read-only

• Client-server read-write

Page 51: 103 Optimizing Data Caching for iPhone App Responsiveness

Data Request Overhead

• On-disk

• Cloud data

• How much data is optimal per request?

• Parsing and memory overhead (why NSXMLDocument doesn't exist on the iPhone)

• Limited memory workspace - working in chunks

• How to distribute pauses in UI responsiveness as a result of requesting data to avoid giving the appearance of a "slow" application

Page 52: 103 Optimizing Data Caching for iPhone App Responsiveness

If it might take a while (you did use Shark, right?)...

Stay off the main thread. Please.

Page 54: 103 Optimizing Data Caching for iPhone App Responsiveness

Look for gogoDocs Google Docs reader for iPhone and iPad. It’s in an

the app store in your pocket.(Pay for my flight, please)

[email protected]://jonathansaggau.com/blog

twit: @jonmarimba

Page 55: 103 Optimizing Data Caching for iPhone App Responsiveness

[email protected]://jonathansaggau.com/blog

twit: @jonmarimba

Nerds for hire // Hiring nerds // Working with Big Nerds

Will Code for food.

Page 57: 103 Optimizing Data Caching for iPhone App Responsiveness

Next up...Meed, um, me.

Apress authors gonna be right over there >