- •C and Objective-C
- •How this book works
- •How the life of a programmer works
- •Installing Apple’s developer tools
- •Getting started with Xcode
- •Where do I start writing code?
- •How do I run my program?
- •So what is a program?
- •Don’t stop
- •Types
- •A program with variables
- •Challenge
- •Boolean variables
- •When should I use a function?
- •How do I write and use a function?
- •How functions work together
- •Local variables, frames, and the stack
- •Recursion
- •Looking at the frames in the debugger
- •return
- •Global and static variables
- •Challenge
- •printf()
- •Integer operations
- •Integer division
- •Operator shorthand
- •Floating-point numbers
- •Tokens for displaying floating-point numbers
- •The while loop
- •The for loop
- •break
- •continue
- •The do-while loop
- •Challenge
- •Getting addresses
- •Storing addresses in pointers
- •Getting the data at an address
- •How many bytes?
- •NULL
- •Stylish pointer declarations
- •Challenges
- •Writing pass-by-reference functions
- •Avoid dereferencing NULL
- •Creating and using your first object
- •Message anatomy
- •Objects in memory
- •Challenge
- •Nesting message sends
- •Multiple arguments
- •Sending messages to nil
- •Challenge
- •Challenge
- •NSMutableArray
- •Reference pages
- •Quick Help
- •Other options and resources
- •Accessor methods
- •Dot notation
- •Properties
- •self
- •Multiple files
- •Challenge
- •Overriding methods
- •super
- •Challenge
- •Object ownership and ARC
- •Creating the Asset class
- •Adding a to-many relationship to Employee
- •Challenge
- •Retain cycles
- •Weak references
- •Zeroing of weak references
- •For the More Curious: Manual reference counting and ARC History
- •Retain count rules
- •NSArray/NSMutableArray
- •Immutable objects
- •Sorting
- •Filtering
- •NSSet/NSMutableSet
- •NSDictionary/NSMutableDictionary
- •Preprocessor directives
- •#include and #import
- •#define
- •Global variables
- •enum
- •#define vs global variables
- •Writing an NSString to a file
- •Reading files with NSString
- •Writing an NSData object to a file
- •Reading an NSData from a file
- •Target-action
- •Helper objects
- •Notifications
- •Which to use?
- •Callbacks and object ownership
- •Challenge
- •Getting started with iTahDoodle
- •BNRAppDelegate
- •Adding a C helper function
- •Objects in iTahDoodle
- •Model-View-Controller
- •The application delegate
- •Setting up views
- •Running on the iOS simulator
- •Wiring up the table view
- •Adding new tasks
- •Saving task data
- •For the More Curious: What about main()?
- •Edit BNRDocument.h
- •A look at Interface Builder
- •Edit BNRDocument.xib
- •Making connections
- •Revisiting MVC
- •Edit BNRDocument.m
- •Writing init methods
- •A basic init method
- •Using accessors
- •init methods that take arguments
- •Deadly init methods
- •Property attributes
- •Mutability
- •Lifetime specifiers
- •copy
- •More about copying
- •Advice on atomic vs. nonatomic
- •Key-value coding
- •Non-object types
- •Defining blocks
- •Using blocks
- •Declaring a block variable
- •Assigning a block
- •Passing in a block
- •typedef
- •Return values
- •Memory management
- •The block-based future
- •Challenges
- •Anonymous block
- •NSNotificationCenter
- •Bitwise-OR
- •Bitwise-AND
- •Other bitwise operators
- •Exclusive OR
- •Complement
- •Left-shift
- •Right-shift
- •Using enum to define bit masks
- •More bytes
- •Challenge
- •char
- •char *
- •String literals
- •Converting to and from NSString
- •Next Steps
- •Index
typedef
[[NSNotificationCenter defaultCenter] addObserver:logger
selector:@selector(zoneChange:)
name:NSSystemTimeZoneDidChangeNotification
object:nil];
you identify a method (typically using @selector()), and then you implement that method somewhere else in the file:
- (void)zoneChange:(NSNotification *)note
{
NSLog(@"The system time zone has changed!");
}
You could use the NSNotificationCenter method addObserverForName:object:queue:usingBlock: and pass a block instead. With this method, you hand the NSNotificationCenter the instructions right then, so you don’t have to put the code for the callback somewhere else. Anyone reading your code will see the instructions and the message send to the NSNotificationCenter in the same chunk of code. (You’ll get to make exactly this change to your Callbacks program in a challenge at the end of the chapter.)
typedef
Block syntax can be confusing, but you can make it friendlier using the typedef keyword that you learned about in Chapter 10. Remember that typedefs belong at the top of the file or in a header, outside of any method implementations. In main.m, add the following line of code:
#import <Foundation/Foundation.h>
typedef void (^ArrayEnumerationBlock)(id, NSUInteger, BOOL *);
int main (int argc, const char * argv[])
{
Notice that this looks identical to a block variable declaration. However, here we are defining a type rather than a variable, so we put an appropriate type name next to the caret. This allows us to simplify declarations of similar blocks. Instead of declaring devowelizer this way
void (^devowelizer)(id, NSUInteger, BOOL *);
you can replace that line with the following declaration:
ArrayEnumerationBlock devowelizer;
This makes your block variable declaration a bit more familiar. Note that the block type itself only defines the block’s arguments and return types; it has no bearing on the set of instructions within a block of that type.
Return values
Finally, when a block returns a value, you can call its block variable like a function.
double (^divBlock)(double,double) = ^(double k, double j) { return k/j;
}
233
Chapter 32 Blocks
In this code, you’ve declared divBlock as a block variable that returns a double and expects two doubles as arguments. Then you assigned it a value which includes the instruction to return the result of dividing the two arguments.
You can use this block like so:
double quotient = divBlock(42.0, 12.5);
Memory management
Like primitive variables, blocks are created and stored on the stack. That is, the block will be destroyed along with the stack frame when the function or method that created the block returns. Sometimes, however, your block needs to live longer than that. For example, it could become an instance variable of an object. In this case, you must copy your block from the stack to the heap.
To copy a block from the stack to the heap, you send it the copy message:
ArrayEnumerationBlock iVarDevowelizer = [devowelizer copy];
Now a copy of your block exists on the heap. It is now a heap-based block instead of a stack-based block, and the new block variable is a pointer to the block.
Methods that take blocks as arguments, such as NSArray’s enumerateObjectsUsingBlock: or
NSNotificationCenter’s addObserverForName:object:queue:usingBlock:, are expected to copy blocks passed to them to keep them around. In doing so, they create pointers – and strong references – to those blocks.
So we’ve seen blocks be declared, assigned values, and passed like variables. We’ve also seen that they look like functions. Now we’re sending a block a message as if it were an object.
A heap-based block behaving like an object comes with some memory management issues:
What about variables that are used with the block?
A block typically uses other variables (both primitive and pointers to objects) within its code that were created outside of it. To make sure these external variables will be available for as long as the block needs them, the variables are captured by the block when the copy is made.
For primitive variables, this means the values are copied and stored as local variables within the block. For pointers, the block itself will keep a strong reference to any objects it references. That means that any objects referred to by the block are guaranteed to live at least as long as the block itself. (If you’ve been wondering about the difference between blocks and function pointers, it’s right here. Let’s see a function pointer do that!)
As an example, take a look at our VowelMovement program. The devowelizer block mentions two objects that were created outside of the block: newStrings (an array for storing the modified versions of strings) and string (the current string to be copied for modification). devowelizer will keep strong references to both of these objects, keeping them alive as long as the block itself exists.
Can these strong references lead to retain cycles/circular references?
You bet. The fix is the same: one of the references needs to become a weak reference. To do this, declare a __weak pointer outside the block and then reference this pointer within the block instead.
234