BNR Reading Notes2-Objective-C and Foundation
All Objective-C programming is done with the Foundation framework. A framework is library of classes that you use to write programs
Objects
object : An object is similar to a struct (such as the struct Person you created in Chapter 11). Like a struct, an
object can contain several pieces of related data. In a struct, we called them members. In an object, we
call them instance variables (or you might hear “ivars”).
classes : A class describes a certain type of object by listing the instance variables and methods that object will have. A class can describe an object that represents
•a concept, like a date, a string, or a set
•something in the real world, like a person, a location, or a checking account
int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *now = [NSDate date]; NSLog(@"This NSDate object lives at %p", now); } return 0; }
Methods and messages
Methods are like functions. They contain code to be executed on command. In Objective-C, to execute
the code in a method, you send a message to the object or class that has that method.
Another message
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *now = [NSDate date]; NSLog(@"This NSDate object lives at %p", now); NSLog(@"The date is %@", now); } return 0; }
Class methods vs. instance methods
You sent the date message to the NSDate class. date is a class method. Typically, class methods create
an instance of the class and initialize its instance variables.
More Messages
A message with an argument
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *now = [NSDate date]; NSLog(@"This NSDate object lives at %p", now); NSLog(@"The date is %@", now); double seconds = [now timeIntervalSince1970]; NSLog(@"It has been %f seconds since the start of 1970.", seconds); NSDate *later = [now dateByAddingTimeInterval:100000]; NSLog(@"In 100,000 seconds it will be %@", later); } return 0; }
Multiple arguments
Nesting message sends
double seconds = [[NSDate date] timeIntervalSince1970]; NSLog(@"It has been %f seconds since the start of 1970", seconds);
alloc and init
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *now = [NSDate date]; NSDate *now = [[NSDate alloc] init]; NSLog(@"This NSDate object lives at %p", now); NSLog(@"The date is %@", now); double seconds = [now timeIntervalSince1970]; NSLog(@"It has been %f seconds since the start of 1970.", seconds); NSDate *later = [now dateByAddingTimeInterval:100000]; NSLog(@"In 100,000 seconds it will be %@", later); NSCalendar *cal = [NSCalendar currentCalendar]; NSLog(@"My calendar is %@", [cal calendarIdentifier]); unsigned long day = [cal ordinalityOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:now]; NSLog(@"This is day %lu of the month", day); } return 0; }
Sending messages to nil
id
Objects and Memory
In this chapter, you will learn about the life of objects on the heap and how heap memory is managed.
On pointers and their values
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *currentTime = [NSDate date]; NSLog(@"currentTime's value is %p", currentTime); } return 0; }
Memory management
ARC
NSString
NSString is another class like NSDate. Instances of NSString hold character strings. Objective-C
developers use NSString instances to hold and manipulate text in their programs.
Creating instances of NSString
In code, you can create an instance of NSString like this:
NSString *lament = @"Why me!?";
NSString methods
- (NSUInteger)length; - (BOOL)isEqualToString:(NSString *)other; - (NSString *)uppercaseString;
NSArray
NSArray is another commonly used Objective-C class. An instance of NSArray holds a list of pointers
to other objects.
Creating arrays
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // Create three NSDate objects NSDate *now = [NSDate date]; NSDate *tomorrow = [now dateByAddingTimeInterval:24.0 * 60.0 * 60.0]; NSDate *yesterday = [now dateByAddingTimeInterval:-24.0 * 60.0 * 60.0]; // Create an array containing all three NSArray *dateList = @[now, tomorrow, yesterday]; } return 0; }
Accessing arrays
Iterating over arrays
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // Create three NSDate objects NSDate *now = [NSDate date]; NSDate *tomorrow = [now dateByAddingTimeInterval:24.0 * 60.0 * 60.0]; NSDate *yesterday = [now dateByAddingTimeInterval:-24.0 * 60.0 * 60.0]; // Create an array containing all three NSArray *dateList = @[now, tomorrow, yesterday]; // Print a couple of dates NSLog(@"The first date is %@", dateList[0]); NSLog(@"The third date is %@", dateList[2]); // How many dates are in the array? NSLog(@"There are %lu dates", [dateList count]); // Iterate over the array NSUInteger dateCount = [dateList count]; for (int i = 0; i < dateCount; i++) { NSDate *d = dateList[i]; NSLog(@"Here is a date: %@", d); } } return 0; }
for (NSDate *d in dateList) { NSLog(@"Here is a date: %@", d); }
NSMutableArray
Your First Class
#import <Foundation/Foundation.h> @interface BNRPerson : NSObject { // BNRPerson has two instance variables float _heightInMeters; int _weightInKilos; } @end
#import <Foundation/Foundation.h> @interface BNRPerson : NSObject { // BNRPerson has two instance variables float _heightInMeters; int _weightInKilos; } // BNRPerson has methods to read and set its instance variables - (float)heightInMeters; - (void)setHeightInMeters:(float)h; - (int)weightInKilos; - (void)setWeightInKilos:(int)w; // BNRPerson has a method that calculates the Body Mass Index - (float)bodyMassIndex; @end
Accessor methods
mikey.weightInKilos = 96; mikey.heightInMeters = 1.8; int weight = [mikey weightInKilos]; float height = [mikey heightInMeters];
Accessor naming conventions
self
Inside any method, you have access to the implicit local variable self. self is a pointer to the object
that is running the method. It is used when an object wants to send a message to itself.
Multiple files
Multiple files
Class prefixes
Properties
Objective-C has a convenient shortcut called properties that lets you skip declaring instance variables
and declaring and implementing accessor methods. Using properties simplifies your class’s code.
Declaring properties
#import <Foundation/Foundation.h> @interface BNRPerson : NSObject // BNRPerson has two properties @property (nonatomic) float heightInMeters; @property (nonatomic) int weightInKilos; { // BNRPerson has two instance variables float _heightInMeters; int _weightInKilos; } // BNRPerson has methods to read and set its instance variables - (float)heightInMeters; - (void)setHeightInMeters:(float)h; - (int)weightInKilos; - (void)setWeightInKilos:(int)w; // BNRPerson has a method that calculates the Body Mass Index - (float)bodyMassIndex; @end
Property attributes
atomic or nonatomic
readonly or readwrite
Dot notation
Inheritance
Overriding methods
super
Object Instance Variables and
Properties
Object ownership and ARC
Adding a to-many relationship to BNREmployee
Class Extensions
#import "BNREmployee.h" // A class extension @interface BNREmployee () { NSMutableArray *_assets; } @property (nonatomic) unsigned int officeAlarmCode; @end @implementation BNREmployee ... In BNREmployee.h, remove the _assets declaration: #import "BNRPerson.h" @class BNRAsset; @interface BNREmployee : BNRPerson { NSMutableArray *_assets } @property (nonatomic) unsigned int employeeID; @property (nonatomic) NSDate *hireDate; @property (nonatomic, copy) NSArray *assets; - (void)addAsset:(BNRAsset *)a; - (unsigned int)valueOfAssets; @end
Preventing Memory Leaks
Strong reference cycles
This is known as a strong reference cycle. Strong
reference cycles are a very common source of memory leaks.
Weak references
#import <Foundation/Foundation.h> @class BNREmployee; @interface BNRAsset : NSObject @property (nonatomic, copy) NSString *label; @property (nonatomic, weak) BNREmployee *holder; @property (nonatomic) unsigned int resaleValue; @end
Collection Classes
NSSet/NSMutableSet
NSDictionary/NSMutableDictionary
Immutable objects
Most beginning programmers are surprised by the immutability of NSArray, NSSet, and NSDictionary.
Why would anyone want a list that cannot be changed? The reasons are performance and security:
Sorting arrays
NSSortDescriptor *voa = [NSSortDescriptor sortDescriptorWithKey:@"valueOfAssets" ascending:YES]; NSSortDescriptor *eid = [NSSortDescriptor sortDescriptorWithKey:@"employeeID" ascending:YES]; [employees sortUsingDescriptors: @[voa, eid]]; NSLog(@"Employees: %@", employees);
Filtering
// Print out the CEO's information NSLog(@"CEO: %@", executives[@"CEO"]); executives = nil; NSPredicate *predicate = [NSPredicate predicateWithFormat: @"holder.valueOfAssets > 70"]; NSArray *toBeReclaimed = [allAssets filteredArrayUsingPredicate:predicate]; NSLog(@"toBeReclaimed: %@", toBeReclaimed); toBeReclaimed = nil; NSLog(@"Giving up ownership of arrays"); allAssets = nil; employees = nil;
Constants
#include and #import
// Include the headers I wrote for Pet Store operations #import "PetStore.h" // Include the headers for the OpenLDAP libraries #import <ldap.h>
#define
#define M_PI 3.14159265358979323846264338327950288
#define MAX(A,B) ((A) > (B) ? (A) : (B))
Global variables
NSString * const NSLocaleCurrencyCode = @”currency”;
enum
typedef NS_ENUM(int, BlenderSpeed) { BlenderSpeedStir, BlenderSpeedChop, BlenderSpeedLiquify, BlenderSpeedPulse, BlenderSpeedIceCrush };
Writing Files with NSString and
NSData
Writing an NSString to a file
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSMutableString *str = [[NSMutableString alloc] init]; for (int i = 0; i < 10; i++) { [str appendString:@"Aaron is cool!\n"]; } [str writeToFile:@"/tmp/cool.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; NSLog(@"done writing /tmp/cool.txt"); } return 0; }
NSError
// Declare a pointer to an NSError object, but do not instantiate it. // The NSError instance will only be created if there is, in fact, an error. NSError *error; // Pass the NSError pointer by reference to the NSString method BOOL success = [str writeToFile:@"/tmp/cool.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error]; // Test the returned BOOL, and query the NSError if the write failed if (success) { NSLog(@"done writing /tmp/cool.txt"); } else { NSLog(@"writing /tmp/cool.txt failed: %@", [error localizedDescription]); }
Reading files with NSString
Writing an NSData object to a file
Writing an NSData object to a file
Finding special directories
• NSApplicationDirectory • NSLibraryDirectory • NSUserDirectory • NSDocumentDirectory • NSDesktopDirectory • NSCachesDirectory • NSApplicationSupportDirectory • NSDownloadsDirectory • NSMoviesDirectory • NSMusicDirectory • NSPicturesDirectory • NSTrashDirectory
Callbacks
In Objective-C, there are four forms that a callback can take:
• Target-action: Before the wait begins, you say “When this event happens, send this message to
this object.” The object receiving the message is the target. The selector for the message is the
action.
• Helper objects: Before the wait begins, you say “Here is an object that will take on a role that
helps another object do its job. When one of the events related to this role occurs, send a message
to the helper object.” Helper objects are often known as delegates or data sources.
• Notifications: There is an object called the notification center. When an event happens, a
notification associated with that event will be posted to the notification center. Before the wait
begins, you tell the notification center “This object is interested in this kind of notification. When
one is posted, send this message to the object.”
• Blocks: A block is a just a chunk of code to be executed. Before the wait begins, you say “Here is
a block. When this event happens, execute this block.”
Blocks
Declaring a block variable