Objective-C Programming The Big Nerd Ranch Guide 笔记 19-37

Properties are either atomic or nonatomic, The difference has to do with multithreading. atomic is the default value.

Properties are either readonly or readwrite. readwrite is the default value.

Whenever you declare a property that points to an NSString or an NSArray, you should include the copy attribute.

Property gets an instance variable, such as _hireDate.

A instance of a subclass can stand in for an instance of the superclass without problems because it inherits everything in the superclass. 

e.g. BNRPerson *mikey = [[BNREmployee alloc] init];

All objects inherit from NSObject.

 

// the token %@ to get an object to describe itself. The %@ token sends a description message to the object pointed to by the corresponding variable.

NSDate *now = [NSDate date]; //创建NSDate实例,并赋值为当前日期

NSLog(@"%@", now);

 

Categories of object instance variables:

Object-type attributes: a pointer to a simple, value-like object like an NSString or an NSDate.

To-one relationship: a pointer to a single complex object.

To-many relationships: a pointer to an instance of a collection class, such as an NSMutableArray. You will often end up explicitly creating instance variables, accessors, and methods for adding or removing objects from the relationship.

 

object ownership: when an object has an object instance variable, the object with the pointer is said to own the object that is being pointed to. When an object has zero owners, it figures no one needs it around anymore and deallocate itself.

If a class overrides dealloc, then this method will be executed when an instance of the class is deallocated.

Command-B: build your program without running it.

Important things about collections and ownership:

When an object is added to the collection, the collection establishes a pointer to the object, and the object gains an owner.

When an object is removed from a collection, the collection gets rid of its pointer to the object,  and the object loses an owner.

When unnecessary objects do not get deallocated, you are said to have a memory leak.

 

Using @class instead of #import gives the compiler less information, but makes the processing of this particular file faster.

A class extension is a set of declarations that is private. Class extensions are added to the class implementation file above the @implementation block where methods are implemented. e.g.

@interface BNREmployee ()

{

NSMutableArray *_assets;

}

@property (nonatomic) unsigned int officeAlarmCode;

@end

Objects that are not instances of BNREmployee can no longer see this property. Moving the _assets instance variable to BNREmployee’s class extension.

A subclass has no access to its superclass’s class extensions.

When a class declares a property in its header, only the accessors for this property are visible to other objects. The instance variables generated by property declarations can not be accessed.

 

A weak reference is a pointer that does not imply ownership.

Preventing strong reference cycle: the parent owns the child, but the child should not own the parent.

A strong reference will keep the object it points to from being deallocated. A weak reference will not.

ARC uses the autorelease pool automatically, but you must create and drain the pool.

@autoreleasepool { … }

 

Retain count rules:

If you create an object using a method whose name starts with alloc or new or contains copy, then you have taken ownership of it.

An object created through any other means is not owned by you.

If you do not own an object and you want to ensure its continued existence, take ownership by sending it the message retain.

When you own an object and no longer need it, give up ownership by sending it the message release or autorelease.

As long as an object has at least one owner, it will continue to exist.

 

 

A set is a collection that has no sense of order, and a particular object can only appear in a set once. Sets are faster at testing object membership than arrays are.——NSSet, NSMutableSet. NSSet method: 

(BOOL) containsObject: (id) x;

 

NSMutableArray methods:

(NSUInteger) indexOfObject:(id) anObject;

(NSUInteger) indexOfObjectIdenticalTo:(id) anObject;

 

Identical objects are always equal. Equal objects are not always identical.

 

A dictionary is a collection of key-value pairs. The key is typically a string, and the value can be any sort of object.——NSDictionary, NSMutableDictionary.

- (void) setObject:(ObjectType)anObject  forKey:(id<NSCopying>)aKey

The keys in a dictionary are unique. If you try to add a second object under an existing key, the first key-value pair gets replaced.

NSDictionary *numberOfMoons = @{ @“Mercury” : @0, @“Venus” : @1, };

NSNumber *venusMoonCount = numberOfMoons[@‘Venus’];

 

@[] is an empty array, equivalent to [NSArray array];

 

Using an immutable collection conserves memory and improves performance because that collection never needs to be copied.

Immutable classes: NSArray, NSString, NSAttributedString, NSData, NSCharacterSet, NSDictionary, NSSet, NSIndexSet, NSURLRequest.

 

NSMutableArray method:

(void) sortUsingDescriptors:(NSArray<NSSortDescriptor *> *) sortDescriptors;

NSSortDescriptor *voa = [NSSortDescriptor sortDescriptorWithKey:@“valueOfAssets” ascending: YES];

NSSortDescriptor *eid = [NSSortDescriptor sortDescriptorWithKey:@“employeeID” ascending: YES];

[employee sortUsingDescriptors: @[voa, eid]];

- (void) filterUsingPredicate:(NSPredicate *) predicate;

 

NSArray method:

- (NSArray *) filteredArrayUsingPredicate:(NSPredicate *) predicate;

NSPredicate *predicate = [NSPredicate predicateWithFormat:@“holder.valueOfAssets > 70”];

NSArray *toBeReclaimed = [allAssets filteredArrayUsingPredicate: predicate];

 

NSSet method:

- (NSSet *) filteredSetUsingPredicate:(NSPredicate *) predicate;

NSMutableSet method:

- (void) filterUsingPredicate:(NSPredicate *) predicate;

 

NSValue is the superclass of NSNumber.

NSPoint somePoint = NSMakePoint(100, 100);

NSValue *pointValue = [NSValue valueWithPoint: somePoint];

instance of NSNull, representing nothingness.

NSMutableArray *hotel = [[NSMutableArray alloc] init];

[hotel addObject: [NSNull null]];

 

 

#import ensures that the preprocessor only includes a file once. #include will allow you to include the same file many times.

 

NSString * const NSLocaleCurrencyCode = @“currency”;

 

typedef enum {

BlenderSpeedStir = 1,

BlendSpeedChop = 2,

BlenderSpeedLiquify = 5,

} BlenderSpeed;

BlenderSpeed speed;

UTF-16: uses two or more bytes for every character.

UTF-8: uses one byte for the first 128 ASCII characters and two or more for other characters.

NSMutableString *str = [[NSMutableString alloc] init];

for (int i = 0; i < 10; i++) {

[str appendString:@"Aaron is cool!\n"];

        }

NSError *error;

        BOOl  success = [str writeToFile:@"/tmp/cool.txt"

              atomically:YES

                encoding:NSUTF8StringEncoding

                          error:&error];

Do not try to access the NSError unless the return value indicates that an error occurred; if the NSError object does not actually exist, trying to access it will crash your program.

 

        NSError *error;

        NSString *str = [[NSString alloc] initWithContentsOfFile:@"/etc/resolv.conf"

                                                        encoding:NSASCIIStringEncoding

                                                           error:&error];

        if (!str) {

            NSLog(@"read failed: %@", [error localizedDescription]);

        } else {

            NSLog(@"resolv.conf looks like this: \n%@", str);

        }

 

 

        NSLog(@"The file is %lu bytes", (unsigned long)[data length]);

        BOOL written = [data writeToFile:@"/tmp/google.png"

                                 options:NSDataWritingAtomic

                                   error:&error];

 

        NSData *readData = [NSData dataWithContentsOfFile:@"/tmp/google.png"];

        NSLog(@"has %lu bytes", (unsigned long)[readData length]);

 

        NSArray *desktops = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES); //get the path for user’s desktop directory

        NSString *desktopPath = desktops[0];

NSApplicationDirectory; NSLibraryDirectory; NSUserDirectory; NSDocumentDirectory; NSDesktopDirectory; NSCachesDirectory; NSApplicationSupportDirectory; NSDownloadsDirectory; NSMoviesDirectory; NSMusicDirectory; NSPicturesDirectory; NSTrashDirectory.

A callback lets you write a piece of code and then associate that code with a particular event.

Four forms of callback:

Target-action: the object receiving the message is the target, the selector for the message is the action. “When this event happens, send this message to this object.”

Helper objects: aften known as delegates or data sources. “Here is an object that will take on a role that helps another object do its job.”

Notifications.

Blocks: just a chunk of code to be executed.

 

[[NSRunLoop currentRunLoop] run]; // Waiting for sth to happen

Creating a timer with a time interval, a target, and an action. After the interval has elapsed, the timer sends the action message to its target.

Action methods always take one argument - the object that is sending the action message.

 

static NSDateFormatter *dateFormatter = nil;

 

BNRLogger *logger = [[BNRLogger alloc] init];

NSTimer *timer = [[NSTimer scheduledTimerWithInterval: 2.8

target: logger

selector: @selector(updateLastTime:)

userInfo.nil

repeats: YES]

[[NSRunloop currentRunLoop] run]

 

Problems with a synchronous connection:

It blocks the main thread while waiting for all the data to arrive.

It has no way to call back

 

In an asynchronous connection, the data comes in chunks rather than all at once. In the helper object, you implement the methods to be executed in response to different connection-related events.

 

A protocol is a list of method declarations. 

 

NSURL *url = [NSURL URLWithString:@“http://www.baidu.com/besy.png”;

NSURLRequest *request = [NSURLRequest requestWithURL:url];

NSURLConnection *fetchConn = [[NSURLConnection alloc] initWithRequest:request

delegate:logger

startImmediately:YES];

Declaring that BNRLogger will implement methods from the NSURLConnectionDelegate and NSURLConnectionDataDelegate protocol.

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSdata *)data;

- (void)connectionDidFinishLoading:(NSURLConnection *)connection;

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

In an asynchronous connection, you need an instance of NSMutableData. As the chunks of data arrive, you will add them to this object.

 

When sending one callback to one object, Apple uses target-action. When sending an assortment of callbacks to one object, Apple uses a helper object with a protocol. Objects that might need to trigger callbacks in several other objects use notification.

 

Each of objects can register as an observer with the notification center. When the time zone is changed, the notification NSSystemTimeZoneDidChangeNotification will be posted to the center, and the center will forward it to all the relevant observers.

[[NSNotificationCenter defaultCenter]

addObserver:logger

selector:@selector(zoneChange:)

name:NSSystemTimeZoneDidChangeNotification

object:nil];

 

Notification centers do not own their observers. If an object is an observer, it will typically remove itself from the notification center in its dealloc method.

[[NSNotificationCenter defaultCenter] removeObserver: self];

 

Objects do not own their delegates or data sources.

Objects do not own their targets.

 

To speed things up, the compiler assigns a unique number to each method name it encounters. At runtime, it uses that number instead of the method name.

A selector is the unique number that represents a particular method name. Using the @selector compiler directive to tell the compiler to look up the selector for the given method name.

 

^{

NSLog(@“This is an instruction whiten a block.”);

}

double (^divBlock)(double, double) = ^(double dividend, double divisor) {

double quotient = dividend / divisor;

return quotient;

}

double myQuotient = divBlock(42.0, 12.5);

 

void (^devowelizer)(id, NSUInteger, BOOL *);

 

        NSArray *originalStrings = @[@"Sauerkraut", @"Raygun", @"Big Nerd Ranch", @"Mississippi"];

        NSMutableArray *devowelizedStrings = [NSMutableArray array];

        NSArray *vowels = @[@"a", @"e", @"i", @"o", @"u"];

        

        void (^devowelizer)(id, NSUInteger, BOOL *);

        devowelizer = ^(id string, NSUInteger i, BOOL *stop) {

            NSRange yRange = [string rangeOfString:@"y" options:NSCaseInsensitiveSearch];

            if (yRange.location != NSNotFound) {

                *stop = YES;

                return;     // End this iteration

            }

            

            NSMutableString *newString = [NSMutableString stringWithString:string];

            for (NSString *s in vowels) {

                NSRange fullRange = NSMakeRange(0, [newString length]);

                [newString replaceOccurrencesOfString:s

                                           withString:@""

                                              options:NSCaseInsensitiveSearch

                                                range:fullRange];

            }

            [devowelizedStrings addObject:newString];

        };

        [originalStrings enumerateObjectsUsingBlock:devowelizer];

        NSLog(@"devewelized strings: %@", devowelizedStrings);

 

typedef void (^ArrayEnumerationBlock)(id, NSUInteger, BOOL *);

ArrayEnumerationBlock devowelizer;

 

An anonymous block is a block that you pass directly to a method without assigning it to a block variable first.

For primitive variables, the values are copied and stored as local variables within the block. For pointers, the block will keep a strong reference to the objects it references.

 

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf;

NSLog(@“Employee: %@”, innerSelf);

NSLog(@“Employee ID: %@”, innerSelf.employeeID);

};

 

By default, variables captured by a block are constant within the block, and you cannot change their values. 

        __block int counter = 0;

        void (^counterBlock)() = ^{ counter++; };

        counterBlock();

 

The class of an object is different from its role in a working system.

 

Frequently display data in an instance of UITableView, The UITableView object does not contain the data that it displays; it has to get data from a helper object. Creating the UITableView class specified the role of UITableView’s data source by creating the UITableViewDataSource protocol.

 

The UITableView class has dataSource property,

@property(nonatomic, assign) id<UITableViewDataSource> dataSource;

 

respondsToSelector: ——Implemented in NSObject. You pass in the selector for the method that you are asking about. The return will be YES if the object has that method.

 

A property list is a combination of any of the following things: NSArray, NSDictionary, NSString, NSData, NSDate, NSNumber( integer, float, or Boolean)。

 

        NSMutableArray *stocks = [[NSMutableArray alloc] init];

        NSMutableDictionary *stock;

        

        stock = [NSMutableDictionary dictionary];

        [stock setObject:@"AAPL" forKey:@"symbol"];

        [stock setObject:[NSNumber numberWithInt:200] forKey:@"shares"];

        [stocks addObject:stock];

        

        stock = [NSMutableDictionary dictionary];

        [stock setObject:@"GOOG" forKey:@"symbol"];

        [stock setObject:[NSNumber numberWithInt:160] forKey:@"shares"];

        [stocks addObject:stock];

        

        [stocks writeToFile:@"/tmp/stocks.plist" atomically:YES];

        

        NSArray *stockList = [NSArray arrayWithContentsOfFile:@"/tmp/stocks.plist"];

        for (NSDictionary *d in stockList) {

            NSLog(@"I have %@ shares of %@", [d objectForKey:@"shares"], [d objectForKey:@“symbol"]);

Output:

2015-10-05 14:40:44.893 OCDemo01[1462:114612] I have 200 shares of AAPL

2015-10-05 14:40:44.894 OCDemo01[1462:114612] I have 160 shares of GOOG

 

Cocoa is the collection of frameworks written by Apple that you use to write applications on the Mac. To write iOS apps, you use another set of frameworks called Cocoa Touch.

A GUI-based application is event-driven.

 

Models are responsible for storing data and making it available to other objects. Their sole purpose is holding and managing data. NSString, NSDate and NSArray are traditional model objects.

Views are the visual elements of an application. Views have no knowledge of the actual data that they display or how it is structured and stored. Like UIView, UITableView, UITextField and UIButton. If you can see it, is is a view.

Controllers perform the logic necessary to connect and drive the different parts of your application. Controllers are the real workhorses of any application.

 

#pragma mark - Application delegate callbacks group methods within a class.

When the application becomes ready, the UIApplication instance sends its delegate the message application:didFinishLaunchingWithOptions:. It is where you put everything that needs to done before the user interacts with the application.

 

When a Cocoa Touch application quits or is sent to the background, it sends its delegate the applicationDidEnterBackground: method from the UIApplicationDelegate protocol.

 

UIApplicationMain function creates the necessary objects for your application to run. It creates a single instance of the UIApplication class. Then, it creates an instance of the application’s delegate, so that it can send its delegate messages when memory gets low, when the application is quit or backgrounded, or when it finishes launching.

alloc creates the space for an object, and init makes the object ready to work.

The instancetype keyword tells the compiler to expect an instance of the class to which a method belongs. Any initializers you write or override should always return instancetype. Using instancetype ensures that initializers can be safely inherited.

 

  - (instancetype) initWithProductName:(NSString *)pn {

// self = [super init];

if  (self = [super init]) { 

_productName = [pn copy];

_voltage =120;

}

}

When you create a subclass, you typically only need to initialize the instance variables that the subclass introduced.

Add an implementation of the superclass’s initializer initWithProductName: that calls initWithProductName:firstOwnedName: and passes a default value for firstOwnedName. There is no need to implement init in subclass.

 

Stylish of writing initializer:

A class has only one designated initializer method. If the class has other initializers, then the implementation of those initializers must call (directly or indirectly) the designated initializer.

The designated initializer will call the superclass’s designated initializer before initializing its instance variables.

If the designated initializer of your class has a different name than the designated initializer of its superclass, you must override the superclass’s designated initializer so that it calls the new designated initializer.

If you have several initializers, clearly document which is the designated initializer in the header.

 

Overriding init to call the new class’s designated initializer with default values is not acceptable. The best thing to do is override the superclass’s designated initializer in a way that lets developers know that they have made a mistake and tells them how to fix it. e.g.

- (instancetype) init {

[NSException raise:@“BNRWallSafeInitialization”

format:@“Use initWithSecretCode:, not nil”];

}

 

assign is the default for non-object types and the simplest; it just assigns the passed-in value to the property.

strong will ensure that a strong reference is kept to passed-in object; it is the default for object pointers, and that is usually what you want.

weak does not imply ownership of the object pointed to, it keeps you safe from dangling pointers. If this object is deallocated, then the property will be set to nil.

unsafe_unretained properties do not imply ownership, it is not automatically set to nil when the object that it pointes to is deallocated.

 

copy option makes a copy of an object and then changes the pointer to refer to this copy. It is most common with object types that have mutable subclasses. e.g. @property (copy) NSString *lastName;

The generated setter method is:

- (void) setLastName:(NSString *)d {

_lastName = [d copy];

}

If you wish for your setter to set the property to be a mutable copy of an object, you must implement the setter yourself so that it calls the mutableCopy method on the incoming object.

The copyWithZone: and mutableCopyWithZone: methods are declared in the NSCopying and NSMutableCopying protocols, respectively. If you want your classes to be compatible with the copy property lifetime specifier, then you must ensure that they conform to the NSCopying protocol.

The nonatomic option will make your setter method run a tiny bit faster. You should always make your readwrite properties nonatomic too.

 

Two reasonable cases to implement an accessor yourself:

need to update the app’s user interface when the change occurs;

need to update some cached info when the change occurs.

If you declare a property and implement both accessors yourself, the compiler will not synthesize an instance variable. If you still want an instance variable, you must create it yourself by adding an @synthesize mushroom = _mushroom; statement to the class’s implementation.

The @synthesize statement tells the compiler that an instance variable named _mushroom is the backing variable for the mushroom and setMushroom: methods.

 

Key-Value coding:

[a setValue:@“Washing Machine” forKey:@“productName”];

[a setValue:[NSNumber numberWithInt:240] forKey:@“voltage”];

// go looking for a setter method named setProductName:. If the object does not have a setProductName: method, it will access the instance variable directly.

[a valueForKey:@“productName”];

// go looking for an accessor named productName.

Anytime a standard framework wants to push data into your objects, it will use setValue:forKey:. Anytime a standard framework wants to read data from your objects, it will use valueForKey:.

NSString *number = [sales valueForKey:@“manager.emergencyContact.phoneNumber”];

[sales setValue:@“555-666” forKey:@“manager.emergencyContact.phoneNumber”

 

Key-value observing (KVO): lets you get a notification when a particular property of an object changes.

BNRObserver *observer = [[BNRObserver alloc] init];

[logger addObserver:observer

forKeyPath:@“lastTime”

options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld

context:&contextForKVO];

 

- (void) observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context {

if (context != &contextForKVO) {

… } else {

NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey];

NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];

}

}

 

- (void) updateLastTime:(NSTimer *)t {

NSDate *date = [NSdate date];

[self willChangeValueForKey:@“lastTime”];

_lastTime = now;

[self didChangeValueForKey:@“lastTime”];

}

 

+ (NSSet *)keyPathForValuesAffectingLastTimeString {

return [NSSet setWithObject:@“lastTime”];

}

 

Categories let a programmer add methods to any existing class.

@interface NSString (BNRVowelCounting)

- (int)bnr_vowelCount;

@end

 

@implementation NSString (BNRVowelCounting)

// implementing

@end

 

posted @ 2015-10-05 22:42  pestle  阅读(261)  评论(0编辑  收藏  举报