This book teaches many of the specifications you should follow when writing Objective-C. It happened that the author had a little gap because the product was just developed some time ago, so he spent three weeks reading and summarizing this book carefully.

I also read many blogs and articles summarizing this book, but I found that most of them only extracted the summary part of each section, so they were not very detailed. Therefore, I want to summarize this book in my own way and present it in the form of blog: it can be shared, and at the same time, it can comb and review the knowledge.

Although the author divides the book into seven chapters and 52 sections according to the knowledge modules, the author found that the knowledge points introduced in the book can be roughly divided into three categories: concept, standard and skills. The author intends to summarize this book according to the three categories and form a trilogy:

  • Concept class: explains some conceptual knowledge.
  • Specification class: explains some of the specifications that need to be followed in order to avoid problems or facilitate future development.
  • Skills: explains the skills needed to solve a particular problem.

Moreover, the author has drawn the structure of the book with mind mapping tool according to his own classification:

As you can see from the figure, the author did not scramble the original author’s title order. This conclusion is part of a trilogy: concepts, followed by specifications and techniques. Note: all the code and images in this summary are from the original book. Among them, the code will be appropriate with the author’s comments, easy to understand.

All right, without further ado, let’s get started!

# 1: Understand the origins of Objective-C

Runtime component

For message structuring languages, the code executed by the runtime is determined by the runtime environment; Only at run time do you go back and look for methods that require execution. It is implemented by the Runtime Component, which contains all the data structures and functions required to use objective-C’s object-oriented features.

A runtime component is essentially a dynamic library that links to the code that the developer has written. The code glue together all the programs that the developer has written, so simply updating a runtime component can improve application performance.

Memory: Objects are allocated to heap space and Pointers to stack space. Memory allocated in the queue must be managed directly, while memory allocated on the stack to hold variables is automatically cleared when its stack frame pops up.

Variables that do not contain * may use stack space. Structs hold non-object types.

# 6: Understand the concept of “attributes.

Properties are used to encapsulate data in an object.

1. Access method

After setting the properties, the compiler automatically writes out a set of access methods to access variables with the corresponding names:

@interface EOCPerson : NSObject

@property NSString *firstName;
@property NSString *lastName;
@end


@interface EOCPerson : NSObject

- (NSString*)firstName;
- (void)setFirstName:(NSString*)firstName;
- (NSString*)lastName;
- (void)setLastName:(NSString*)lastName;

@end
Copy the code

To access properties, you can use dot syntax. The compiler converts the dot syntax into a call to an access method:

aPerson.firstName = @"Bob"; // Same as:
[aPerson setFirstName:@"Bob"];


NSString *lastName = aPerson.lastName; // Same as:
NSString *lastName = [aPerson lastName];
Copy the code

If we don’t want the compiler to automatically generate access methods, we need to set the @dynamic field:

@interface EOCPerson : NSManagedObject

@property NSString *firstName;
@property NSString *lastName;

@end


@implementation EOCPerson
@dynamic firstName, lastName;
@end
Copy the code

2. Attribute keywords

When attributes are defined, they are often given features to meet the requirements that a class must follow to store data.

Atomicity:

  • Nonatomic: No synchronization lock is used
  • Atomic: Add synchronization lock to ensure atomicity

Read and write

  • Readwrite: Concurrent access methods
  • Readonly: only access methods

Memory management

  • Assign: A simple assignment operation for the scalar type
  • Strong: The owner retains the new value, releases the old value, and sets the new value
  • Weak: Non-owning relationship. When the owning object is destroyed, the property is emptied
  • Unsafe_unretained: Similar to assign, this attribute applies to the object type and is not owned. If the object specified by the attribute is destroyed, the attribute cannot be deleted.
  • Copy: Copies the new value instead of keeping it

Note: Follow the property definition

If the property is defined as copy, the semantics of copy should also be followed when setting the property in non-set methods

- (id)initWithFirstName:(NSString*)firstName lastName:(NSString*)lastName
{
         if ((self = [super init])) {
            _firstName = [firstName copy];
            _lastName = [lastName copy];
        }
       return self;
}

Copy the code

Rule 8: Understand the concept of “object equality”

1. Equality judgment

The == operator compares pointer values, which are memory addresses.

However, sometimes we just want to compare what the pointer points to. In this case, we use the isEqual: method to do the comparison.

Also, if two objects are known to be strings, it is best to compare them using the isEqualToString: method. For arrays and dictionaries, there are also isEqualToArray: methods and isEqualToDictionary: methods.

Alternatively, if the object type being compared is the same as the current object type, you can use your own determination method; otherwise, the parent class’s isEqual: method is called:

- (BOOL)isEqualToPerson:(EOCPerson*)otherPerson {// compares object types first and then each propertyif (self == object) return YES;
     if(! [_firstName isEqualToString:otherPerson.firstName])return NO;
     if(! [_lastName isEqualToString:otherPerson.lastName])return NO;
     if(_age ! = otherPerson.age)return NO;
     returnYES; } - (BOOL)isEqual:(id)object {// If the objects belong to the same type, call our own check method; if not, call isEqual: method of the parent classif ([self class] == [object class]) {    
         return [self isEqualToPerson:(EOCPerson*)object];
    } else {    
         return[super isEqual:object]; }}Copy the code

2. Depth equivalence determination

To compare whether two arrays are equal, we can use the depth equality judgment method:

1. Compare the number of arrays. 2.

Rule 11: Understand the role of objc_msgSend

In OC, if information is passed to an object, a dynamic binding mechanism is used to determine which methods need to be invoked. At the bottom, all methods are plain C language functions.

However, which method to call after an object receives a message is entirely determined at runtime, and can even be changed while the program is running. These features make OC a truly dynamic language.

In OC, the syntax for sending a message to an object is:

id returnValue = [someObject messageName:parameter];
Copy the code

Here, someObject is called “receiver “, messageName: is called” selector “, selector and parameter together called “message”. The compiler sees this message and converts it into a standard C function call. The function called is the core of the messaging mechanism called objc_msgSend, which has the following prototype:

void objc_msgSend(id self, SEL cmd, ...)
Copy the code

The first argument represents the receiver, the second argument represents the selector, and the subsequent arguments are those in the message, which are variable in number, so this function is a variable number of arguments.

Therefore, the above function presented in OC form will be converted to the following function:

id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);
Copy the code

The function searches the recipient’s “list of methods” in the class to which it belongs, implements the code if it can find a method that matches the name of the selector child, and continues up the inheritance hierarchy if it can’t. If it is found, it performs the message forwarding operation if it is not eventually found.

Note: If the match is successful, the result of the match is cached in the “quick map table”. Each class has such a cache. So if a similar message is sent to the class again in the future, execution will be faster.

Rule 12: Understand the message forwarding mechanism

If neither the object’s class nor any of its parents can interpret the received message, message Forwarding is initiated.

In particular, when we write our own classes, hooks can be set up during message forwarding to perform predetermined logic rather than crash the application.

Message forwarding is divided into two stages:

  1. The recipient is asked if it can dynamically add methods to handle the unknown selectors, a process called Dynamic Method Resolution.

  2. Ask the receiver to see if any other objects can handle this message:

    2.1 If so, the runtime system will forward the message to that object. 2.2 If not, the full forwarding mechanism will be enabled and all the details of the message will be encapsulated in the NSInvocation object and the receiver will be given one last chance to resolve the message that is not yet processed.

Class method +(BOOL)resolveInstanceMethod (SEL) Selector: Checks to see if the class can add an instance method to handle this selector

Instance methods – forwardTargetForSelector: (SEL) the selector (id); : asks if the standby receiver of the unknown message can be found, returns the standby object if it can be found, and returns nil if it cannot.

Example method – (void)forwardInvocation:(NSInvocation*) Invocation: Creates an NSInvocation object that encapsulates all the details of the as-yet-unhandled message. When the NSInvocation object is fired, The message-Dispatch system delivers the message to the target object.

Let’s look at an example of dynamic method resolution:

#import <Foundation/Foundation.h>

@interface EOCAutoDictionary : NSObject
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;

@end



#import "EOCAutoDictionary.h"
#import <objc/runtime.h>


@interface EOCAutoDictionary ()
@property (nonatomic, strong) NSMutableDictionary *backingStore;
@end



@implementation EOCAutoDictionary

@dynamic string, number, date, opaqueObject;



- (id)init {
 if ((self = [super init])) {
    _backingStore = [NSMutableDictionary new];
}

   return self;

}



+ (BOOL)resolveInstanceMethod:(SEL)selector {

     NSString *selectorString = NSStringFromSelector(selector);
     if ([selectorString hasPrefix:@"set"]) {
         class_addMethod(self,selector,(IMP)autoDictionarySetter, "v@:@");
     } else {
         class_addMethod(self,selector,(IMP)autoDictionaryGetter, "@ @.");
    }
     return YES;
}

Copy the code

In this case, the EOCAutoDictionary class sets the property to @dynamic, which means that the compiler cannot automatically generate set and GET methods for its properties, so we need to add set and GET methods to it dynamically.

ResolveInstanceMethod: converts the selector to a String, and then determines whether the String contains a set field. If so, add a set method to handle the selector. If not, add get methods that handle selectors. Class_addMethod can dynamically add methods to a class.

Implement the get method to add processing selectors:

id autoDictionaryGetter(id self, SEL _cmd) {

     // Get the backing store from the object
     EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;
     NSMutableDictionary *backingStore = typedSelf.backingStore;

     // The key is simply the selector name
     NSString *key = NSStringFromSelector(_cmd);

     // Return the value
     return [backingStore objectForKey:key];
}

Copy the code

In this case, the name of the key equals the name of the method, so the method name is converted to a string before fetching the value of the key.

Implement the set method to increase the processing selectors:


void autoDictionarySetter(id self, SEL _cmd, id value) {

     // Get the backing store from the object
     EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;
     NSMutableDictionary *backingStore = typedSelf.backingStore;

     /** The selector will be for example, "setOpaqueObject:".
     * We need to remove the "set".":" and lowercase the first
     * letter of the remainder.
     */
     NSString *selectorString = NSStringFromSelector(_cmd);
     NSMutableString *key = [selectorString mutableCopy];

     // Remove the ':' at the end
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];

     // Remove the 'set' prefix
    [key deleteCharactersInRange:NSMakeRange(0, 3)];

     // Lowercase the first character
     NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];

     if (value) {
       [backingStore setObject:value forKey:key];
    } else{ [backingStore removeObjectForKey:key]; }}Copy the code

Because the name of the key corresponds to the property name, that is, the string without set, starts with lowercase letters, and ends with no:. However, after converting the set method to a string, we need to dispose of all the edges and corners of the set method. Finally, after obtaining the “clean” key, the dictionary assignment is performed.

Rule 14: Understand the meaning of class objects

The runtime library header defines the data structure used to describe the OC object:

typedef struct objc_class *Class;

    struct objc_class {
         Class isa;
         Class super_class;
         const char *name;
         long version;
         long info;
         long instance_size;
         struct objc_ivar_list *ivars;
         struct objc_method_list **methodLists;
         struct objc_cache *cache;
         struct objc_protocol_list *protocols;
};



Copy the code

In this case, the ISA pointer points to the class to which the object belongs: the metaclass, which is the first variable in the entire structure. Super_class defines the superclass of this class.

We can also send objects specific methods to see the class’s inheritance: which class it belongs to; Self inheritance and which kind.

We use isMemberOfClass to determine whether an object is an instance of a particular class; The isKindOfClass: method determines whether an object is an instance of a class or a derived class.

Both methods use the ISA pointer to get the class of the object and then query it in the inheritance system through the super_class class. In OC, you must use this method of querying type information to fully understand the true type of an object. Because object types cannot be determined at compile time.

In particular, when fetching objects from a collection class, it is usually a query for type information because these objects are not strongly typed. The type that will fetch them from a collection class is usually id, which can respond to any message (compile time).

So if we don’t get the type of these objects right, we might end up with objects that can’t respond to messages. Therefore, after we pull an object from a collection, we usually make a type judgment:


- (NSString*)commaSeparatedStringFromObjects:(NSArray*)array {

         NSMutableString *string = [NSMutableString new];

             for (id object in array) {
                    if ([object isKindOfClass:[NSString class]]) {
                            [string appendFormat:@"% @.", object];
                    } else if ([object isKindOfClass:[NSNumber class]]) {
                            [string appendFormat:@"%d,", [object intValue]];
                    } else if ([object isKindOfClass:[NSData class]]) {
                           NSString *base64Encoded = /* base64 encoded data */;
                            [string appendFormat:@"% @.", base64Encoded];
                    } else {
                            // Type not supported
                    }
              }
             return string;
}
Copy the code

Rule 21: Understand Objective-C error types

In OC, we can use NSError to describe errors. NSError encapsulates three types of information:

  • Error domain: Error domain. The type is a string
  • Error Code: Indicates the Error code. The type is an integer
  • User info: indicates the User information. The type is dictionary

1. Use of NSError

Usage:

1. Pass NSError through the delegate protocol to tell the proxy the type of error.

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
Copy the code

2. Returned to the caller as an “output parameter” to the method

- (BOOL)doSomething:(NSError**)error
Copy the code

Use examples:


NSError *error = nil;
BOOL ret = [object doSomething:&error];

if (error) {
    // There was an error
}

Copy the code

2. Customize NSError

We can set error ranges and error codes for our own programs

  • The error range can be defined as a global constant string.
  • Error codes can be defined using enumerations.
// EOCErrors.h extern NSString *const EOCErrorDomain; / / define the error code typedef NS_ENUM (NSUInteger EOCError) {EOCErrorUnknown = 1, EOCErrorInternalInconsistency = 100, EOCErrorGeneralFault = 105, EOCErrorBadInput = 500, }; // EOCErrors.m NSString *const EOCErrorDomain = @"EOCErrorDomain"; // Define the error scopeCopy the code

Article 22: Understand the NSCopying agreement

If we want our class to support copy operations, we implement the NSCopying protocol, which has only one method:

- (id)copyWithZone:(NSZone*)zone
Copy the code

The author gives an example:


- (id)copyWithZone:(NSZone*)zone {

     EOCPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName  andLastName:_lastName];
    copy->_friends = [_friends mutableCopy];
     return copy;
}

Copy the code

It is copy->_friends, not copy. Friends because friends is not a property, but an instance variable for internal use.

1. Copy mutable versions:

To comply with the agreement

And to implement:

- (id) mutableCopyWithZone (NSZone *) zone;Copy the code

Note: Copying mutable and immutable types sends copy and mutableCopy messages, whereas we implement the – (id)copyWithZone:(NSZone*)zone and – (id)mutableCopyWithZone:(NSZone*)zone methods.

Also, if we want to get an immutable type of an object, we call copy. Get the mutable type of an object and call mutableCopy.

For example, an array copy:

-[NSMutableArray copy] => NSArray
-[NSArray mutableCopy] => NSMutableArray
Copy the code

2. Shallow copy and deep copy

Collection classes in the Foundation framework all perform shallow copying by default: copying only the container objects themselves, not the data in them. Deep copy means that both the object itself and its underlying data are copied.

The difference between a shallow copy and a deep copy is illustrated by a diagram:

The shallow copy and the original point to the same object. The deep copy refers to a copy of the original object

3. How to deep copy?

We need to write our own deep-copy method: iterate over each element, copy it, and then rebuild the copied elements into a new set.

- (id)initWithSet:(NSArray*)array copyItems:(BOOL)copyItems;

Copy the code

Here, we provide a deep-copy method of our own: this method takes two arguments: the array to copy and whether to copy elements (deep-copy or not)


- (id)deepCopy {
       EOCPerson *copy = [[[self class] alloc] initWithFirstName:_firstName andLastName:_lastName];
        copy->_friends = [[NSMutableSet alloc] initWithSet:_friends copyItems:YES];
        return copy;
}

Copy the code

Rule 29: Understand reference counting

Although automatic reference counting is already supported on iOS, it is still necessary for developers to understand its memory management mechanism.

1. Operation of counter:

  1. Retain: Increments the retention count.
  2. Release: decrements the retention count
  3. Autorelease: decrement the retention count when autorelease pool is cleared later.

Note: After the object is initialized, the reference count may not be 1 and may be greater than 1. Because in the implementation of the initialization method, there may be other operations that make the reference count +1, such as other objects that also retain this object.

Sometimes we cannot determine the exact value of the reference count after an operation, but only whether the operation increments or decrements the retention count.

2. Automatic release pool:

Instead of having an object’s reference count -1 immediately after it is placed into the auto-release pool, it is decremented at the next event loop of the current thread.

Example: If we want to release the method we currently want to use, we can temporarily put it in the automatic release pool:


- (NSString*)stringValue {
     NSString *str = [[NSString alloc] initWithFormat:@"I am this: %@", self];
     return [str autorelease];
}

Copy the code

3. Retain Cycle

Objects with strong references to each other make it impossible to free anything. The solution is to change one end of the reference to weak reference without incrementing the reference count.

Rule 30: Simplify reference counting with ARC


Using ARC, you can omit reference counting and let developers focus on development:

if ([self shouldLogMessage]) {
     NSString *message = [[NSString alloc] initWithFormat:@"I am object, %p", self];
     NSLog(@"message = %@", message);
      [message release]; ///< Added by ARC
}
Copy the code

Obviously we don’t need the Message object here, so ARC will automatically add a memory management statement for us.

Therefore, it is illegal to call memory management statements in ARC environments:

  • retain
  • release
  • autorelease
  • dealloc

Note: ARC only manages the memory of OC objects; CoreFoundation objects are not managed by ARC

Rule 37: Understand the concept of a block

Without going into the basics of “blocks”, let’s emphasize the types of blocks.

Blocks fall into three categories:

  • Stack blocks
  • lumps
  • Global block

1. The stack block

When a block is defined, the memory area it occupies is allocated on the stack and is only valid for the range in which it is defined:

void (^block)();

if ( /* some condition */ ) {
    block = ^{
     NSLog(@"Block A");
    };

} else {
    block = ^{
     NSLog(@"Block B");
    };
}

block();
Copy the code

The two blocks defined above are valid only within the scope of the if else statement. Once the last closing bracket is left, the program will crash if the compiler overwrites the memory allocated to the block.

2. Pile block

To solve this problem, we can send a copy message to the object and make a copy to the heap with a reference count:

void (^block)();

if ( /* some condition */ ) {
    block = [^{
         NSLog(@"Block A");
   } copy];
} else {
    block = [^{
         NSLog(@"Block B");
    } copy];
}

block();
Copy the code

3. The global block

Global block declarations are in global memory and do not need to be created on the stack each time they are used.

void (^block)() = ^{
     NSLog(@"This is a block"); };Copy the code

Rule 47: Familiarize yourself with the system framework

If we use the ready-made framework provided by the system, then users can directly enjoy the improvements brought by the system upgrade after the system upgrade.

Main system framework:

  • Foundation: NSObject, NSArray, NSDictionary, etc
  • CFoundation framework: C language API, many functions of Foundation framework, can be found here the CORRESPONDING C language API
  • CFNetwork framework :C language API, provides C language level network communication capabilities
  • CoreAudio:C API for manipulating audio hardware on devices
  • AVFoundation framework: Provides OC objects that can playback and record audio and video
  • CoreData framework: API for OC to write objects to a database
  • CoreText framework: C API for efficient text typesetting and rendering operations

Benefits of implementing the API in C: You can bypass the OC runtime system and thus speed up execution.

The last word

As mentioned at the beginning of this paper, this paper is the first part of the trilogy: concept part. The author mainly extracted and combined the knowledge points explained in this book to form the concept part. The content is simpler than the last two parts. I will launch the second (standard), the third (skills) in a week ~ hope all the gods and partners in the gods on the road more communication.

This post has been synced to my blog: Portal

The other two portals:

Effective Objective-C — Effective Objective-C — Effective Objective-C — Effective Objective-C

Effective Objective-C Effective Objective-C Effective Objective-C

— — — — — — — — — — — — — — — — — — — — — — — — — — — — on July 17, 2018 update — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Pay attention!!

The author recently opened a personal public account, mainly to share programming, reading notes, thinking articles.

  • Programming articles: including selected technical articles published by the author before, and subsequent technical articles (mainly original), and gradually away from iOS content, will shift the focus to improve the direction of programming ability.
  • Reading notes: Share reading notes on programming, thinking, psychology, and career books.
  • Thinking article: to share the author’s thinking on technology and life.

Because the number of messages released by the official account has a limit, so far not all the selected articles in the past have been published on the official account, and will be released gradually.

And because of the various restrictions of the major blog platform, the back will also be released on the public number of some short and concise, to see the big dry goods article oh ~

Scan the qr code of the official account below and click follow, looking forward to growing with you