iOS Programming Guide(2nd) 读书笔记
这是一本iOS4的入门书籍,有一些概念过时,有一些概念没有,但不妨碍学习一些最基本的概念。从头学起,从最基础学起。
Chapter 1
MVC
Xib
A XIB file is an XML representation of the archived objects. When you build a project, the XIB file is compiled into a NIB file. Developers manipulate XIB files (they’re easier to work with), and applications use NIB files (they’re smaller and easier to parse). When you build an application, the compiled NIB file is copied into the application’s bundle. An iOS application is really a directory containing the executable and any resources the executable uses. We refer to this directory as the bundle. When your application reads in the NIB file, the objects in the archive are brought to life. A complex application can have many NIB files that are read in as they are needed.
Deploy an app
To install an application on your development device, you need a developer certificate from Apple. Developer certificates are issued to registered iOS Developers who have paid the developer fee. This certificate grants you the ability to sign your code, which allows it to run on a device. Without a valid certificate, devices will not run your application.
Developer Certificate
This certificate file is added to your Mac’s keychain using Keychain Access. It is used to digitally sign your code.
App ID
The application identifier is a string that uniquely identifies your application on the App Store. Application identifiers typically look like this: com.bignerdranch.AwesomeApp, where the name of the application follows the name of your company. The App ID in your provisioning profile must match the bundle identifier of your application. A development profile, like you just created, will have a wildcard character (*) for its App ID
and therefore will match any bundle identifier.
Device ID (UDID)
This identifier is unique for each iOS device.
Provisioning Profile
This is a file that lives on your development device and on your computer. It references a Developer Certificate, a single App ID, and a list of the device IDs for the devices that the application can be installed on. When an application is deployed to a device, Xcode uses a provisioning profile on your computer to access the appropriate certificate. This certificate is used to sign the application binary. Then, the development device’s UDID is matched to one of the UDIDs contained within the provisioning profile, and the App ID is matched to the bundle identifier. The signed binary is then sent to your development device where it is confirmed by the same provisioning profile on the device and finally launched.
Chapter 2
Exceptions
In a language like C, we have functions, and when we call a function, code is executed. If we try and call a function that doesn’t exist, the compiler says, “I can’t do that, Joe,” and the code will fail to compile. This is known as a compile-time error. Objective-C, being a dynamically typed language, isn’t able to figure out at compile time whether an object will respond to a message. (An object only responds to a message if its class implements the associated method.) The compiler will warn you if it thinks you are sending a message to an object that won’t respond, but the code will still compile. If, for some reason (and there are many), you end up sending a message to an object that doesn’t respond, your application will throw an exception, also known as a run-time error.
Chapter 3
Autorelease pool draining
Chapter 4
Projects, targets, and frameworks
A project is a file that contains a list of references to other files (source code, resources, frameworks, and libraries) as well as a number of settings that lay out the rules for items within the project. Projects end in .xcodeproj, as in Whereami.xcodeproj. A project always has at least one target.
A target uses the files in the project to build a particular product. When you build and run, you build and run the target, not the project.
The product the target builds is typically an application, although it can be a compiled library or a unit test bundle. When you create a new project and choose a template, Xcode automatically creates a target for you.
A framework is a collection of related classes that you can add to a target. Cocoa Touch is a collection of frameworks. One of the benefits of Cocoa Touch being organized into frameworks is that you only have to add the frameworks that a target needs.
Delegation
Delegation is an object-oriented approach to callbacks. A callback is a function that is supplied in advance of an event and is called every time the event occurs. Some objects need to make a callback for more than one event. In a target-action pair, you have a target object that you send an action message to when an event occurs. The target must implement the action message, and, for each event, a new target-action pair must be created. With delegation, on the other hand, you set the delegate once and can then send it messages for different events. The delegate will implement the method for each event it wants to hear about.
Build Phases
Building an application in Xcode takes several steps. We call these steps build phases.
- Compile Sources This build phase contains the source code files that are compiled when this target is built. By default, any time you add a source code file to a project, it is added to this build phase.
- Link Binary With Libraries After your source code has been compiled, it is linked with the frameworks (libraries). This allows your code to use classes from these frameworks.
- Copy Bundle Resources After your code is compiled and linked, an executable is created and placed inside an application bundle, which is really just a folder. Then, the files listed in the Copy Bundle Resources phase are added to the bundle alongside the executable. These resources are the data files that your application uses at runtime, like MainWindow.xib and any images or sounds that are part of the application. By default, when you add a file to a project that is not source code, it is added to this build phase.
Preprocessing
The goal of the preprocessing phase is to create an intermediate file for each implementation file (.m). The intermediate file is still Objective-C code like the implementation file, but the intermediate file can get very large. To create an intermediate file, the preprocessor resolves all the preprocessor directives in the implementation file. Preprocessor directives are statements prefixed with the pound symbol (#), like #import. The resolution of a #import statement replaces the import statement with the contents of the imported file.
Compiling
Once the preprocessor has finished, the generated intermediate files are compiled. Compiling an intermediate file takes the Objective-C code and turns it into machine code. This machine code is stored in an object file, one for each intermediate file. The compiling phase – the transition to machine code – is where we see most of our errors as programmers. When the compiler doesn’t understand our code, it generates an error. We call errors generated during this phase compile-time errors or syntax errors. Compile-time errors are typically misplaced semicolons, unbalanced brackets ([]) or braces ({}), spelling or capitalization errors.
Linking
An object file contains the machine code for the methods implemented in the implementation file. However, within an implementation file, you use code from other implementation files. Instead of copying the code for this method into the object file, the compiler leaves a link to the other object file.
A framework is a collection of classes, and a class is defined by two files: a header file and an implementation file. A framework, however, has pre-compiled its implementation files and shoved the resulting object files into one or more library files. That’s why in Objective-C you can’t see the implementation files in a framework – they are already machine code.
If a link cannot be resolved (because the object file that contains the code cannot be found or because the object file doesn’t contain the referenced code), you get a linker error. Linker errors are more difficult for new developers to understand because they use unfamiliar terms and because there isn’t one line of code that generated the error.
Chapter 6
The drawRect: method
Each time an instance of UIView needs to be drawn (or redrawn), the system prepares a graphics context specifically for that view. Then the context is activated, and the message drawRect: is sent to the instance of UIView that is being drawn. The graphics context’s type is CGContextRef (Core Graphics Context Reference), and it is responsible for aggregating drawing commands and producing an image as a result. This image is the appearance of the view instance. A graphics context also stores its drawing state, which includes things like the current drawing color, coordinate system, and the current line width. Sometimes when drawing a view, you will use Objective-C to make calls defined in the UIKit framework that implicitly use the active graphics context. Other times, you will get hold of the graphics context explicitly and draw using the C functions of the Core Graphics framework.
@class
Notice the @class directive after the import statement. This is a forward declaration for the class. When you forward declare a class, you aren’t going as far as importing the header file; you are just informing the Header File of the class so the compiler can validate it. Forward declaring a class saves time when compiling – especially with large projects.
Redrawing Views
When a UIView instance is sent the message setNeedsDisplay, that view is marked for re-display. View subclasses send themselves setNeedsDisplay when their drawable content changes. When a view has marked itself for re-display, it is not immediately redrawn; instead, it is added to a list of views that need updating. Your application is a giant infinite loop called the run loop. The run loop’s job is to check for input (a touch, Core Location updates, data coming in through a network interface, etc.) and then find the appropriate handlers for that event (like an action or delegate method for an object). Those handler methods call other methods, which call more methods, and so on. Views are not redrawn until after the methods have completed and control returns to the run loop.
When control returns to the run loop, it says, “Well, a bunch of code was just executed. I’m going to check if any views need to be redrawn.” Then the run loop prepares the necessary drawing contexts and sends the message drawRect: to all of the views that have been sent setNeedsDisplay in this iteration of the loop. After a view has redrawn itself, its subviews are automatically asked to redraw themselves as well.
Chapter 7
File’s Owner
We need some way to connect objects loaded from the XIB file to objects that exist in memory before the XIB file is loaded. This way, the already existing CurrentTimeViewController could set its view instance variable to point at the view loaded from this XIB file. This is where the File’s Owner comes in. The File’s Owner is a placeholder object. When a XIB file is loaded, placeholder objects are not instantiated. You can think of a placeholder object as a hole in which existing objects can be placed so that connections can be made between them and the objects in the XIB file.
When your application first launches, an instance of UIApplication is created and it loads MainWindow.xib. The UIApplication is the File’s Owner. The File’s Owner of this file has a outlet connection for its delegate that has been connected to HypnoTimeAppDelegate. Therefore, after the XIB file loads, the delegate of the UIApplication is set to point at the HypnoTimeAppDelegate object – the reason why HypnoTimeAppDelegate gets sent the message application:didFinishLaunchingWithOptions:.
The View Controller Lifecycle and Low-Memory Warnings
A view controller, like any other object, is created through alloc and init. It does not, however, create its view at that time. Instead, it waits until the view is really needed before it calls loadView. When the system is running low on RAM, it issues a low-memory warning to the running application. The application responds by freeing up any resources that it doesn’t need at the moment and can easily recreate. View controllers, during a low-memory warning, are sent the message didReceiveMemoryWarning. The default implementation of this method will check to see if the view is currently on screen; if it is not, it is released. (If the view is on screen, nothing happens.)
A rule: Never access a view controller’s view in that view controller’s initialization method. If you have extra work you want to perform on the view, do so in viewDidLoad. This message is sent to a view controller each time it loads its view.
Chapter 8
FirstResponder
The window’s firstResponder is the object that gets sent all of the motion events. Motion events have nothing to do with the UIAccelerometer delegate. The system determines there is a shake by querying the accelerometer hardware and then sends the appropriate messages to the firstResponder.
Retina Display
For vector graphics, like HypnosisView’s drawRect: method and drawn text, you don’t need to do anything; the same code will render as crisply as the device allows. However, if you draw using Core Graphics functions, these graphics will appear differently on different devices. In Core Graphics, also called Quartz, we describe lines, curves, text, etc. in terms of points. On a non-Retina display, a point is 1x1 pixel. On a Retina display, a point is 2x2 pixels.
Given these differences, bitmap images (like JPEG or PNG files) will be unattractive if the image isn’t tailored to the device’s screen type. Say your application includes a small image of 25x25 pixels. If this image is displayed on a Retina display, then the image must be stretched to cover an area of 50x50 pixels. At this point, the system does a type of averaging called anti-aliasing to keep the image from looking jagged. The result is an image that isn’t jagged – but it is fuzzy.
You could use a larger file instead, but the averaging would then cause problems in the other direction when the image is shrunk for a non-Retina display. The only solution is to bundle two image files with your application: one at a pixel resolution equal to the number of points on the screen for non-Retina displays and one twice that size in pixels for Retina displays.
Fortunately, you do not have to write any extra code to handle which image gets loaded on which device. All you have to do is suffix the higher-resolution image with @2x. Then, when you use UIImage’s imageNamed: method to load the image, this method looks in the bundle and gets the file that is appropriate for the particular device.
Chapter 9
NSNotificationCenter
The notification center does not retain observers. If an object registers with the notification center, that object must unregister before it is deallocated. If it doesn’t, the next time a notification it registered for is posted, the center will try to send the object a message. Since that object has been deallocated, your application will crash.
Autosizing
You can click this control in six different places: on the four sides outside the inner box and along the vertical and horizontal axes inside the inner box. We call the outside four struts, and the inside two springs. Clicking on one of these areas toggles an autoresizing mask option. A solid red line means the option is on, and a dim red dotted line means the option is off.
A spring that is turned on tells the view to change size when its superview changes size. For example, if you turn on the horizontal spring, the view will change its width at the same rate its superview changes its width.
A strut tells the view to keep the margin between the view and its superview constant. For example, if you turn on the left strut, the view will maintain the distance from the left side of its superview when the superview changes its width.
Chapter 10
UITableViewCells
Table views can be broken up into sections, and each section has its own set of rows. A UITableViewCell is a subclass of UIView, and each row in a UITableView is a UITableViewCell. A table in iOS can only have one column, so a row only has one cell. The UITableViewCells are subviews of the UITableView.
. `
A cell itself has one subview – its contentView. The contentView is the superview for the content of the cell.
It also can draw an accessory indicator. The accessory indicator shows an action-oriented icon, such as a checkmark, a disclosure icon, or a fancy blue dot with a chevron inside. These icons are accessed through pre-defined constants for the appearance of the accessory indicator.
Reusing UITableViewCells
iOS devices have a limited amount of memory. If we were displaying a list with thousands of entries in a UITableView, we would have thousands of instances of UITableViewCell. To preserve the lives of iOS devices everywhere, you can reuse table view cells. When the user scrolls the table, some cells move offscreen. Offscreen cells are put into a pool of cells available for reuse. Then, instead of creating a brand new cell for every request, the data source first checks the pool. If there is an unused cell, the data source configures it with new data and returns it to the table view.
There is one problem: sometimes a UITableView has different types of cells. Occasionally, you have to subclass UITableViewCell to create a special look or behavior. However, different subclasses floating around the pool of reusable cells create the possibility of getting back a cell of the wrong type. You must be sure of the type of the cell returned to you so that you can be sure of what properties and methods it has.
Chapter 11
Editing Mode
UITableView has an editing property, and when this property is set to YES, the UITableView enters editing mode. Once the table view is in editing mode, the rows of the table can be manipulated by the user. The user can change the order of the rows, add rows, or remove rows. Editing mode does not allow the user to edit the content of a row.
NSBundle
To load a XIB file manually, you use NSBundle. This class is the interface between an application and the application bundle it lives in. When you want to access a file in the application bundle, you ask NSBundle for it. An instance of NSBundle is created when your application launches, and you can get a pointer to this instance by sending the message mainBundle to NSBundle.
XIB files are typically used to create the window and application delegate (MainWindow.xib) and to create the view for a view controller (for example, CurrentTimeViewController.xib). The code to load these XIB files is already written in the implementations of UIApplication and UIViewController.
However, as you can see, a XIB file can be used any time you wish to archive view objects, and any object can load a XIB file.
- (UIView *)headerView
{
// If we haven't loaded the headerView yet...
if (!headerView) {
// Load HeaderView.xib
[[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil];
}
return headerView;
}
Adding Rows && Deleting Rows && Moving Rows
Implement the corresponding DataSource Methods
Chapter 12
UINavigationController
When your application presents multiple screens of information, UINavigationController maintains a stack of those screens. The stack is an NSArray of view controllers, and each screen is the view of a UIViewController. When a UIViewController is on top of the stack, its view is visible.
When writing iOS applications, it is important to treat each UIViewController as its own little world.
UINavigationBar
Every UIViewController has a navigationItem property of type UINavigationItem. However, unlike UINavigationBar, UINavigationItem is not a subclass of UIView, so it cannot appear on the screen. Instead, the navigation item supplies the navigation bar with the content it needs to draw. When a UIViewController comes to the top of a UINavigationController’s stack, the UINavigationBar uses the UIViewController’s navigationItem to configure itself.
SEL
The SEL data type is a pointer to a selector. A selector is a unique ID for a method. Wrapping a method name in the @selector() directive returns the SEL that points at that method. Remember that a selector is the entire method name including any colons.
Notice that @selector() doesn’t care about the return type, argument types, or names of arguments – only the selector itself. Also, know that @selector() doesn’t check to see if the method actually exists. If you give a SEL to a button, that button will send the corresponding message regardless of whether the method is implemented by the target.
Pushing view controllers
When using a UINavigationController, you cannot simply store all of the possible view controllers in its stack. The viewControllers array of a navigation controller is dynamic – you start with a root view controller and add view controllers depending on user input.
Any view controller in a navigation controller’s stack can get a pointer to the navigation controller by sending itself the message navigationController.
Having a view controller push the next view controller is a common pattern. In any application with a UINavigationController, the navigation controller has one root view controller. The root view controller typically creates the next view controller, and the next view controller creates the one after that, and so on.
Chapter 13
A UIToolbar works a lot like a UINavigationBar in that you can add UIBarButtonItems to it. However, where a navigation bar has two bar button items, a toolbar has an array of items. You can place as many UIBarButtonItems in a toolbar as can fit on the screen.
When a view controller is modal, it takes over the entire screen until it has finished its work. To present a view modally, presentModalViewController:animated: is sent to the UIViewController whose view is on the screen. The view controller to be presented is passed to it, and its view slides up from the bottom of the screen.
A view controller should re-populate its view’s subviews with data whenever it is sent the message viewWillAppear:, eliminating the possibility that a low-memory warning could wipe out its content.
Chapter 14
Show a view controller’s view:
- Set it as the root view controller of the window
- Push it onto a UINavigationController’s stack
- Add it to a UITabBarController
- Present it modally
Every view controller has a property named modalViewController. If a view controller presents another view controller modally, then this property holds a pointer to that view controller. Every modally-presented view controller has a pointer named parentViewController that it sets to the view controller.
Chapter 15
Saving, Loading, and Multitasking
Every iOS application has its own application sandbox. An application sandbox is a directory on the filesystem that is barricaded from the rest of the filesystem. Your application must stay in its sandbox, and no other application can access your sandbox.
application bundle
This directory contains all the resources and the executable. It is read-only.
Library/Preferences/
This directory is where any preferences are stored and where the Settings application looks for application preferences. Library/Preferences is handled automatically by the class NSUserDefaults (which you will learn about in Chapter 19) and is backed up when the device is synchronized with iTunes.
tmp/
This directory is where you write data that you will use temporarily during an application’s runtime. You should remove files from this directory when done with them, and the operating system may purge them while your application is not running. It does not get backed up when the device is synchronized with iTunes. To get the path to the tmp directory in the application sandbox, you can use the convenience function NSTemporaryDirectory.
Documents/
This directory is where you write data that the application generates during runtime and that you want to persist between runs of the application. It is backed up when the device is synchronized with iTunes. If something goes wrong with the device, files in this directory can be restored from iTunes. For example, in a game application, the saved game files would be stored here.
Library/Caches/
This directory is where you write data that the application generates during runtime and that you want to persist between runs of the application. However, unlike the Documents directory, it does not get backed up when the device is synchronized with iTunes. A major reason for not backing up cached data is that the data can be very large and extend the time it takes to synchronize your device. Data stored somewhere else – like a web server – can be placed in this directory. If the user needs to restore the device, this data can be downloaded from the web server again.
Archiving
There are many ways to write data to the filesystem on iOS, and one of the most important is called archiving. Archiving is the process of taking one or more objects from memory and writing them to the filesystem. Unarchiving reads these objects back from the filesystem into memory.
Archiving works by creating an instance of NSCoder, which is essentially just a container for data, and placing objects and their data inside it. Once the NSCoder has all of the data you have instructed it to collect, it will be written to a specific file in the filesystem.
Not all objects can be archived – only those whose class conforms to the NSCoding protocol. The NSCoding protocol has two methods, and both are required: encodeWithCoder: (for archiving) and initWithCoder: (for unarchiving).
Like NSString, the classes NSDictionary and NSArray have writeToFile: and initWithContentsOfFile: methods. In order to write objects of these types to the filesystem in this fashion, they must contain only property list serializable objects. The only objects that are property list serializable are NSString, NSNumber, NSDate, NSData, NSArray, and NSDictionary. When an NSArray or NSDictionary is written to the filesystem with these methods, an XML property list is created.
Application States, Transitions, and Multitasking
An application in the suspended state cannot execute code, and any resources it doesn’t need while suspended are released. A suspended application is essentially freeze-dried and can be quickly thawed when the user relaunches it. The resources that are released are ones that can be reloaded, like cached images, system-managed caches, and other graphics data.
The Application Bundle
When you build an iOS application project in Xcode, you create an application bundle. The application bundle contains the application executable and any resources you have bundled with your application. Resources are things like XIB files, images, audio files – any files that will be used at runtime. When you add a resource file to a project, Xcode is smart enough to realize that it should be bundled with your application and categorizes it accordingly.
Also, files within the application bundle are read-only. You cannot modify them nor can you dynamically add files to the application bundle at runtime. Files in the application bundle are typically things like button images, interface sound effects, or the initial state of a database you ship with your application. You will use this method in later chapters to load these types of resources at runtime.
Chapter 16
Subclassing UITableViewCell
You subclass UITableViewCell by changing the view objects in a cell’s contentView. Adding subviews to the contentView instead of directly to the UITableViewCell subclass is important because the cell will resize the contentView at certain times. For example, when a table view enters editing mode, the contentView redraws itself to make room for the editing controls.
If you were to add subviews directly to the UITableViewCell, these editing controls would obscure the subviews. The cell doesn’t know to adjust its size when entering edit mode, but the contentView does.
Laying out subviews
When a table view’s data source is asked for a cell, it creates a cell, configures its content, and returns it to the table view. The table view then adds the cell as a subview of itself and positions and sizes the cell. Therefore, when you first create a cell, it doesn’t know its size quite yet. For this reason, you do not set the frames of a cell’s content view at initialization time. Instead, you wait until the cell knows how big it is.
First, you always invoke the superclass’ implementation of layoutSubviews. Invoking this method allows the UITableViewCell to layout its subview, the contentView. Then, you get the bounds of the contentView to find out how much area you have to work with when sizing and positioning all of the subviews. (If you don’t invoke the superclass’ implementation of layoutSubviews, the bounds of the contentView may not be correct.) Finally, you set the frame of each subview relative to the contentView’s bounds.
Chapter 18
Internationalization is making sure your native cultural information is not hard-coded into your application. (By cultural information, we mean language, currency, date formats, number formats, and more.) Localization, on the other hand, is providing the appropriate data in your application based on the user’s Language and Region Format settings. NSLocale knows how different regions display symbols, dates, and decimals and whether they use the metric system.
Localizing Resources
Localization is the process by which application-specific substitutions given a region or a language setting are created. This usually means one of two things:
- Generating multiple copies of resources like images, sounds, and interfaces for different regions and languages
- Creating and accessing “strings tables” to translate text into different languages
ibtool
NSLocalizedString and Strings Tables
A strings table is a file containing a list of key-value pairs for all of the strings your application uses and their associated translations.
NSLocalizedString().
NSBundle’s Role in Internationalization
The real work of adding a localization is done for you by the class NSBundle. For example, when a UIViewController is initialized, it is given two arguments: the name of a XIB file and an NSBundle object. The bundle argument is typically nil, which is interpreted as the application’s main bundle. (The main bundle is another name for the application bundle – all of the resources and the executable for the application. When an application is built, all of the lproj directories are copied into this bundle.)
When the view controller loads its view, it asks the bundle for the XIB file. The bundle, being very smart, checks the current language settings of the device and looks in the appropriate lproj directory. The path for the XIB file in the lproj directory is returned to the view controller and loaded.
NSBundle knows how to search through localization directories for every type of resource using the instance method pathForResource:ofType:. When you want a path to a resource bundled with your application, you send this message to the main bundle. Here’s an example using the resource file myImage.png:
NSString *path = [[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"];
The bundle first checks to see if there is a myImage.png file in the top level of the application bundle. If so, it returns the full path to that file. If not, the bundle gets the device’s language settings and looks in the appropriate lproj directory to construct the path. If no file is found, it returns nil.
This is why you must delete and clean an application when you localize a file. The previous un-localized file will still be in the root level of the application bundle because Xcode will not delete a file from the bundle when you re-install. Even though there are lproj folders in the application bundle, the bundle finds the top-level file first and returns its path.
Chapter 19
NSUserDefaults
These temporary defaults are placed in the registration domain of NSUserDefaults. Any preferences set by the user are stored in a different domain, the application domain.
By default, the application domain is empty: there are no keys and no values. The first time a user changes a setting, a value is added to the application domain for the specified key. When you ask the NSUserDefaults for the value of a preference, it first looks in the application domain. If there is a value for that key, then the user has set a preference, and the NSUserDefaults returns that value. If not, the NSUserDefaults looks in the registration domain and finds the temporary default.
The application domain is always saved to disk; that’s why it remembers user preferences on the next launch. The registration domain is not, and its values must be set every time the application launches. To set the values of the registration domain, you create an NSDictionary with a key-value pair for each preference you plan on using in your application. Then, you send the dictionary as an argument to the message registerDefaults: of NSUserDefaults.
Typically, you send the registerDefaults: message before any object is able to access the instance of NSUserDefaults. This means before the instance of the application delegate is created. What comes before the creation of the WhereamiAppDelegate? The creation of the WhereamiAppDelegate class. Like any object, a class also must be initialized before it can receive messages. So, after a class is created but before it receives its first message, it is sent the message initialize.
+ (void)initialize
{
NSDictionary *defaults = [NSDictionary
dictionaryWithObject:[NSNumber numberWithInt:1]
forKey:WhereamiMapTypePrefKey];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
}
Chapter 21
The Static Analyzer
When the static analyzer checks the code, it examines each function and method individually by iterating over every possible code path. A method can have a number of control statements (if, for, switch, etc.). The conditions of these statements will dictate which code is actually executed. A code path is one of the possible paths the code will take given these control statements.
Xcode Schemes
A workspace is a collection of projects, and a project is a collection targets and files. A target has a number of build settings and phases that reference files from its project. When built, a target creates a product, which is usually an application. A scheme contains one or more targets and specifies what to do with the product or products.
When you create a new project, a scheme with the same name as the project is created for you.
Creating a new scheme
As projects become larger and more complicated, they require more specific testing and debugging. This can result in more time fiddling with the options for scheme actions. To avoid constant fiddling, we typically create new schemes for common situations. For example, if we have an application that consumes a lot of memory, we might want to routinely run the Allocations instrument on it. Instead of having Instruments ask which instrument to use when we profile the application, we can set up a new scheme that always runs Allocations.
Build Settings
Every target includes build settings that describe how it should be built. Every project also has build settings that serve as defaults for all the targets within the project.
The far right column shows the iOS Default settings; these serve as the project’s defaults, which it can override. The next column to the left shows the project’s settings, and the one after that shows the currently selected target’s settings. The Resolved column shows which setting will actually be used; it is always be equal to the left-most specified value.
Each target and project has multiple build configurations. A build configuration is a set of build settings. When you create a project, there are two build configurations: debug and release. The build settings for the debug configuration make it easier to debug your application, while the release settings turn on optimizations to speed up execution.
When performing a scheme action, the scheme will use one of these configurations when building its targets. You can specify the build configuration that the scheme uses in the scheme editor in the option for Build Configuration in the Info pane.
Chapter 22
At its core, an instance of CALayer is a buffer containing a bitmap. When you draw a layer (or a stack of layers), the rendering is hardware-accelerated. This makes drawing a layer to the screen incredibly fast. A CAAnimation object causes a change over time.
Layers and views
A view doesn’t know how to draw to the screen; it only knows how to draw to an instance of CALayer. When you instantiate a view, it creates a layer, and when the view draws, it is drawing on its layer. We call layers created by views implicit layers. Because every view has a layer, there is a matching layer hierarchy that mimics the view hierarchy. After the views draw on their layers, the layers are composited to the screen.
UIView is a subclass of UIResponder. A view is really an abstraction of a visible object that can be interacted with on the screen, wrapped into a tidy class. A layer, on the other hand, is all about the drawing.
Creating a CALayer
Not all layers are implicit layers. You can create a layer by sending alloc to the class CALayer. Layers created this way are called explicit layers.
Layer Content
A layer is simply a bitmap, and its contents can be set programmatically or with an image. To set the contents programmatically, you either subclass CALayer or assign a delegate to an instance of CALayer. The delegate then implements drawing routines.
Because layers exist in a hierarchy, they can have sublayers, and each layer has a pointer back to its parent layer called superlayer. When a layer is composited to the screen, it is copied to the screen, and then each sublayer is composited atop it. Therefore, a layer always draws on top of its superlayer.
When the Z-axis is discussed, some developers imagine that perspective is applied, and they expect a layer to appear larger as its zPosition increases. However, Core Animation layers are presented orthographically; they do not appear as different sizes based on their zPositions.
Implicitly Animatable Properties
Several of the properties of CALayer are implicitly animatable. This means that changes to these properties are automatically animated when their setters are called.
To disable an implicit animation, you can use an animation transaction. Animation transactions allow you to batch animations and set their parameters, like the duration and animation curve. To begin a transaction, you send the message begin to the class CATransaction. To end a transaction, you send commit to CATransaction. Within the begin and commit block, you can set properties of a layer and also set values for the transaction as a whole.
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction commit];
Programmatically Generating Content
- Subclass - drawInContext:
- Delegate - drawLayer:inContext:
For both subclassing and delegation, you must send an explicit setNeedsDisplay to the layer in order for these methods to be invoked. Otherwise, the layer thinks it doesn’t have any content and won’t draw.
Layers, Bitmaps, and Contexts
A layer is simply a bitmap – a chunk of memory that holds the red, green, blue, and alpha values of each pixel. When you send the message setNeedsDisplay to a UIView instance, that method is forwarded to the view’s layer. After the run loop is done processing an event, every layer marked for re-display prepares a CGContextRef. Drawing routines called on this context generate pixels that end up in the layer’s bitmap.
How do drawing routines get called on the layer’s context? After a layer prepares its context, it sends the message drawLayer:inContext: to its delegate. The delegate of an implicit layer is its view, so in the implementation for drawLayer:inContext:, the view sends drawRect: to itself.
A layer creates the same kind of context when it needs to redraw its contents.
Chapter 23
An animation object drives change over time. An animation object is an instruction set (“Move from point A to point B over 2 seconds”) that can be added to a CALayer instance.
Animation Objects
CAPropertyAnimation is a subclass of CAAnimation that extends the ability of its superclass by adding the ability to change the properties of a layer. Each property animation has a key path of type NSString. This string is the name of an animatable property of a CALayer. Many of CALayer’s properties are animatable. CAPropertyAnimation is an abstract superclass. To create an animation object that modifies a property of a layer, you use one of the two concrete subclasses of CAPropertyAnimation: CABasicAnimation and CAKeyframeAnimation.
CABasicAnimation has two properties: fromValue and toValue, and it inherits CAAnimation’s duration property. When a basic animation is added to a layer, the property to be animated is set to the value in fromValue. Over the time specified by duration, the value of the property is interpolated linearly from fromValue to toValue.
The difference between CABasicAnimation and CAKeyframeAnimation is that a basic animation only interpolates two values while a keyframe animation can interpolate as many values as you give it. These values are put into an NSArray in the order in which they are to occur. This array is then set as the values property of a CAKeyframeAnimation instance. Each value in the values property is called a keyframe. Keyframes are the values that the animation will interpolate; the animation will take the property it is animating through each of these keyframes over its duration. A basic animation is really a keyframe animation that is limited to two keyframes. (In addition to allowing more than two keyframes, CAKeyframeAnimation adds the ability to change the timing of each of the keyframes)
A CAAnimationGroup instance holds an array of animation objects. When an animation group is added to a layer, the animations run concurrently.
CATransition animates layers as they are transitioning on and off the screen. On Mac OS X, CATransition is made very powerful by Core Image Filters. In iOS, it can only do a couple of simple transitions like fading and sliding. (CATransition is used by UINavigationController when pushing a view controller’s view onto the screen.) CATransaction turn off implicit animations for any layer modifications occurring within the transaction.
The Presentation Layer and the Model Layer
CALayer as having two parts: the content that gets composited onto the screen, and a set of parameters that describe how it should be composited: opacity, transform, position, etc. When a layer is being animated, it actually has two copies of these parameters: the model version and the presentation version. The presentation parameters are the ones that are being smoothly changed by the animation object. The model parameters are the persistent ones – the ones that will be used once the animation is over. So, when a layer is being animated, its content is composited to the screen using the presentation parameters. When it is animation-less, the model parameters are used.
Chapter 24
A block is a set of instructions that can be passed around as a variable and called like a function. Blocks provide conveniences for a programmer and performance boosts for applications. If you are familiar with other high-level languages, you may know blocks as closures or anonymous functions. Block captures variables which separates it from function pointers. You can define the block’s code at the same point where the block is registered as a callback. This keeps your code nice and clean.
Categories have another use: pseudo-private methods.
Pros and Cons of Callback Options
Callbacks have two major components: the process of registering it and the code for the callback.
You use target-action when you have a close relationship between the two objects (like a view controller and one of its views) and when there may be many instances of object that call back.
Delegation is used when an object receives many events and it wants the same object to handle each of those events.
Notifications are used when you want multiple objects to invoke their callback for the same event and/ or when two objects are not related.
Blocks are the outliers when it comes to callbacks because they are not an object-oriented approach. Blocks are useful when the callback is going to happen only once or when the callback is just a quick and simple task (like updating a progress bar). One of the reasons blocks are better suited for this one-shot behavior is because they will retain any objects they reference.
An approach to callbacks we have not discussed is subclassing.
Chapter 25
An NSURL instance contains the location of a web application in URL format.
An NSURLRequest instance holds all the data necessary to communicate with a web server.
An NSURLConnection instance is responsible for actually making the connection to a web server, sending the information in its NSURLRequest, and gathering the response from the server.