[Download] Free [PDF] DOWNLOAD Pro iOS Apps Performance Optimization FOR KINDLE FOR IPAD Get now homeranking.info Today s. It's about making people feel that the application responds to any interaction as fast as possible. Therefore, performance optimization in your iPhone application . For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and.
|Language:||English, Spanish, Hindi|
|ePub File Size:||21.87 MB|
|PDF File Size:||12.64 MB|
|Distribution:||Free* [*Regsitration Required]|
Pro iOS Apps Performance Tuning and Optimization covers many common but DRM-free; Included format: PDF; ebooks can be used on all reading devices. Today's iPhone and iPad apps developers are often running into the need to refine, improve Pro iOS Apps Performance Optimization Khang Vo. Pages 39 PDF · Increase App Performance Using Image and Data Caching Techniques. Todays iPhone and iPad apps developers are often running into the need to refine, improve and optimize their apps performances. As more complex apps can.
Storing in CoreData CoreData is a strong and powerful built-in framework provided by Apple to help developers manage and control their data easily. File Loading Time: Keep them in a balance mode to make sure your application runs perfectly. For the subview, you need to override the drawRect method and then draw the text or image by method drawInRect. File Activity instrument Figure 4—2. The colors will be displayed as shown in Figure 2—
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare. Like this presentation? Why not share! An annual anal Embed Size px. Start on. Show related SlideShares at end. WordPress Shortcode. Published in: Full Name Comment goes here. Are you sure you want to Yes No. Be the first to like this. No Downloads.
Views Total views. Actions Shares. Embeds 0 No embeds. No notes for slide. Book details Author: I will explain this difference later and why you will use imageNamed instead of initWithContentsOfFile. So, if the CPU needs to create a new cell every time user scrolls up and down to a new cell, the whole performance will decrease.
Increase and Optimize UITableView Performance standard and default way to improve this process is to reuse the cell whenever the cell is out of the screen. There are two main ways to create a new custom cell, either by using InterfaceBuilder or by writing the custom code directly by calling the addSubview: The identifier can be modified by open the xib file, going to the first tab, and changing the first row, as shown in Figure 3—4.
Setting the Reuse Identifier for the Cell Now, inside the initialization code for cell, you need to use exactly the same identifier. The main difference between these two approaches is the way you load and initialize your UITableViewCell.
CellIdentifier nibName: CGRectMake 20, 20, 30, 30 ]; [self.
Running Benchmarks Again After reusing the cell, you can benchmark the scrolling performance again. As you can see in Table 3—3, the performance doubles after you reuse the cell correctly. Table 3—3. You always want the performance to go up to around 0. Here is the reason why you should always reuse the cell.
It takes time and memory for the OS to create and load a new cell into the memory. This is why the tableView will always queue the cell for reuse whenever the cell is out of the screen. This process is much faster than when the OS needs to create a new cell. For this section, please refer to the project named ReuseImageViewController.
The imageNamed does an important job: The problem with this method is that it can only get the image from your bundle—in other words, just images that come with the source code of the app. With these methods, the OS will not automatically cache the image in the memory for the app. So, I want you to cache the images yourself by using a small dictionary to store the image in memory see Chapter 4. Another important part of dealing with images is multithreading see Chapter 6.
Using this technique, you can put the heavy processing jobs out of the current processing thread. In my current example, I will not use multithreading because it would be asking you to assimilate too many new ideas at once. You should try it yourself as an exercise at the end of the chapter. Here is the main code to store images in an NSDictionary please do not use this way to store images in your app because it will cause memory warning issue. As you can see from Table 3—4, you get a much better result.
The average running time is now 0. Table 3—4. The fps rate now is almost 60, and the preparation time is decent. If the scrolling performance is still jerky, however, you may need to use a much better but also much more complicated way to get the same performance.
As already stated in Chapter 1 and Chapter 2, you should always be careful to avoid over-optimization. Reducing Preparation Time Generally, I want to reuse the images by caching them and I want to reduce the initialization process. When the OS needs to render a new cell for the TableView, it will ask for a new cell by calling the following method: This is part of the reason why users will see that the scrolling just stops at one place forever.
To make this process as fast as possible, you can remove logic, delay calculation, and cache data and images that you can reuse them.
Another approach is to reuse a cell with default image and data first. In this example, I will increase the complexity of the cells by adding up to 10 subviews with images and text from a real application.
Thus, you can see that in some real applications like Facebook, the one we are trying to emulate , scrolling performance can be significantly affected by the complicated subviews structure. The app that I benchmark will have a user interface like the one you see in Figure 3—5. Figure 3—5. Increase and Optimize UITableView Performance Every cell inside the app will have an avatar, a username, and a post with image, title, and content.
It also shows which application made the post at what time. The results for the benchmarks are in Table 3—5. Table 3—5. Table 3—6. For the cell that is not optimized yet, it will be made up by a number of components and subviews. Look at Figure 3—5 to make sure you understand the problem.
The subview is a general approach that developers use if you want to have a different background color or to a manage view component inside easier. This approach causes performance issue for table view scrolling, so you want to avoid it. You will see what you need to do to implement this behavior, then I will sum up the advantages and disadvantage of the different techniques. The sample source code comes from the project DrawingCellViewController.
Here is the main source code. CGRectMake 70,0, 95, 21 withFont: Inside the initialize method, you have to add a subview into the cell content. For the subview, you need to override the drawRect method and then draw the text or image by method drawInRect. The reason why the drawing code will run much faster than loading from nib files or from creating and adding subview directly is the GPU Graphic Processing Unit will run the drawing code. The GPU is quite fast at rendering and displaying the UI; therefore, the drawing code is the fastest way to deal with complicated subviews.
One important thing ito remember is to set the background color of CustomDrawingView to white. The default color is black. From the two preceding examples, there are some basic lessons you should always remember. It will help speed up performance. This will display the image in the shortest amount of time. This will allow the GPU to speed up the process. As you can see from the benchmark results, the fps result becomes much better and even closer to the perfect number of You always need to calculate the position and the size yourselves and put that information into drawRect.
This will quickly cause a headache in terms of maintenance and feature bloat adding more features into your application. So, be careful with drawRect and with over-optimization. If you can understand the concept, you can apply the same technique to other cases. Caching the Height You need to cache the height of the rows because TableView requests this information whenever it needs to create a new cell. Try something like this: With a loop inside like this, the OS will need to run a loop that repeats times whenever it needs to know the height of the cell.
When a view is transparent, the iOS will need to render one pixel twice or more because that one pixel belongs to many subviews at the same time. This can be a time-consuming process. This part can be done easily through code or through InterfaceBuilder. Developers should double-check to see that all the subviews are opaque. Figure 3—7 shows how to set the opaque checkbox for the subview inside the cell.
Figure 3—7. Set opaque for the subviews For the custom code, you can also set it by code, as follows: You should check for graphical effects and problems by using the CoreAnimation with some small configuration, as shown in Figures 3—8 and 3—9.
CoreAnimation options to set up debug options Figure 3—9. However, the drawing approach also has some serious problems in animation and reordering performance.
If you draw the view by code in the animation edit or reordering, you then have to draw the view again to fit into the new view. This makes for more work, both in creating and maintaining the code. This is a tradeoff that you always have to think about when you have performance issue with UITableViewController. This can make the app run slower but still well enough.
Summary By going through examples with source code, you learned a number of important techniques to improve application performance. I let you see a real example of using the Instrument and benchmark tools efficiently to understand the nature of the problem and how much improvement you make after every optimization step. This is the first and most important step.
It is easy to implement cell reuse but many applications lack this step. So, if you have any performance problems, make sure to double check this. Another important step is to reduce the time to load data and do logic processing while returning a cell for displaying.
Therefore, you should always try to reduce this processing as much as possible. To utilize the full computing capacity of the CPU while rendering the table view, you may need to consider using drawing methods directly. This will boost the rendering process a lot and increase the performance measurements; the fps rate becomes almost maximal. You can draw your custom cell by overriding the drawRect method and drawing every UI element yourself with different drawing methods inside each element.
This small problem is encountered when developers position their user interface elements into the view. This is another small mistake that developers often make. There are two main methods called every time a cell is requested. The more graphical effects you have on a cell, the slower the rendering process. Therefore, you should test for this as well. You can definitely use the CoreAnimation to see the rendering effect of each UI element. Optimizing the performance for scrolling can cause performance problems for editing and reordering because the UIKit and animation framework is optimized to work well with subviews.
If you draw the cell yourself, those framework optimizations will become ineffective. Theory 1. Create a checklist to make sure you follow all the necessary and basic steps to have a good, high performance UITableViewCell. Practice 1. Try it with multithreading technique to load images from file to display. The tradeoff between memory consumption and performance. For most iPhone apps today, developers usually either load data from their own servers or consume data from third-party services.
A minority of apps have data stored in the file system and load it to display to users when necessary. Very few apps do not use any kind of network or file IO processing. Therefore, understanding the impact of these types of processing helps you to figure out problems and solve them easier. Of course, the results will vary depending on how quickly the server processes the request, the speed of the network, and how far the server is from the testing machine.
However, I want to demonstrate the important idea that loading an image across the network is much slower than loading it from a file and that loading it from a file is much slower than having the image already in memory.
I tested the performance based on loading a 50kb image. Here are the results: File Loading Time: The total time for loading all those images would be more than couple of seconds. How to Identify the Bottleneck There are two main problems with loading from a file or the network.
This time may increase proportionally with the number of images. If the user interface UI needs to load many images while running something like UITableView, users will have to wait every time they scroll down to see more information. This is also covered in Chapter 6. If your app has to wait for the data from the network, all other processes must wait as well.
As you saw in Chapter 2, you can observe the data loading process with System Activity and File Activity. Figure 4—1 shows the UI of those instruments. File Activity instrument Figure 4—2. Figure 4—2 shows more details about each activity, which helps you to see what kinds of file activities are running most often.
Figure 4—3. System Usage instrument Figure 4—4. List of results from System Usage Figure 4—3 and 4—4 shows more about System Usage, which is more general and covers more data types. As you can see in Figure 4—4, there are activities with the plist and nib files. Increase App Performance Using Image and Data Caching Techniques Introduction to Caching This section will explain many important terms and concepts in caching and will introduce some basic algorithms for caching in general environments such as web and desktop applications.
Many of these algorithms can be applied to the iPhone environment; these will be explained in subsequent sections of this chapter. I will explain the following concepts: Then I will explain some of the most used caching algorithms: What is Caching?
In other words, it saves time. The cache hit ratio is usually used to determine if your algorithm is a good or bad one. It is usually combined with how much size you save whenever a cache hit happens. This savings helps you to improve the loading time from both network and file activities. Another perspective to consider is how your users feel about the performance: This can be a bad thing for your app, for example, when the app is not connected to the Internet.
To avoid this, you usually need to cache many images and data when the app is online to show them offline. This can be separated into two cases. As shown in the previous example, this consumes the highest amount of time compared to loading from file and memory. The retrieval cost for the network data is high.
This consumes less time to load from a file to memory so the retrieval cost in this case is low. Storage Cost Storage cost is also divided into two cases: The iPhone file system can contain up to 8, 16, or 32GB, depending on the device itself. If you use an rss reader to read news and this reader keeps storing all the images from the news, you will get to 8GB quickly.
Your app should only use up to few hundred megabytes; otherwise, the user may delete your app soon. The maximum memory that an app can use to store its own internal data to process is around MB. Finally, for the latest iPhone, you can store around 50 1MB images. This is actually not much compared to many of the memory-consuming apps and games in the iPhone App Store.
Happily, most web services will take care of this and will change the URL when the image is actually changed. If you cache the data in your file system by using a database or plain text; I will explain more about this later in this chapter , then you may never know if your data is out of date. If every time you use the data you have to go to the network to check if there is new data in the server, you lose all the benefits of caching data within your file system.
There are a couple of ways to solve this cache invalidation problem. After some specific period of time, the app will check for latest data and replace the whole data with new data. Usually developers use a combination of the first and third approach because it makes more sense. The third approach can be done easily with a refresh button, and the app can reload for new data when the app is open or is closed, which can also be done easily.
It can also tell you at what time you should check to delete cache to keep a fresh cache base. There are four main things you should always consider when you pick one replacement policy over other policies. The higher this number, the better.
Usually refers to two different types of latency. The first meaning is the time it takes to load data from the cache in case you have a cache hit. The second meaning is the time it takes to load data from the original source in case you have a cache miss. You will have to consider if you want to cache the heaviest items such as 1MB and above or if you want to cache smaller items less than 10KB.
The smaller item will give you a higher cache hit ratio, but it may not save bandwidth and time like the heavier item. This is usually a trade-off with cache hit ratio because you only have limited available space both memory and file space in an iPhone, so you have to consider it carefully. Otherwise, you can store as much as you want in order to increase the cache hit ratio.
How hard will it be to implement the algorithm to match your favorite policy? This is a big trade-off; you will use more memory, and the app needs to spend CPU and time to execute the algorithm to determine if it should delete a piece of cache. Note that the more complex the algorithm, the more you will need to spend to build, maintain, and fix any bugs inside your own algorithm. Caching Algorithms I will cover some very basic and easy-to-implement algorithms like random replacement, first in first out and simple time-based plus more complex algorithms like least recently used and least frequently used.
Why do I say that this is a theoretical algorithm? Because in many real-world cases, you never know if that piece of information will be necessary in the future or not. The image gets deleted. Then the web designer changes his mind and replaces back the old image.
Now the app needs to reload the old image. However, this algorithm is used as a benchmark to compare other algorithms. You delete cache based on some random access. The algorithm may mistakenly delete some files that are still necessary while keeping an unused file for a long time. In this sample, I use cacheDictionary to store the memory data. You need to base it on the creation date of the files. You will need to find the creation date of all files and get the oldest creation date to delete.
I use an array cacheOrders to store the list of unique names of images. This array stores data in an ordered manner: You specify a basic period of time for a cache.
After that specific period of time, the app checks how long a cache exists the age of the cache.
If the cache is older than a specific age for example, 14 days , the cache is automatically deleted. For example, if you only need to delete 10MB of images in file system to have more space, and 20MB of the images are older than a specific data, you may over-delete. Usually used with file system cache rather than memory cache.
You will need to find the creation date of all files and extract the files that have creation date older than your specified date.
Then you delete those files. From my own experience, because the file storage can be really big, and you may only need to delete it once per several months, you can just use the simple time-based algorithm. Imagine that you have a list of items. Whenever the same item is requested, you put that item at the head of the list.
When you need to delete an item, you delete an item from the tail of the list. Table 4—1 displays an example array of four items. The Current State of the List After Every Request Step Current List New Request Item 1 1, 2, 3, 4 1 2 1, 2, 3, 4 1 3 1, 2, 3, 4 2 4 2, 1, 3, 4 4 5 4, 2, 1, 3 5 6 5, 4, 2, 1 3 7 3, 5, 4, 2 As you can see in step 5, because a new item comes in called 5 and I can only store four items inside the list, I have to delete item 3.
I will cover another algorithm that will try to approach this issue later in this chapter Implementation: It can be implemented both with file and memory. However, I hope that you get the main idea. It focuses on how often an item is requested. If an item is requested more often than other items, it should be kept on the cache. Table 4—2 demonstrates the least frequently used algorithm. Table 4—2. Increase App Performance Using Image and Data Caching Techniques As you can see, with the same initial list and the same list of item requests, these two algorithms generate different results at the end.
Item 1 is accessed times right at the beginning. The problem is that the item 1 will still be kept in the cache because no other item has been accessed more than times. As a quick exercise, write a small objective-C code to implement the algorithm. Again, try to do it yourself as an exercise before moving to the next page to see the hints. Whenever a new request comes in, you increase the count and check if you need to delete the old cache.
If you need to delete the old cache, then you should choose the cache name with the smallest count of access. As stated in the replacement policy, you will have to face four main issues when deciding your caching policy: The algorithm complexity depends on your capability; if you are a good developer, it may not have much effect on the decision over other factors.
Imagine that you have two file-caching scenarios related to cache hit ratio and retrieval constraint. As you can see, the storage cost for both strategies is the same: The cache hit ratio of the first strategy gives you 21KB while the other strategy gives you 50KB. Increase App Performance Using Image and Data Caching Techniques terms of retrieval cost, you already saved more bandwidth and loading time with the second strategy.
Table 4—3 offers a quick summary of the terms you just learned. Table 4—3. Cache miss When a new request comes and no cache is found for that request. Storage cost How much cache you can store before deleting old cache. Retrieval cost How much time or CPU is wasted if a cache is not found in other words, a cache miss happens. Invalidation How you know if a cache is out of date or not synchronized correctly with that piece of data on the server. Replacement policy The general policy you apply to store and invalidate cache, based on a number of factors: Measuring cache How to measure cache in real-world situations.
What You Should Cache Developers should only care about caching few things: I separate them out into two main types: For each kind, I will show you some important features that you need to remember when you store those files. In other words, where should you cache your files so that you can retrieve them and not worry about its lifetime? There are some classic locations where you can store your files: Each of them has a unique characteristic that you need to know and remember.
In fact, it will be deleted by the OS at some indeterminate time. The temporary directory will also be deleted when the user restarts their iOS device. For example, you want to store some screenshots of the app to show to users while they are waiting.
Cache Directory You can access and store data inside this directory by using the following code snippet: This will help the backup process faster when your user syncs the app with his iTunes.
You have control over what and when you store your files. I have seen many apps store data in this folder and it takes hours to back up my whole device. The main time is spent storing images inside the documents directory. Figure 4—5. Photo Album This directory belongs to the OS and will be shared between many applications. You are allowed to store photos and videos inside these albums. The main application for users to see all photos and videos in an iOS device is the Photos app, as shown in Figure 4—6.
Figure 4—6. Images and videos inside the Photos app To store photos and videos inside the Photos app, use the appropriate code snippet.
For videos: Application Bundle Images and videos will be bundled to your app when you develop the app and will be installed with the app, as shown in Figure 4—7. Figure 4—7.
Images bundled with the application To get files, you can use the following code snippets: Users will have to wait too long for it to download and install. These are generally icons or videos that you will show again and again inside your app. Here are the main ways to store your data.
Figure 4—8. Data can be stored easily and visualized You can also see the data in XML format, as shown in Figure 4—9. Storing in CoreData CoreData is a strong and powerful built-in framework provided by Apple to help developers manage and control their data easily. It creates a layer of object-oriented abstraction over the relational relationship problem between data. You can create your object relational model using drag and drop. Figure 4—10 shows an example of a CoreData model between questions, answers, and their categories.
CoreData object relational model CoreData will also generate code automatically. Figures 4—11 and 4—12 show you the code that is generated for the object question in Figure 4— CoreData will take care of all actions from storing, retrieving, and managing relationship between objects. Figure 4— Code generated for the implementation file of the object Advantages: CoreData will take care of it.
You do not have a UI to manage the initial data. You should use this to cache your data in most cases. Because SQLite is written purely in C. This decision depends on the environment in which you are working; in other words, an iOS environment is different than a web environment. In a web server, you can run a check method in a loop to see if any cache is outdated and delete it.
When you should check cache contents and delete them depends much on the selected algorithm that you use to cache them. This approach is usually used for memory caching where you need more precise algorithm and have a strict storage constraint.
For file caching, you will usually choose a simple time-based algorithm.
File caching does not have a strict limited storage capacity like memory does. Again, here is an important lesson: If you think your approach can cause a bottleneck, you can try to benchmark it. Here are some main approaches: Be careful if you use multithreading—the app may end before you finish the checking and deleting cache process.
It may be slow the first time you request that piece of cache, but it will be faster on subsequent tries. I recommend not using multithreading in this case because you may delete a file that another thread is reading. Increase App Performance Using Image and Data Caching Techniques Memory Caching Here are some specific details about how to do memory caching and the specific problems you may encounter when you do memory caching in iOS environment.
If you do, the iOS run-time environment will keep giving you memory warnings until your app is forced to close. Some people are scared of storing in memory because of the limited memory environment. Not to worry: The more you can cache data and images in memory, the better performance your app will get.
However, if you are using too much memory, be aware that your app may get memory warning or to be forced to close. Global Access vs. Strict Access For memory caching, you may need to think about whether you want this caching to be available to any class and methods within your application or only strictly to some CHAPTER 4: If this is global access, anybody can change the cache whenever they want.
However, if you keep the caching data so private and hard to access, the cache becomes useless because some classes or methods may need the data and will just request it from the server again.
Global Access: You may need to use static to define global object. Just In Time Preloading is when you load the image before you actually need it. The difficult part is that you may need to guess if you need to preload the images. Preloaded image for paging view As Figure 4—14 shows, for a view like PageView where user wants to have a smooth scrolling experience, preloading images into cache is the best way to keep the scrolling smooth. Otherwise, the user either has to wait after scrolling to the view for multithreading or loading the image after stopping in the view or they have a stuck scrolling experience Another way to load the images into cache is called just in time.
The bad part about this is that it may slow down and keep your users waiting for the data. Increase App Performance Using Image and Data Caching Techniques Figure 4—15 is an example where the user knows that he may need to wait for a couple of seconds in order to get the big image view. If you load from file to memory to display, the loading time will be quite fast; therefore you may not need to worry about preloading the images into memory.
Another problem is if the image is big, preloading it into memory will cost your app a huge amount of memory. Summary Based on my tests, you can see that the performance from network is much slower than performance from file and the performance from file is much slower than performance from memory.
You should always try to identify the bottleneck with instrument tools before spending too much of your time to optimize some parts. In other words, please avoid over-optimization. You learned about basic topics in caching such as cache hit, cache miss, storage cost, retrieval cost, replacement policy, and several well-known algorithms. Some of them are really basic but not efficient; some of them can be efficient but cost time and effort to implement.
You also learned about all the possible ways to store cache and the sceanrios in which to use each approach. You can store your data in the temporary folder, the cache folder, or document folder based on the specific purpose of your requirement.
You may also choose to use the photos album so you can easily share it with other applications. Lastly, you learned about memory caching. Should you allow global access or strict access to your memory cache data? Should you preload data into memory or load them just in time? Both offer benefits and drawbacks that you will need to consider carefully before making your decision. Implement the LFU caching algorithm yourself. If you have any problems, you can look at my hints.
Write a simple program to get a plist file and parse it to a corresponding structured data. Take the plist file Questions. However, as mentioned with regards to caching issues in the previous chapter, especially when your phone is offline, you should store your data locally and compute it within the phone environment. Here is the issue: Tune Your App Using Algorithms and Data Structures First Example This first example will show you how a bad algorithm can affect your program when it runs in the strict mobile phone environment.
My sample code is simple: The first and second arrays are the same in terms of the number of elements. I loop through two arrays to check how many common elements are contained between the two arrays.
The results are shown in Table 5—1. Table 5—1. Considering that 1, items is not practical when you deal with real applications, 5. However, remember that there are many old devices with less power out there, and their owners may not update these devices any time soon.
In that light, 5. What follows is a look at the initial source code and few explanations before I move on to explain some concepts in-depth. Listing 5—1 solves the problem by using an array and loop while Listing 5—2 solves the problem by using a set.
Listing 5—1. To avoid a duplicate count, after the program finds the item in the second array, the program stops looping over the second array.
The second approach is much simpler; with the use of NSSet, you only need to store all elements inside the NSSet and call the correct method to do the job for you.
The intersect method will find same items inside two sets and put them into the variable myFirstSet. As you can see, a wrong approach can be times slower than a correct approach. Moreover, the correct approach may save you a few hours of coding when trying to reuse all the necessary data structures and algorithms.
Computer science requires exact data and standards to make sure that some algorithms are actually better than others. People usually use Big-O notation to describe how an algorithm scales performs in the worst case scenario.
There are other terms and definitions used in computer science that I will not cover here. For example, if you have an algorithm with the performance O N , then that algorithm when working on an array of 25 elements will take 25 steps, compared to 5 steps for an array with 5 elements. So, it takes 5 times longer for the 25— element array to finish the task. How to Measure Big-O To determine the Big-O of an algorithm, you need to look at how the size of the input affects the time it takes the algorithm to execute.
Another way to accomplish this is to logically think through the algorithm execution. Listing 5—3 should help you understand this concept. Listing 5—3. I will explain them further later. There are other computing logics like NSLog "my first Count: In this part, m and n are the number of elements in myFirstArray and mySecondArray respectively. This helps you do performance analysis yourself without using the actual counting code.
The outer loop the loop that runs over myFirstArray runs m times. Because the outer loop runs m times, every computation logic inside the outer loop runs m times as well. To calculate the number of computation inside the outer loop, I consider the inner loop and the two NSLog lines. For each cycle of the loop, the inner loop and two NSLog lines run once.
Similarly to the previous situation, the inner loop runs n times, and every computation logic inside the inner loop runs n times. The next step is to keep the most significant term or the fastest growing term in the formula, so you have only 2n2 left. The last step is to remove all constants in the formula, so you have n2 left, which you can write in the theoretical format as O n2.
Each of the operations has its own growth rate. You can see that by increasing n by 9 units, the log n only increases 1 unit, n increases 9 units, and n2 increases 99 units.
Thus by increasing the same number of units, different operations grow differently. Figure 5—1 shows how fast each operation log n , n, n2 and n3 grows when you increase n. For example, in a small scale, an insertion sort will be faster than a quick sort; when repeated times, the total performance of insertion sort will beat the quick sort.
Therefore, Big-O notation gives you a general understanding about algorithms, which you can use to quickly compare different algorithms and approaches. There are other notations in the family of notations to describe average and best cases performance. These notations indicate how good each algorithm is when you take individual circumstances into consideration.
Big-O of Famous Algorithms To give you a better understanding of Big-O, Table 5—2 lists famous algorithms and their corresponding Big-O notation so that you can see how these algorithms will behave theoretically.
O n Merge sort An algorithm to separate the array into smaller parts and then sort those smaller parts. O n Binary search This searches by dividing the collections into subcollections and then searches inside the correct subcollections. O logn 2 You may get confused about the order in which you should understand the Big-O notation. The order is like this: O log n Bubble sort is slower than merge sort and quick sort. Thus, because merge sort and quick sort run faster than bubble sort, you should choose these sorting algorithms over bubble sort in most cases.
Practical Measurement In practical use, you will need to combine some theory about Big-O with the instruments and analysis tools covered in Chapter 2. Detailed results of each method and function inside the program Figure 5—2 shows the usage of CPU Sampler showing system load, user load, and total load when the program is running. Figure 5—3 provides more details about each method inside the program, such as how often each method is called and how long it takes for a method to finish.
Figure 5—4. The Time Profiler instrument Figure 5—5. Figure 5—5 shows the detailed results of the time perspective. You should use the instruments to figure out which method you actually need to optimize. You have limited time and resources to optimize the algorithms and data structure for that method. Data Structure and Algorithms Cocoa Touch framework of iOS environment gives you three main data structures for most usages: Each of them has benefits and best usage scenarios.
I will discuss each of these main data structures for iPhone, then I will walk you through some other important data structures via code and samples so you can build your own in case you need a better performance data structure: For example, there is no use in talking about NSDictionary and hash table without knowing what a hash function does. Therefore, I will discuss them in parallel; I will talk about data structures and what algorithms they use to speed up their performance.
In the last sections, I talk separately about common algorithms shared between data structures. When discussing data structures, I will talk about their performance in terms of these main issues: I use the Big-O notation to give you the idea of how fast each data structure performs under each insertion, deletion, search, or sort.
NSMutableArray An array is the simplest form of data structure. Array illustration An array is a collection of individual items put into a block, like in Figure 5—6, where each block is numbered from 0 to the length of the array. NSArray also allows you to store duplicate data in the array. Figure 5—7. Inserting an item into the second position of the array Search: The process is shown in Figure 5—8.
A similar problem with searching inside an array is determining if that array contains a particular element. Figure 5—8. Table 5—3. NSUInteger index withObject: NSComparator cmptr - void sortUsingFunction: To fully understand why NSSet and NSDictionary run so fast when searching for items, you need to understand hashing first. The general task of a hashing function is to make sure that it creates a unique hash value for each distinct item.
And the same item should always return the same hash value. A real hashing algorithm is complicated and requires lots of optimization, so I will use an easy hash calculation as an example.
With a string, I can use the integer value of each character in the alphabet table and the position of the character to compute the hash value of the string Figure 5—9. Figure 5—9. Using this hash function, you can see that you created two unique hash values of two distinct items. The purpose of the hash function is to generate a unique value for a specific piece of data.
Later, I will examine the benefits of having this unique representation of data. Hash was just discussed so you should have a clear picture of what the hash function does.
By default for a single object, these two things are the same. To make it clear, here is a class MyItem that overrides the isEqual: Therefore, people usually use the overridden method of the isEqual method to write the hash method.
If your isEqual method is determined by a variable identifier, your hash should be something like that in Listing 5—4. Listing 5—4. In Listing 5—4, I wanted to demonstrate how to write a hash algorithm yourself so I calculated a new hash value based on the string hash value. In a simple case like this, you can just return [self. A set is an unordered collection that contains no duplicate elements. In other words, NSSet contains no pair of elements e1 and e2 such that [e1 isEqual: Based on the unique characteristics of a set, people usually implement a set based on a hashing algorithm.
The hashing algorithm will make it really easy for a set to find any elements inside. Now, imagine that you have a set like that in Figure 5— Your set has the capacity to contain seven elements. Figure 5— As shown in Figure 5—8, if you want to do this with an array, you have to loop over that array to check for equality. First step: Second step: You check the item into the index 1 spot, as in Figure 5— The performance is: The first purpose is to check if that object is contained inside the set.
The second purpose is to find an object in the set that is equal to the object you have in hand. The last purpose is to see if the object inside the set has a specific property that satisfies a condition. As an example, in the isEqual: The performance for this case is: One way to access the element discussed in the search section is by creating a mock object that isEqual: The performance is O 1. It only provides you with a method to return a sorted array.
Based on the sorting performance of an array, I can estimate that the performance for a sorting in set is: O nlogn. Table 5—4. Set Manipulation Union: Like a set concept in mathematics, you may need to use a special calculation like union, intersect, and minus. If you need to manipulate collections with unique data a lot and are concerned with union, intersect, or minus, set is the best choice.
For example, you are given two lists: Now, you need to find three lists: I will leave this as an exercise for you to practice.
You should try it with both approaches: The specification for a hash table is you have a list of unique keys and that each key corresponds to a value, which does not need to be unique. The hash table utilizes the hash concept to make sure that it has a list of unique keys. The key is associated with an object, and that object can be accessed through the key. When you have a new pair of key values that you want to add into the NSMutableDictionary, you need to calculate the hash and add them in, like the way you did with NSMutableSet above.
The key collection is like a set and can only contain unique items. After the hashing function, you can determine the index of the key in the key collection, and then you insert both the key and value into the correct position. Illustration for the Dictionary concept In the simplest case where the key is an integer, you can understand that the NSMutableDictionary works similarly to an array, with the index in the left and the value on the right.
In the case of a dictionary, the main difference is that the key will be hashed to create an index. If you already know the key, then the performance for accessing the value is O 1. Either way will make you create a new array from the dictionary and then sort it. The performance is the same as if you sort an array: Table 5—5. For an object to be put as a key into a NSMutableDictionary, the corresponding class has to conform to nscopying protocol, and implement the method copyWithZone:.
I have finished the three main important data structures and their APIs that Cocoa Touch framework provides free as built-in data structures. These classes and their methods will handle most of your problems in a reasonable amount of time. Next, I will introduce other important and useful data structures for specific cases. Using these built-in and well-tested data structures should always be your first choice. Good when you need a collection that always sorted.
This is different than whenever you insert a new item into an array and you need to sort the array again. Searching in NSArray, even a sorted array, will always require a linear search because there is no way to know that the array is already sorted. Binary tree is better when you need to do a binary search for the item. Binary search compared to linear search is O logn compare to O n.
These are good when you need to build an API that only allows you to add at the end and get out from the end stack , or add at the end and get out from the beginning queue. You can easily implement them by a linked list or a normal array. This is an important concept in mathematics and computer science to demonstrate difficult problems.
Although many problems will be solved in server side, knowing about the graph will help you with many problems on the iPhone side. Linked List The specification and supported public methods API of a linked list and an array are usually the same in many cases. You can add an object in anywhere into a linked list, retrieve object from any index inside the linked list, check for the existing object inside the linked list, and sort the linked list.
However, the main difference between a linked list and an array is shown in Figure 5— Comparison between an array and a linked list As you can see, in an array, you store items in an indexed position and you can access any item inside the array using the index. However, in linked list, the main List object that holds the List will only hold the first and the last item. Tune Your App Using Algorithms and Data Structures object inside the linked list, you have to go from one of two sides: If you go by the first item, you move forward one by one item until you reach the index that you want.
If you go by the last item, you move backwards one by one item until you reach the index that you want. Figures 5—14 and 5—15 illustrate the process. Accessing the item at index 2 third item in the linked list Figure 5— You should download the source code project LinkedList for a full version of the sample.
I shall briefly explain the most important parts to make sure you fully understand the linked list concepts. There are two main objects in the implementation, Node and List. The List object holds the first and the last node, as shown in Figure 5— Every Node object contains three items: For the first item, the previous item will point to a nil object; similarly, for the last item, the next item will point to a nil object; see Listings 5—5 and 5—6.
Listing 5—5. ListNode ListNode. Linked List LinkedList. NSInteger index; end LinkedList. I will show you only the main implementation methods because others are similar or easy to implement yourself. There are six main methods that you will need to think about when implementing a linked list: Init - id initWithHead: Add Object - void addToFront: Adding to the end is similar and I leave it as an exercise for you.