`
啸笑天
  • 浏览: 3434956 次
  • 性别: Icon_minigender_1
  • 来自: China
社区版块
存档分类
最新评论

25 iOS App Performance Tips & Tricks

 
阅读更多

from http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks

 

This is a post by iOS Tutorial Team Member Marcelo Fabri, an iOS developer working at Movile. Check out his personal websiteor follow him on Twitter.

When developing an iOS app, it’s critical that your app has good performance. Your users expect it, and it will hurt your reviews if your app appears unresponsive or slow.

However, due to the limitations of iOS devices this can sometimes be quite hard to get working quite right. There’s a lot to keep in mind during development, and it’s easy to forget the performance impact of your decisions.

That is exactly why I wrote this article! This article gathers together 25 tips and tricks that you can use to improve the performance of your apps, in the form of a handy checklist.

So keep reading to give your future apps a nice boost!

Note: Before optimizing any code, make sure there’s a problem to be solved! Don’t get sucked into the mistake of “pre-optimizing” your code. Use Instruments frequently to profile your code and uncover any areas that need improvement. Matt Galloway wrote a great tutorial about using Instruments to optimize your code.

Also, keep in mind that some of the tips offered in this article offered are trade-offs; the suggested improvements will make your code faster or more efficient, but they may require a lot of work to implement, or make your code more complicated, so choose wisely!

 

Table of Contents

The tips below are categorized into three different levels – beginner, intermediate and advanced:

Beginner

These are tips that you’ll always want to implement in any app you develop.

  1. Use ARC to Manage Memory
  2. Use a reuseIdentifier Where Appropriate
  3. Set Views as Opaque When Possible
  4. Avoid Fat XIBs
  5. Don’t Block the Main Thread
  6. Size Images to Image Views
  7. Choose the Correct Collection
  8. Enable GZIP Compression

Intermediate

These are tips you should use when you run into slightly more complicated issues.

  1. Reuse and Lazy Load Views
  2. Cache, Cache, Cache
  3. Consider Drawing
  4. Handle Memory Warnings
  5. Reuse Expensive Objects
  6. Use Sprite Sheets
  7. Avoid Re-Processing Data
  8. Choose the Right Data Format
  9. Set Background Images Appropriately
  10. Reduce Your Web Footprint
  11. Set the Shadow Path
  12. Optimize Your Table Views
  13. Choose Correct Data Storage Option

Advanced

These are tips you should use only when you’re positive they’ll fix the issue, and you feel comfortable using them.

  1. Speed up Launch Time
  2. Use Autorelease Pool
  3. Cache Images – Or Not
  4. Avoid Date Formatters Where Possible

Without further ado, let’s get into the tips!

Beginner Performance Improvements

This section is dedicated to basic changes that can improve your app’s performance. But developers of all levels will still benefit from this quick checklist of items that are still sometimes overlooked.

 
1) Use ARC to Manage Memory

ARC was released with iOS 5 and it eliminates the most common kind of memory leaks – the forgetful ones.

ARC stands for “Automatic Reference Counting”, and it automatically manages the retain/release cycles in your code, so you don’t have to do it manually.

The code block below shows some common code that you might use to create a view:

UIView *view = [[UIView alloc] init];
// ...
[self.view addSubview:view];
[view release];

It’s tremendously easy to forget the release call at the end of this code block. ARC does it for you, automatically and under-the-hood.

In addition to helping you avoid memory leaks, ARC can also improve your performance, by making sure that objects are deallocated as soon as they are no longer needed. These days, you should always use ARC in your projects!

Here are a few great resources to learn more about ARC:

It’s worth noting that ARC doesn’t eliminate all memory leaks. You can still have memory leaks, but they’ll mainly be due to blocks, retain cycles, poorly managed CoreFoundation objects (and C structures in general), or just really poorly written code.

There’s a very good blog post that details some of the issues that ARC can’t fix — and how to deal with them.

 
2) Use a reuseIdentifier Where Appropriate



 

Use a reuseIdentifier Where Appropriate.

A common mistake in app development is not setting the correct reuseIdentifier for UITableViewCells, for UICollectionViewCells, or even UITableViewHeaderFooterViews.

For maximum performance, a table view’€™s data source should generally reuse UITableViewCell objects when it assigns cells to rows in tableView:cellForRowAtIndexPath:. A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse.

What happens if you don’t use a reuseIdentifier?

If you don’t, your table view will configure a brand-new cell each time a row is displayed. This is an expensive operation and will definitely affect the scrolling performance of your app.

Since the introduction of iOS 6, you’re expected to use reuseIdentifiers for header and footer views, as well as UICollectionView’s cells and supplementary views.

To use reuseIdentifiers, call this method from your data source object when asked to provide a new cell for the table view:

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

This method dequeues an existing cell if one is available, or creates a new one if necessary using the previously registered nib file or class. If no cell is available for reuse, and you did not register a class or nib file, this method returns nil.

 
3) Set Views as Opaque When Possible



 

Set Views as Opaque When Possible.

If you have opaque views — that is, views that have no transparency defined — you should set their opaque property to YES.

Why? This will allow the system to draw your views in an optimal manner. It’s a simple property that can be set in both Interface Builder and code.

The Apple documentation has this to say about setting the opaque property for images:

This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.

On relatively static screens, setting the opaque property won’t be a big deal. However, if your view is embedded in a scroll view, or is part of a complex animation, not setting this property will definitely impact the performance of your app!

You can also use the Debug\Color Blended Layers option in the simulator to see visually what views are not set as opaque. Your goal should be to make as many of your views opaque as possible!

 
4) Avoid Fat XIBs



 

Avoid Fat XIBs.

Storyboards, introduced in iOS 5, are quickly replacing XIBs. However, XIBs are still useful in some cases. If you need to target pre-iOS 5 devices, or you have a custom reusable view, then you can’t really avoid them.

If you’re forced into using XIBs, make them as uncomplicated as possible. Try to create one XIB per view controller, and if possible, break out a view controller’s view hierarchy into separate XIBs.

Note that when you load a XIB into memory, all of its contents are loaded into memory, including any images. If you have a view you’re not using immediately, then you’re wasting precious memory. It’s worth noting that this won’t happen with storyboards, since a storyboard will only instantiate a view controller when it’s needed.

When you load a XIB, any image files are cached, along with sound files if you’re developing for OS X.Apple’s documentation has this to say:

When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.

Apparently, this also happens when using storyboards; however, I wasn’t able to find anything supporting this claim. If you know anything about this behavior, please drop me a line!

Want to learn more about storyboards? Check out Matthijs Hollemans’ Beginning Storyboards in iOS 5 Part 1and Part 2.

 
5) Don’t Block the Main Thread



 

Don’t Block the Main Thread.

You should never do any heavy lifting on the main thread. This is because UIKit does all of its own work on the main thread, such as drawing, managing touches, and responding to input.

The risk of doing all of your app’s work on the main thread is that if your code does block this thread, your app will appear unresponsive. That’s a quick route to one-star reviews on the App Store! :]

Most cases of blocking the main thread occur when your app is performing an I/O operation which involves any task that needs to read or write from an external source, such as the disk or the network.

You can perform network tasks asynchronously by using this method on NSURLConnection:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

or by using a third party framework such as AFNetworking.

If you’re doing any other kind of expensive operation (such as performing a time-intensive computation or reading/writing to the disk) then use Grand Central Dispatch, or NSOperations and NSOperationQueues.

The template for using GCD looks like the code below:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // switch to a background thread and perform your expensive operation
 
    dispatch_async(dispatch_get_main_queue(), ^{
        // switch back to the main thread to update your UI
 
    });
});

See how there’s a nested dispatch_async inside the first one? That’s because any UIKit related code needs to be executed on the main thread.

Curious about the finer details of NSOperation or GCD? Take a look at Ray Wenderlich’s Multithreading and Grand Central Dispatch on iOS for Beginners tutorial, as well as Soheil Azarpour’s How To Use NSOperations and NSOperationQueues tutorial.

 
6) Size Images to Image Views



 

Size Images to Image Views.

If you’re displaying an image from the app’s bundle in a UIImageView, make sure that the image and the UIImageView are same size. Scaling images on the fly can be expensive, especially if your UIImageView is embedded in a UIScrollView.

If the image is downloaded from a remote service, sometimes you don’t have control over the size, or you might not be able to scale it on the server prior to downloading. In cases like these, you can scale the image manually once you’ve finish downloading it — preferably on a background thread! — and then use the resized image in your UIImageView.

 
7) Choose the Correct Collection



 

Choose the Correct Collection.

Learning to use the most appropriate class or object for the task at hand is fundamental to writing efficient code. This is especially true when dealing with collections.

Happily, there’s a document named Collections Programming Topics on Apple’s Developer Library that explains in detail the differences between the available classes, as well as which situations suit each class. It’s a must read document for anyone looking to work with collections.

TLDR? Here’s a quick synopsis of the most common collection types:

  • Arrays: Ordered list of values. Quick lookup by index, slow to lookup by value, slow to insert/delete.
  • Dictionaries: Store key/value pairs. Quick lookups by key.
  • Sets: Unordered list of values. Quick lookup by value, quick to insert/delete.

 

8) Enable GZIP Compression



 

Enable GZIP compression.

A significant and growing number of apps rely on external data from remote servers or other external APIs. At some point you’ll be developing an app that downloads data in XML, JSON, HTML or some other text format.

The problem is that the network condition cannot be relied upon when it comes to mobile devices. A user can be on an EDGE network one minute, and the a 3G network the next. Whatever the scenario, you don’t want to keep your user waiting!

One option to reduce the file size and speed up the download of network-based resources is by enabling GZIP compression on both your server and on your client. This is especially useful for text-based data, which has a high potential ratio for compression.

The good news is that iOS already supports GZIP compression by default if you’re using NSURLConnection, or a framework built on top of it such as AFNetworking. Even more good news is that some cloud providers, such as Google App Engine already send compressed responses.

There’s a great article about GZIP compression which explains how to enable it on your Apache or IIS server.

Intermediate Performance Improvements

Okay, so you’re pretty confident that you’ve hit all of the low-hanging fruit when it comes to optimizing your code. But sometimes there are solutions that aren’t quite as obvious, and depend heavily on how you structure and code your app. However, in the right context, they can be invaluable!

 
9) Reuse and Lazy Load Views

More views means more drawing; which ultimately means more CPU and memory overhead. This is especially true if your app embeds many views inside of a UIScrollView.

The trick to managing this is to mimic the behavior of UITableView and UICollectionView: don’t create all subviews at once. Instead, create your views as you need them, adding them to a reuse queue when you’re finished with them.

This way, you have only to configure your views when a scroll is performed, avoiding the allocation cost — which can be expensive.

The problem of timing the creation of views applies to other areas of your app as well. Take the situation where you need to present a view when the user taps a button. There are at least two approaches to this:

  1. Create the view when the screen is first loaded and hide it; then when you need it, show it.
  2. Do nothing until you need to show the view. Then, create the view and show it, all at once.

Each approach has its own pros and cons.

Using the first method, you consume more memory because you immediately create the view which holds on to that memory until it’s released. However, when the user does taps the button, your app will appear more responsive as it only needs to change the view’s visibility.

Taking the second approach will have the opposite effect; by creating the view only when it’s required, you consume less memory; however, the app won’t appear as responsive when the button is tapped.

 
10) Cache, Cache, Cache

A great rule of thumb when developing your app is to “cache what matters” — that is, things that are unlikely to change, but are accessed frequently.

What can you cache? Some candidates for caching are remote server responses, images, or even calculated values, such as UITableView row heights.

NSURLConnection already caches resources on disk or in memory according to the HTTP headers it processes. You can even create a NSURLRequest manually and make it load only cached values.

Here’s a great snippet to use whenever you need to create a NSURLRequest for an image that is unlikely to change:

+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
 
    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image
    request.HTTPShouldHandleCookies = NO;
    request.HTTPShouldUsePipelining = YES;
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
 
    return request;
}

Note that you can fetch a URL request via NSURLConnection, but AFNetworking can fetch it as well; that way you don’t need to change all your networking code because of this tip! :]

If want to know more about HTTP caching, NSURLCache, NSURLConnection and friends, be sure to take a look at the NSURLCache entry on NSHipster.

If you need to cache other things that don’t involve HTTP requests, then NSCache is your go-to guy.

NSCache looks and behaves like an NSDictionary, but it automatically removes its contents when the system needs to reclaim memory. Mattt Thompson wrote this amazing post on NSHipster about it.

Interested in learning more about HTTP caching? Google has a best-practices document on HTTP cachingthat is a recommended read.

 
11) Consider Drawing



 

Consider Drawing.

There are several ways to make great-looking buttons in iOS. You can use full sized images, resizable images, or you could go the distance and draw it manually using CALayer, CoreGraphics or even OpenGL.

Of course, there’s different levels of complexity with each of these approaches, as well as differences in their performance. There’s an awesome post about iOS graphics performance herewhich is well worth a read. Andy Matuschak, who is a member of the UIKit team over at Apple, commented on the post, and there’s some great insight into the different approaches and their performance trade-offs.

The short story is that using pre-rendered images is faster, because iOS doesn’t have to create an image and draw shapes on it to finally draw into than screen (the image is already created!). The problem is that you need to put all those images in your app’s bundle, increasing its size. That’s why using resizable images is so great: you save space by removing “wasted” image space that iOS can repeat for you. You also don’t need to generate different images for different elements (e.g. buttons).

However, by using images you lose the ability to tweak your images by code, needing to regenerate them every and putting them into the app again and again. That can be a slow process. Another point is that if you have an animation or just a lot of images with slightly changes (they can have multiple overlay color, for example), you’ll have to embed a lot of images, growing the app’s bundle size.

To summarize, you need to think what’s most important to you: drawing performance or app’s size. Generally both are important, so you’ll use both approaches in the same project!

 
12) Handle Memory Warnings

iOS notifies all running apps when system memory is running low. Here’s what the official Apple documentation says about handling the low memory warning:

If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.

Fortunately, UIKit provides several ways to receive these low-memory warnings, including the following:

  • Implement the applicationDidReceiveMemoryWarning: method of your app delegate.
  • Override didReceiveMemoryWarning in your custom UIViewController subclass.
  • Register to receive the UIApplicationDidReceiveMemoryWarningNotification notification.

Upon receiving any of these warnings, your handler method should respond by immediately freeing up any unnecessary memory.

For example, the default behavior of UIViewController is to purge its view if that view is not currently visible; subclasses can supplement the default behavior of their parent class by purging additional data structures. An app that maintains a cache of images might respond by releasing any images that are not currently on-screen.

It’s very important to release all memory possible once you receive a memory warning. Otherwise, you run the risk of having your app killed by the system.

However, be careful when you start culling objects to free up memory, as you’ll need to make sure they can be recreated later. Be sure to use the Simulate memory warning feature on the iOS simulator to test this condition while you are developing your app!

 
13) Reuse Expensive Objects



 

Some objects are very slow to initialize — NSDateFormatter and NSCalendar are two examples. However, you can’t always avoid using them, such as when parsing dates from a JSON/XML response.

To avoid performance bottlenecks when working with these objects, try to reuse these objects if at all possible. You can do this by either adding a property to your class, or by creating a static variable.

Note that if you choose the second approach, the object will remain in memory while your app is running, much like a singleton.

The code below demonstrates making a property that lazy-loads a date formatter. The first time it is called, it creates a new date formatter. Future calls will just return the already created instance:

// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;
 
// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {
    if (! _formatter) {
        _formatter = [[NSDateFormatter alloc] init];
        _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
    }
    return _formatter;
}

Also remember that setting a NSDateFormatter’s date format is almost as slow as creating a new one! Therefore, if you frequently need to deal with multiple date formats in your app, your code may benefit from initially creating, and reusing, multiple NSDateFormatter objects.

 
14) Use Sprite Sheets



 

Use sprite sheets.

So you’re a game developer? Then sprite sheets are one of your best friends. Sprite sheets make drawing faster and can even consume less memory than standard screen drawing methods.

There are two awesome sprite sheet tutorials about sprite sheets on this site:

  1. How To Use Animations and Sprite Sheets in Cocos2D
  2. How to Create and Optimize Sprite Sheets in Cocos2D with Texture Packer and Pixel Formats

The second tutorial covers pixel formats in detail, which can have a measurable impact on a game’s performance.

If you’re not yet familar with sprite sheets, then a great introduction can be found in SpriteSheets – The Movie, Part 1and Part 2. The author of these videos is Andreas Löw, the creator of Texture Packer, one of the most popular tools for creating sprite sheets.

Besides using sprite sheets, several tips already covered here can be applied to games as well. For example, if your game has many sprites, such as enemies or projectiles in your standard shoot-em-up, then you can reuse sprites instead of recreating them every time.

 
15) Avoid Re-Processing Data

Many apps make calls for data from remote servers to get information the app requires to function. This data usually comes across in JSON or XML format. It’s important to try and use the same data structure at both ends when requesting and receiving the data.

Why? Manipulating data in memory to fit your data structures can be expensive.

For example, if you need to display the data in a table view, it would be best to request and receive the data in an array format to avoid any intermediate manipulation of the data to make it fit the data structure that you’re using in your app.

Similarly, if your application depends on accessing specific values by their keys, then you’ll probably want to request and receive a key/value pair dictionary.

By getting the data in the right format the first time, you’ll avoid a lot of re-processing in your app to make the data fit your chosen structure.

 
16) Choose the Right Data Format



 

Choose the right data format.

There are multiple ways you can transfer data to your app from a web service, but the most common two are JSON and XML. You want to make sure you choose the right one for your app.

JSON is faster to parse, and is generally smaller than XML, which means less data to transfer. And since iOS 5, there’s built-in JSON deserialization so it’s easy to use as well.

However, one advantage XML has is that if you use the SAXparsing method, you can work with XML data as you read it off the wire, instead of having to wait for the entire data to arrive before you parse it like JSON. This can give you increased performance and reduced memory consumption when you are dealing with very large sets of data.

 

17) Set Background Images Appropriately

Like many other things in iOS coding, there’s at least two different ways to place a background image on your view:

  1. You can set your view’s background color to a color created with UIColor’s colorWithPatternImage.
  2. You can add a UIImageView subview to the view.

If you have a full size background image, then you should definitely use a UIImageView because UIColor’s colorWithPatternImage was made to create small pattern images that will be repeated, and not large images size. Using UIImageView will save a lot of memory in this case.

// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];

However, if you plan to have a patterned image background, which uses smaller images which will be repeated or tiled to fill the background, you should go with UIColor’s colorWithPatternImage instead, as it’s faster to draw and won’t use a lot of memory in this case.

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];

 
18) Reduce Your Web Footprint

UIWebView is very useful. It’s a very easy to display web content, or even to create a visual aspect of your app that would be difficult with standard UIKit controls.

However, you may notice that the UIWebView component you can use in your apps is not as fast as the one which powers Apple’s Safari app. This is down to the restricted use of Webkit’s Nitro Engine, which featuresJIT compilation.

So to get the best performance, you’ll need to tweak your HTML a bit. The first thing you should do is get rid of as much Javascript as you can, which includes avoiding large frameworks such as jQuery. It can sometimes be a lot faster to work with vanilla Javascript instead of relying on frameworks to do the work for you.

Also follow the practice of loading your Javascript files asynchronously where possible – especially when they don’t directly impact the behavior of the page, such as analytics scripts.

And finally — always be aware of the images that you are using, and keep images right-sized for your purposes. As mentioned earlier in this tutorial, make use of sprite sheets wherever possible to conserve memory and improve speed.

For more information, be sure to take a look at WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS.

 
19) Set the Shadow Path

So you need to add a shadow to a view, or to a layer. How should you handle this?

Most developers would just add the QuartzCore framework to their project, and then add the following code:

#import <QuartzCore/QuartzCore.h>
 
// Somewhere later ...
UIView *view = [[UIView alloc] init];
 
// Setup the shadow ...
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity = 0.6;

Looks pretty easy, right?

The bad news is that there’s a problem with this approach. Core Animation has to do an offscreen pass to first determine the exact shape of your view before it can render the drop shadow, which is a fairly expensive operation.

The good news is that there’s an alternative that is much easier for the system to render: setting the shadow path!

view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];

By setting the shadow path, iOS won’t need to recalculate how it should draw the shadow all the time. Instead it’s going to use a pre-calculated path that you’re informing. The bad news is that depending on your view format, it may be harder to calculate the path by your own. Another problem is that you need to update the shadow path every time your view’s frame changes.

If you want to know more about this trick, Mark Pospesel wrote a great post about shadowPath.

 
20) Optimize Your Table Views

Table views need to scroll quickly — when they don’t, users really notice the lag.

To keep your table views scrolling smoothly, ensure that you’ve implemented all of the suggestions below:

  • Reuse cells by setting the correct reuseIdentifier.
  • Make as many views opaque as possible, including the cell itself.
  • Avoid gradients, image scaling, and offscreen drawing.
  • Cache the height of any rows if they aren’t always the same.
  • If the cell shows content that comes from the web, be sure to make those calls asynchronously and cache the responses.
  • Use shadowPath to set up shadows.
  • Reduce the number of subviews.
  • Do as little work as possible in cellForRowAtIndexPath:. If you need to do some work, do it only once and cache the results.
  • Use the appropriate data structure to hold the information you need. Different structures have different costs for different operations.
  • Use rowHeight, sectionFooterHeight and sectionHeaderHeight to set constant heights instead of asking the delegate.

 
21) Choose Correct Data Storage Option



 

Choose Correct Data Storage Option.

What are your options when it comes to storing and reading large data sets?

You have several options, including:

  • Store them using NSUserDefaults
  • Save to a structured file in XML, JSON, or Plist format
  • Archive using NSCoding
  • Save to a local SQL database such as SQLite
  • Use Core Data.

What’s the issue with NSUserDefaults? Although NSUserDefaults is nice and easy, it’s really only good if you have a very small amount of data to save (like what level you’re on, or whether sound is turned on and off). Once you start getting large amounts of data, other options are better.

Saving to a structured file can be problematic as well. Generally, you need to load the entire file into memory before you can parse it, which is an expensive operation. You could use SAX to process a XML file, but that’s a complex solution. As well, you’d end up having all objects loaded in memory — whether you want them there or not.

Okay, then, what about NSCoding? Unfortunately, it also reads and writes to a file, so it experiences the same problems as above.

Your best bet in this situation is to use SQLite or Core Data. By using these technologies, you can perform specific queries to only load the objects you need and avoid a brute-force searching approach to retrieve the data. In terms of performance, SQLite and Core Data are very similar.

The big difference between SQLite and Core Data is really about the general usage of each. Core Data represents an object graph model, while SQLite is just a regular DBMS. Usually Apple recommends that you go with Core Data, but if you have a particular reason you want to avoid it, you can go lower level to SQLite.

If you choose to use SQLite in your app, a handy library to use is FMDB which allows you to work with a SQLite database without having to delve into the SQLite C API.

Advanced Performance Tips

Looking for some elite ways to become a total code ninja? These advanced performance tips can be used when appropriate to make your apps run as efficiently as possible!

 
22) Speed up Launch Time

Launching your app quickly is very important, especially when the user launches for the first time. First impressions mean a lot for an app!

The biggest thing that you can do to ensure your app starts as quickly as possible is to perform as many asynchronous tasks as possible, such as network requests, database access, or parsing data.

As well, try to avoid fat XIBs, since they’re loaded on the main thread. But recall that storyboards don’t have this problem — so use them if you can!

Note: The watchdog doesn’t run while debugging with Xcode, so be sure to test your app with your device disconnected from Xcode while testing for launch performance.

 
23) Use Autorelease Pool

NSAutoreleasePool is responsible for releasing the autoreleased objects within a block. Usually, it’s called automatically by UIKit. But there are some scenarios where may you need to create NSAutoreleasePools manually.

For example, if you create a lot of temporary objects in your code, you’ll note that memory usage increases until these objects are released. The problem is that this memory is only released after UIKit drains its autorelease pool, which means this memory is held much longer than necessary.

The good news is that you can avoid this by creating these temporary objects inside your own @autoreleasepool block, as shown in the code below:

NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&error];
        /* Process the string, creating and autoreleasing more objects. */
    }
}

This releases all the autorelease objects at the end of each iteration.

You can read more about NSAutoreleasePool in Apple’s official documentation.

 
24) Cache Images – Or Not

There are two common ways to load a UIImage from an app bundle. The first, and more common way to do it is using imageNamed. The second, and less common way, is with imageWithContentsOfFile.

Why are there two methods which effectively achieve the same thing?

imageNamed has the advantage of caching the image as it’s loaded. The documentation for imageNamedexplains it this way:

This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.

Alternately, imageWithContentsOfFile simply loads the image with no caching.

The two methods are demonstrated in the code block below:

UIImage *img = [UIImage imageNamed:@"myImage"]; // caching
// or
UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching

When would you use one over the other?

If you’re loading a large image that will be used only once, there’s no need to cache the image. In this caseimageWithContentsOfFile will fit the bill nicely – this way, the OS doesn’t waste memory caching the image.

However, imageNamed is a much better choice for images that you’re going to be reusing in your app. This way, the OS saves time having to constantly load the image from disk.

 
25) Avoid Date Formatters Where Possible

If you have a lot of dates that you need to parse with NSDateFormatter, you need to tread carefully. As mentioned previously, it’s always a good idea to reuse NSDateFormatters whenever possible.

However, if you need more speed, you can parse dates directly using C instead of NSDateFormatter. Sam Soffes wrote a blog post about this topic which presents some code to parse ISO-8601 date strings. However, you can easily tweak his code examples to fit your particular needs.

Well, that sounds great — but would you believe there’s an even better way?

If you can control the format of the dates you are dealing with, choose Unix timestamps if at all possible. Unix timestamps are simply integers which represent how many seconds have passed since the “epoch”, which is simply the common reference date of 00:00:00 UTC on 1 January 1970.

You can easily transform this timestamp into an NSDate, as shown below:

 
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
    return [NSDate dateWithTimeIntervalSince1970:timestamp];
}

This is even faster than the C function!

Note that many web APIs return timestamps as milliseconds, since it’s very common for Javascript to eventually consume and process this data. Just be aware that you’ll need to divide the timestamp by 1000 before passing it to the dateFromUnixTimestamp method.

Where to Go From Here?

The following WWDC videos are highly recommended viewing for anyone interested in optimizing the performance of their apps. You’ll first need to ensure that you’ve registered your Apple ID as a developer, but once that’s done, you can have your fill of any of the videos from WWDC 2012:

  • #406: Adopting Automatic Reference Counting
  • #238: iOS App Performance: Graphics and Animations
  • #242: iOS App Performance: Memory
  • #235: iOS App Performance: Responsiveness
  • #409: Learning Instruments
  • #706: Networking Best Practices
  • #514: OpenGL ES Tools and Techniques
  • #506: Optimizing 2D Graphics and Animation Performance
  • #601: Optimizing Web Content in UIWebViews and Websites on iOS
  • #225: Up and Running: Making a Great Impression with Every Launch

There’s also some videos from WWDC 2011 that contain some great information as well:

  • #308: Blocks and Grand Central Dispatch in Practice
  • #323: Introducing Automatic Reference Counting
  • #312: iOS Performance and Power Optimization with Instruments
  • #105: Polishing Your App: Tips and tricks to improve the responsiveness and performance
  • #121: Understanding UIKit Rendering

There’s even more videos, mostly from iOS 5 Tech Talks:

  • Your iOS App Performance Hitlist
  • Optimizing App Performance with Instruments
  • Understanding iOS View Compositing

Based on “Your iOS App Performance Hitlist” video there’s a post written by Ole Begemann, that is a textual summary about the original presentation by Michael Jurewitz.

Apple has also provided a very useful resource called Performance Tuning, which provides a lot of great additional tips and tricks about making your apps perform well on iOS.

I hope you found this collection of tips helpful. If you know of a good tip that isn’t mention here, or have any comments or questions, please join the forum discussion below!

  • 大小: 13.1 KB
  • 大小: 33.1 KB
  • 大小: 44.5 KB
  • 大小: 30.5 KB
  • 大小: 7.6 KB
  • 大小: 62.4 KB
  • 大小: 37.7 KB
  • 大小: 18.6 KB
  • 大小: 13.2 KB
  • 大小: 19.6 KB
  • 大小: 11.6 KB
  • 大小: 53.5 KB
分享到:
评论
25 楼 啸笑天 2013-04-28  
25) 尽量避免Date格式化
如果有许多日期需要使用NSDateFormatter,那么需要小心对待了。如之前(重用花销很大的对象)所提到的,无论什么时候,都应该尽量重用NSDateFormatters。

然而,如果你需要更快的速度,那么应该使用C来直接解析日期,而不是NSDateFormatter。Sam Soffes写了一篇文章,其中提供了一些解析ISO-8601格式日期字符的串代码。你只需要简单的调整一下其中的代码就可以满足自己特殊的需求了。

这听起来不错把——不过,你相信这还有更好的一个办法吗?

如果你自己能控制处理日期的格式,那么可以选择 Unix timestamps(http://en.wikipedia.org/wiki/Unix_time)。Unix timestamps是一个简单的整数,代表了从新纪元时间(epoch)开始到现在已经过了多少秒,通常这个新纪元参考时间是00:00:00 UTC on 1 January 1970。

你可以很容易的见这个时间戳转换为NSDate,如下所示:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
    return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
上面这个方法比C函数还要快!

注意:许多网络APIs返回的时间戳都是毫秒,因此需要注意的是在将这个时间戳传递给dateFromUnixTimestamp之前需要除以1000。

何去何从?
强烈建议对程序性能优化感兴趣的读者看看下面列出来的WWDC视频。在看视频之前,你需要注册一个Apple ID(注册后就可以观看所有WWDC2012的视频):
#406: Adopting Automatic Reference Counting
#238: iOS App Performance: Graphics and Animations
#242: iOS App Performance: Memory
#235: iOS App Performance: Responsiveness
#409: Learning Instruments
#706: Networking Best Practices
#514: OpenGL ES Tools and Techniques
#506: Optimizing 2D Graphics and Animation Performance
#601: Optimizing Web Content in UIWebViews and Websites on iOS
#225: Up and Running: Making a Great Impression with Every Launch

下面这些视频来自WWDC 2011 ,也非常有用:
#308: Blocks and Grand Central Dispatch in Practice
#323: Introducing Automatic Reference Counting
#312: iOS Performance and Power Optimization with Instruments
#105: Polishing Your App: Tips and tricks to improve the responsiveness and performance
#121: Understanding UIKit Rendering

这里还有更多相关视频,大多数来自iOS 5技术讲座:
Optimizing App Performance with Instruments
Understanding iOS View Compositing

基于 “Your iOS App Performance Hitlist” 视频,Ole Begemann写了一篇文章。苹果还提供了一篇非常好的文章:性能优化。其中提供的技巧和提示对程序性能提升很有帮助。
24 楼 啸笑天 2013-04-28  
24) 缓存图片--或者不缓存
iOS中从程序bundle中加载UIImage一般有两种方法。
第一种比较常见:imageNamed。
第二种方法很少使用:imageWithContentsOfFile

为什么有两种方法完成同样的事情呢?imageNamed的优点在于可以缓存已经加载的图片。苹果的文档中有如下说法:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.

这种方法会在系统缓存中根据指定的名字寻找图片,如果找到了就返回。如果没有在缓存中找到图片,该方法会从指定的文件中加载图片数据,并将其缓存起来,然后再把结果返回。

而imageWithContentsOfFile方法只是简单的加载图片,并不会将图片缓存起来。这两个方法的使用方法如下:
UIImage *img = [UIImage imageNamed:@"myImage"]; // caching
// or
UIImage *img = [UIImage imageWithContentsOfFile:@"myImage"]; // no caching

那么该如何选择呢?

如果加载一张很大的图片,并且只使用一次,那么就不需要缓存这个图片。这种情况imageWithContentsOfFile比较合适——系统不会浪费内存来缓存图片。

然而,如果在程序中经常需要重用的图片,那么最好是选择imageNamed方法。这种方法可以节省出每次都从磁盘加载图片的时间。
23 楼 啸笑天 2013-04-28  
23) 使用Autorelease Pool
NSAutoreleasePool负责释放一个代码块中的自动释放对象。一般都是由UIKit来创建的。不过有些情况下需要手动创建NSAutoreleasePool。

例如,如果在代码中创建了大量的临时对象,你将注意到内存使用量在增加,直到这些对象被释放。问题是只有当UIKit耗尽了 autorelease pool,这些对象才会被释放,也就是说当不再需要这些对象之后,这些对象还在内存中占据着资源。

不过这个问题完全可以避免:在@autoreleasepool代码块中创建临时对象,如下代码:
NSArray *urls = &lt;# An array of file URLs #&gt;;
for (NSURL *url in urls) {
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                         encoding:NSUTF8StringEncoding error:&amp;error];
        /* Process the string, creating and autoreleasing more objects. */
    }
}
当每次迭代完之后,都会释放所有的autorelease对象。

关于NSAutoreleasePool的更多内容可以阅读苹果的官方文档。
22 楼 啸笑天 2013-04-28  
22) 加速启动时间
能快速的启动程序非常重要,特别是在用户第一次启动程序时。第一映像对程序来说非常重要!

让程序尽量快速启动的方法就是尽量以异步方式执行任务,例如网络请求,数据访问或解析。

另外,避免使用臃肿的XIBs,因为XIB的加载是在主线程中进行的。但是记住storyboard没有这样的问题——所以如果可以的话就使用storyboard吧!

注意:在利用Xcode进行调试时,watchdog不会运行,所在设备中测试程序启动性能时,不要将设备连接到Xcode。
21 楼 啸笑天 2013-04-28  
21) 选择正确的数据存储方式

选择正确的数据存储方式

当需要存储和读取大量的数据时,该如何选择存储方式呢?有如下选择:
1.使用NSUserDefaults进行存储
2.保存为XML,JSON或Plist格式的文件
3.利用NSCoding进行归档
4.存储到一个本地数据库,例如SQLite。
5.使用Core Data.

使用NSUserDefaults有什么问题呢? 虽然NSUserDefaults很好并且容易,不过只只针对于存储小量数据(比如你的级别,或者声音是开或关)。如果要存储大量的数据,最好选择别的存储方式。

大量数据保存为结构化的文件也可能会带来问题。一般,在解析这些结构数据之前,需要将内容全部加载到内存中,这是很消耗资源的。虽然可以使用SAX来处理XML文件,但是这有点复杂。另外,加载到内存中的所有对象,不一定全部都需要用到。

那么使用NSCoding来保存大量数据怎么样呢?因为它同样是对文件进行读写,因此依然存在上面说的问题。

要保存大量的数据,最好使用SQLite或Core Data。通过SQLite或Core Data可以进行具体的查询——只需要获取并加载需要的数据对象——避免对数据进行不合理的搜索。在性能方面,SQLite和Core Data差不大。

SQLite和Core Data最大的区别实际上就是用法上。Core Data代表一个对象模型,而SQLite只是一个DBMS。一般,苹果建议使用Core Data,不过如果你有特殊的原因不能使用Core Data的话,可以使用低级别的SQLite。

在程序中,如果选择使用SQLite,这里有个方便的库FMDB :可以利用该库操作SQLite数据库,而不用深入使用SQLite C API。
20 楼 啸笑天 2013-04-28  
20) 优化TableView
Table views需要快速的滚动——如果不能的话,用户会感觉到停顿。为了让table view平滑的滚动,确保遵循了如下建议:
1.设置正确的reuseIdentifer以重用cell。
2.尽量将view设置为不透明,包括cell本身。
3.避免渐变,图像缩放以及离屏绘制。
4.如果row的高度不相同,那么将其缓存下来。
5.如果cell显示的内容来此网络,那么确保这些内容是通过异步来获取的。
6.使用shadowPath来设置阴影。
7.减少subview的数量。
8.在cellForRowAtIndexPath:中尽量做更少的操作。如果需要做一些处理,那么最好做过一次之后,就将结果缓存起来。
9.使用适当的数据结构来保存需要的信息。不同的结构会带来不同的操作代价。
10.使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight 来设置一个恒定 高度,而不要从delegate中获取。
19 楼 啸笑天 2013-04-28  
19) 设置阴影路径
如果需要在view活layer中添加一个阴影,该如何处理呢?大多数开发者首先将QuartzCore框架添加到工程中,然后添加如下代码:
#import &lt;QuartzCore/QuartzCore.h&gt;

// Somewhere later ...
UIView *view = [[UIView alloc] init];

// Setup the shadow ...
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity = 0.6;

上面这种方法有一个问题,Core Animation在渲染阴影效果之前,必须通过做一个离屏(offscreen)才能确定view的形状,而这个离屏操作非常耗费资源。下面方法可以更容易地让系统进行阴影渲染:设置阴影路径!
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];

通过设置阴影路径,iOS就不用总是再计算该如何绘制阴影了。只需要使用你预先计算好的路径即可。有一点不好的是,根据view的格式,自己可能很难计算出路径。另外一个问题就是当view的frame改变时,必须每次都更新一下阴影路径。

如果你想了解更多相关信息,可参看Mark Pospesel的一篇文章:shadowPath。
18 楼 啸笑天 2013-04-28  
18) 降低Web内容的影响
UIWebView非常有用。用它可以很容易的显示web内容,甚至可以构建UIKit空间难以显示的内容。

不过,你可以能已经注意到程序中使用的UIWebView组建没有苹果的Safari程序快。这是因为JIT编译限制了WebKit的Nitro引擎的使用。

因此为了获得更加的性能,需要调整一下HTML的大小。首先就是尽量的摆脱JavaScript,并避免使用大的矿建,例如jQuery。有时候使用原始的JavaScript要比别的框架快。

另外,尽量的异步加载JavaScript文件——特别是不直接影响到页面行为时,例如分析脚本。

最后——让使用到的图片,跟实际需要的一样大小。如之前提到的,尽量使用sprite sheets,以此节省内存和提升速度。

更多相关信息,可以看一下: WWDC 2012 session #601 – 在iOS中优化UIWebView和网站中的Web内容。
17 楼 啸笑天 2013-04-28  
17) 设置适当的背景图片
在iOS编码中,跟别的许多东西类似,这里也有两种方法来给view设置一个背景图片:

1.可以使用UIColor的colorWithPatternImge方法来创建一个颜色,并将这个颜色设置为view的背景颜色。
2.可以给view添加一个UIImageView子视图。

如果你有一个全尺寸的背景图片,那么应该使用UIImageView,因为UIColor的colorWithPatternImge方法是用来创建小图片的——该图片会被重复使用。此时使用UIImageView会节省很多内存。
// You could also achieve the same result in Interface Builder
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
[self.view addSubview:backgroundView];

不过,如果你计划用小图片当做背景,那么应该使用UIColor的colorWithPatternImge方法。这种情况下绘制速度会很快,并且不会消耗大量的内存。
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
16 楼 啸笑天 2013-04-28  
16) 选择正确的数据格式

选择正确的数据格式

将数据从程序传到网络服务器中有多种方法,其中使用的数据格式基本都是JSON和XML。你需要做的就是在程序中选择正确的数据格式。

JSON的解析速度非常快,并且要比XML小得多,也就意味着只需要传输更少数据。并且在iOS5之后,已经有内置的JSON反序列化API了,所以使用JSON是很容易的。

不过XML也有它自己的优势:如果使用SAX方法来解析XML,那么可以边读XML边解析,并不用等到全部的XML获取到了才开始解析,这与JSON是不同的。当处理大量数据时,这种方法可以提升性能并减少内存的消耗。
15 楼 啸笑天 2013-04-28  
15) 避免重新处理数据
许多程序都需要从远程服务器中获取数据,以满足程序的需求。这些数据一般是JSON或XML格式。在请求和接收数据时,使用相同的数据结构非常重要。

为什么呢?在内存中把数据转换为适合程序的数据格式是需要付出额外代价的。

例如,如果你需要在table view中显示一些数据,那么请求和接收的数据格式最好是数组格式的,这样可以避免一些中间操作——将数据转换为适合程序使用的数据结构。

类似的,如果程序是根据键来访问具体的值,那么最好请求和接收一个键/值对字典。

在第一时间获得的数据就是所需要格式的,可以避免将数据转换为适合程序的数据格式带来的额外代价。
14 楼 啸笑天 2013-04-28  
14) 使用Sprite Sheets

使用sprite sheets

你是一个游戏开发者吗?是的话那么sprite sheets是最佳选择之一。使用Sprite sheets跟常用的绘制方法比起来,绘制更快,并且消耗更少的内存。

下面是两个非常不错的sprite sheets教程:
如何在Cocos2D中使用动画和Sprite Sheets
如何在Cocos2D中使用纹理包(Texture Packer)和像素格式来创建并优化Sprite Sheets  。(第二个教程详细的介绍了像素格式——在游戏中可以衡量性能的影响)

如果还不熟悉sprite sheets,可以看看这里的介绍:SpriteSheets – 视频, Part 1和 Part 2 。这两个视频的作者是Andreas Löw, 他是纹理包(Texture Packer)的创建者, 纹理包是创建sprite sheets的重要工具。

除了使用sprite sheets外,这里还介绍了一些用于游戏开发中的技巧,例如,如果你有很多sprite(比如射击类游戏中),那么可以重用sprite,而不用每次都创建sprite。
13 楼 啸笑天 2013-04-28  
13) 重用花销很大的对象

有些对象的初始化非常慢——比如NSDateFormatter和NSCalendar。不过有时候可以避免使用这些对象,例如在解析JSON/XML中的日期时。

当使用这些对象时,为了避免性能上的瓶颈,可以尝试尽量重用这些对象——在类中添加一个属性或者创建一个静态变量。

注意,如果使用静态变量的话,对象会在程序运行的时候一直存在,就像单例一样。

下面的代码演示创建一个延迟加载的日期格式属性。第一次调用属性的时候,会创建一个新的日期格式。之后再调用的话,会返回已经创建好的实例对象:
// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;

// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {
    if (! _formatter) {
        _formatter = [[NSDateFormatter alloc] init];
        _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
    }
    return _formatter;
}

另外,还需要记住的是在设置NSDateFormatter的日期格式时,同样跟创建新的一个NSDateFormatter实例对象时一样慢!因此,在程序中如果需要频繁的处理日期格式,那么对NSDateFormatter进行重用是非常好的。
12 楼 啸笑天 2013-04-28  
12) 处理内存警告
当系统内存偏低时,iOS会通知所有在运行的程序。苹果的官方文档中介绍了如何处理低内存警告:
If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.

如果程序收到了低内存警告,在程序中必须尽量释放内存。最佳方法就是移除强引用的涉及到的缓存,图片对象,以及其它可以在之后使用时还可以重新创建的数据对象。

UIKit中提供了如下几种方法来接收低内存(low-memory)警告:
实现app delegate中的applicationDidReceiveMemoryWarning: 方法。
在UIViewController子类中重写(Override)didReceiveMemoryWarning方法。
在通知中心里面注册UIApplicationDidReceiveMemoryWarningNotificatio通知。

在收到以上任意的警告时,需要立即释放任何不需要的内存。

例如,UIViewController的默认情况是清除掉当前不可见的view;在UIViewController的子类中,可以清除一些额外的数据。程序中不没有显示在当前屏幕中的图片也可以release掉。

当收到低内存警告时,尽量释放内存是非常重要的。否则,运行中的程序有可能会被系统杀掉。

不过,在清除内存时要注意一下:确保被清除的对象之后还可以被创建出来。另外,在开发程序的时候,请使用iOS模拟器中的模拟内存警告功能对程序进行测试!
11 楼 啸笑天 2013-04-28  
11) 考虑绘制

在iOS中制作漂亮的按钮有多种方法。可以使用全尺寸图片,可缩放图片,或使用CALayer, CoreGraphics, 甚至是OpenGL来手动测量和绘制按钮。

这些方法的复杂程度不同,性能也有区别。这篇关于iOS中图形性能的文章值得一读。其中Andy Matuschak(曾经是苹果的UIKit小组的组员)对这篇文章的评论中,对于不同的方法及其性能权衡有非常好的一个见解。

简单来说,使用预渲染图片技术是最快的,因为iOS中不用等到在屏幕上显示的时候才创建图形和对形状进行绘制(图片已经创建好了!)。这样带来的问题是需要把所有的图片都放到程序bundle中,从而增加了程序的大小。因此使用可伸缩图片在这里将排上用场了:可以移除“浪费”空间的图片——iOS可以重复利用。并且针对不同的元素(例如按钮)不需要创建不同的图片。

不过,使用图片的话会失去代码对图片的控制能力,进而针对不同的程序,就需要重复的生成每一个需要的图片,并反复的放到每个程序中。这个处理过程一般会比较慢。另外一点就是如果你需要一个动画,或者许多图片都要进行轻微的调整(比如多个颜色的覆盖),那么需要在程序中加入许多图片,进而增加了程序bundle的大小。

总的来说,要考虑一下什么才是最重要的:绘制性能还是程序大小。一般来说都重要,所以在同一个工程中,应该两种都应考虑。
10 楼 啸笑天 2013-04-28  
10) 缓存、缓存、缓存
在开发程序时,一个重要的规则就是“缓存重要的内容”——这些内容一般不会改变,并且访问的频率比较高。

可以缓存写什么内容呢?比如远程服务器的响应内容,图片,甚至是计算结果,比如UITableView的行高。

NSURLConnection根据HTTP头的处理过程,已经把一些资源缓存到磁盘和内存中了。你甚至可以手动创建一个NSURLRequest ,让其只加载缓存的值。
下面的代码片段一般用在为图片创建一个NSURLRequest:
+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image
    request.HTTPShouldHandleCookies = NO;
    request.HTTPShouldUsePipelining = YES;
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    return request;
}
注意:你可以使用NSURLConnection抓取一个URL请求,但是同样可以使用AFNetworking来抓取,这种方法不用修改所有网络相关的代码——这是一个技巧!

更多关于HTTP 缓存, NSURLCache, NSURLConnection 以及相关的内容, 那么看一下NSHipster中的the NSURLCache entry。

如果你需要缓存的内容没涉及到HTTP请求,那使用NSCache。NSCache的外观和行为与NSDictionary类似, 但是,当系统需要回收内存时,NSCache会自动的里面存储的内容。Mattt Thompson 在NSHipster上写了一篇关于NSCache非常不错的文章。

更多关于HTTP缓存的内容,建议读一下Google的这篇文章:best-practices document on HTTP caching。
9 楼 啸笑天 2013-04-28  
9) 重用和延迟加载View
程序界面中包含更多的view,意味着界面在显示的时候,需要进行更多的绘制任务;也就意味着需要消耗更多的CPU和内存资源。特别是在一个UIScrollView里面加入了许多view。

这种情况的管理技巧可以参考UITableView和UICollectionView的行为:不要一次性创建所有的subview,而是在需要的时候在创建view,并且当view使用完毕时候将它们添加到重用队列中。

这样就可以仅在UIScrollView滚动的时候才配置view,以此可以避免分配创建view的带来的成本——这可能是非常耗资源的。

现在有这样的一个问题:在程序中需要显示的view在什么时机创建(比如说,当用户点击某个按钮,需要显示某个view)。这里有两种可选方法:
在屏幕第一次加载以及隐藏的时候,创建view;然后在需要的时候,再把view显示出来。
直到需要显示view的时候,才创建并显示view。

每种方法都有各自的优点和缺点。第一种方法需要消耗更多的内容,因为创建出来的view一直占据着内存,直到view被release掉。不过,使用这种方法,当用户点击按钮时,程序会很快的显示出view,因为只需要修改一下view的可见性即可。而第二种方法则产生相反的效果;当需要的时候猜创建view,这会消耗更少的内存;不过,当用户点击按钮的时候,不会立即显示出view。
8 楼 啸笑天 2013-04-28  
8) 使用GZIP压缩

使用GZIP压缩

越来越多的程序依赖于外部数据,这些数据一般来自远程服务器或者其它的外部APIs。有时候你需要开发一个程序来下载一些数据,这些数据可以是XML,JSON,HTML或者其它一些文本格式。

问题是在移动设备上的网络是不确定的。用户的设备可能在EDGE网络一分钟,然后接着又在3G网络中。不管在什么情况下,都不要让用户等待。

有一个可以优化的选择:使用GZIP对网络传输中的数据进行压缩,这样可以减小文件的大小,并加快下载的速度。压缩对于文本数据特别有用,因为文本具有很高的压缩比。

iOS中,如果使用NSURLConnection,那么默认情况下已经支持GZIP压缩了,并且基于NSURLConnection的框架页支持GZIP压缩,如AFNetworking。甚至有些云服务提供商已经提供发送经压缩过的响应内容,例如 Google App Engine。

这里有一篇关于GZIP压缩很好的文章,介绍了如何在Apache活IIS服务器中开启支持GZIP压缩
7 楼 啸笑天 2013-04-28  
7) 选择正确的集合

选择正确的集合

学习使用最适合的类或对象是编写高效代码的基础。特别是在处理集合数据时,尤为重要。

苹果的官网上有一篇文章:集合编程主题(Collections Programming Topics)——详细的介绍了在集合数据中可以使用的类,以及什么情况下使用哪个类。在使用集合时,每个开发者都应该阅读一下这个文档。

太长,不想阅读(TLDR)?下面是常见集合类型的一个简介:
•数组:是一个值按顺序排列的一个列表。根据索引可以快速查找,不过根据值进行查找就比较慢,另外插入和删除也比较慢。
•字典:  存储键/值对。根据键可以快速查找。
•Sets:  是一个值无序排列的列表,根据值可以快速查找,另外插入和删除也比较快。
6 楼 啸笑天 2013-04-28  
6) 让图片的大小跟UIImageView一样

确保图片和UIImageView大小一致

如果需要将程序bundle中的图片显示到UIImageView中,请确保图片和UIImageView的大小是一样的。因为图片的缩放非常耗费资源,特别是将UIImageView嵌入到UIScrollView中。
如果是从远程服务中下载图片,有时候你控制不了图片的尺寸,或者在下载之前无法在服务器上进行图片的缩放。这种情况,当图片下载完之后,你可以手动进行图片的缩放——做好是在后台线程中!——然后再在UIImageView中使用缩放过的图片。

相关推荐

Global site tag (gtag.js) - Google Analytics