After reviewing the OC classic Effective Objective-C 2.0:52 Effective Ways to Write High Quality iOS and OS X code, this article will help you finish the book quickly. Due to your limited ability, there will be some omissions or errors. Please see each officer not stingy give advice! Thank you very much! At the same time, if you have any questions, you can also leave a comment below, welcome to exchange progress! In addition, due to space reasons, the introduction of some basic knowledge in the book is omitted.

Click here to download


Chapter 1: Familiarize yourself with Objective-C

# 1 Understand the origins of Objective-C

  1. Objective-CFrom the Smalltalk languageSmalltalkLanguage evolved,SmalltalkIs the originator of message language.
  2. Objective-CisThe C languageSuperset of, inThe C languageObject oriented and so on, and the syntax might seem a little strange to you at first, becauseObjective-CUsing dynamic bindingMessage structureAnd theJava.C++And so forth use function calls.
  3. Message structurewithA function callThe key difference is the language in which the function is called at compile timeThe compilerGenerate someVirtual method tableFind the method to execute from the table at run time. While using dynamic bindingMessage structureWhen you receive a message at runtime, the runtime decides what code to execute next, not the compiler.

Rule 2: Minimize references to other header files in class files

  1. If you need to reference a class file, just need to use the class name, do not need to know the details, can be used@class xx.h, this has the advantage of reducing some compile time. If it’s used#importIf you import all of them, it will appeara.hImport theb.hwhenc.hAnd the importa.hWhen theb.hThey’re all imported, and if you just use the class name, it’s really wasteful and not elegant
  2. Sometimes it doesn’t work@classForward declarations, such as that a class follows a protocol declared in another class, can be placed in a separate header file or in a classification to reduce the cost of reference.

Rule 3: Use literal syntax more than its equivalent

1. Use literal syntax to create strings, arrays, dictionaries, etc. The traditional way to create an array:

NSArray *languages = [NSArray arrayWithObjects:@"PHP"The @"Objective-C", someObject, @"Swift"The @"Python", nil];
NSString *Swift = [languages objectAtIndex:2];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"key"The @"value", nil];
NSString *value = [languages objectForKey:@"key"];
Copy the code

Literal:

NSArray *languages = @[@"PHP"The @"Objective-C", someObject, @"Swift"The @"Python"];
NSString *Swift = languages[2];
NSDictionary *dict = @{@"key" : @"value"};
NSString *value = languages[@"key"];

Copy the code

Benefits: It makes code cleaner, easier to read, and avoids nil problems. For example, if someObject in a languages data is nil, the literal syntax will throw an exception, but the traditional way to create a languages array is @[@”PHP”, @” Objective-c “]. Because literal syntax is a syntactic sugar, the effect is to create an array and then add all the objects in parentheses to the array. One of the minor drawbacks of literal syntax is that you can create arrays, strings, and so on that are immutable, so if you want to create mutable objects you have to do mutableCopy, for example

NSMutableArray *languages = [@[@"PHP"The @"Objective-C"The @"Swift"The @"Python"] mutableCopy];
Copy the code

Rule 4: Use type constants more often than #define preprocessors

Article 4, article 5 look at this

Rule 5: Use enumerations to represent states, options, and status codes

Article 4, article 5 look at this


Chapter 2: Objects, messages, runtime

Number six: Understand the concept of “attributes.

This article describes the basic concept of attributes, as well as the various attributes of the modifier, these are not verbose, but here to emphasize:

  1. When defining attributes that are open to the outside world, try to minimize exposure permissions and include attributes that you do not want to be modifiedreadonly.
  2. atomicThere is no guarantee that multithreading is safe. For example, one thread will read the value of a property several times in a row, while other threads will still read different values when changing the value of the property. The principle of atomic is simply to add one to setter and getter methods@synchronized(self), so all properties in iOS development should be declared asnonatomicBecause atomic has a significant impact on performance, but development on Mac OSX usually doesn’t have this performance problem
  3. Say which of the following property declarations is problematic
@property (nonatomic, strong) NSArray *arrayOfStrong;
@property (nonatomic, copy) NSArray *arrayOfCopy;
@property (nonatomic, strong) NSMutableArray *mutableArrayOfStrong;
@property (nonatomic, copy) NSMutableArray *mutableArrayOfCopy;
Copy the code

Click to view the answer is normal should be declared like this

@property (nonatomic, copy) NSArray *arrayOfCopy;
@property (nonatomic, strong) NSMutableArray *mutableArrayOfStrong;
Copy the code

Rule 7: Try to access instance variables directly from within an object

  1. When reading property data from within a class, it should be read through a direct instance variable instead of being distributed by objecit-c’s method. It is faster for the compiler to compile code that accesses the value of the instance variable’s block of memory rather than generating code distributed by the method.
  2. When you write data to a property, you should write it as a property, which calls setter methods. But in some cases the initializer and dealloc methods should always read and write data directly from instance variables to avoid exceptions caused by subclasses overwriting setter methods.
  3. Lazy-loaded properties are used and data should always be read and written as properties.

8. Understand the concept of “object equality”

What is the output?

    NSString *aString = @"iphone 8";
    NSString *bString = [NSString stringWithFormat:@"iphone %i"8]; NSLog(@"%d", [aString isEqual:bString]);
    NSLog(@"%d", [aString isEqualToString:bString]);
    NSLog(@"%d", aString == bString);
Copy the code

The answer is that the 110 == operator only compares two Pointers, not the object to which the pointer points

Rule 9: Hide implementation details in a “family pattern.

Why is the following if always false

    id maybeAnArray = @[];
    if ([maybeAnArray class] == [NSArray class]) {
         //Code will never be executed
    }
Copy the code

Because the return of [maybeAnArray Class] will never be an NSArray, NSArray is a family of classes, and the value returned will always be an entity subclass of NSArray. Most collection classes are ‘abstract base classes’ in a family of classes, so the above if should be changed to have any chance of being executed

id maybeAnArray = @[];
    if ([maybeAnArray isKindOfClass [NSArray class]) {
         //Code probably be executed
    }
Copy the code

This means that the maybeAnArray object is a member of the NSArray family of classes. Class families have the advantage of hiding implementation details behind a simple set of public interfaces

Rule 10: Use associated objects to store custom data in existing classes

This is objc_setAssociatedObject and objc_getAssociatedObject, but I won’t talk about how to use them here. It is important to note that the use of associated objects may introduce hard-to-find bugs, since this is the Runtime phase, so you may have to choose carefully

Article 11: Understand the purpose of objc_msgSend

We talked about objective-C earlier when we looked at the origins of objective-C, which is a message structure. This is just a message for you to understand how to deliver.

  1. In Objective-C, if a message is passed to an object, the dynamic binding mechanism is used at run time to determine which methods need to be invoked. But to the bottom concrete implementation, it is the ordinary C language function. The function of this implementation isobjc_msgSend, the function is defined as follows:
void objc_msgSend(id self, SEL cmd, ...) 
Copy the code

This is a variable number of arguments. The first argument represents the receiver, the second argument represents the selector (OC function name), and the subsequent arguments are those in the message (OC function call) 2. For example:

id return = [git commit:parameter];
Copy the code

The objective-C methods above are converted to the following function at run time:

id return = objc_msgSend(git, @selector(commit), parameter);
Copy the code

The objc_msgSend function searches the list of methods in the recipient’s class, and if it finds a method with the same name as the selector’s child, it jumps to its implementation code and executes. If the current class is not found, it continues to look up along the inheritance system, and then jumps to the right method. If it still cannot be found, it enters the message forwarding process to deal with it. 3. If you look at the OC function call implementation, you’ll notice that message forwarding is a lot of work, especially for search. Fortunately, objc_msgSend caches the search area. This way, some of the frequently called methods of this class will appear in the Fast Map, instead of having to search through the list of methods again and again. 4. There is an interesting point, is in the underlying process to send messages, call useful to end optimization, principle is probably at the end of the function calls a does not contain the return value function, the compiler will automatically do not to memory that is allocated on the stack space, but directly to release all call a function within the local variables, and then directly to the called function address.

12. Understand message forwarding mechanisms

IOS understands Message forwarding in Objective-C with Demo

Article 13: Use “Method Deployment Technology” to debug “Black Box Method”

Method Swizzling (Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling, Method Swizzling Specific examples can be viewed by clicking on me

Understand the meaning of class objects

The Objective-C Class is represented by the Class type, which is actually a pointer to the objc_class structure. It is defined as follows:

typedef struct objc_class *Class;
Copy the code

You can see his implementation in <objc/ Runtime.h > :

struct objc_class { Class isa OBJC_ISA_AVAILABILITY; ///< point to metaClass#if ! __OBJC2__Class super_class OBJC2_UNAVAILABLE; ///< parent class const char *name OBJC2_UNAVAILABLE; //< class name long version OBJC2_UNAVAILABLE; ///< Class version information, default is 0 long info OBJC2_UNAVAILABLE; ///< class information, some bits for run-time use to identify long instance_size OBJC2_UNAVAILABLE; Struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; Struct objc_method_list **methodLists OBJC2_UNAVAILABLE; Struct objc_cache *cache OBJC2_UNAVAILABLE; Struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; ///< protocol linked list#endif

} OBJC2_UNAVAILABLE;
Copy the code

This structure stores metadata about a class, such as how many methods an instance of a class implements and how many instance variables it has. The ISA pointer here points to another class called metaClass. So what is a metaclass? A metaclass is a class of a class object. Or you can put it in a more understandable way:

  1. When you send a message to an object, the Runtime processes it by looking in the list of methods of the object’s class
  2. When you send a message to a class, the Runtime processes it by looking in the list of methods in the metaclass of that class

Let’s look at a classic picture to understand:

  1. eachClassThere is aIsa pointerPoint to a uniqueMeta Class
  2. eachMeta ClasstheIsa pointerThey all point to the topMeta ClasstheMeta ClassisThe Meta Class NSObject. (includingThe Meta Class NSObjecttheIsa pointerAlso pointingThe Meta Class NSObjectThat’s self, it’s a closed loop.
  3. eachMeta Classthesuper classThe pointer points to itOriginally the ClasstheMeta Class of Super ClassIt’s on the topThe Meta Class NSObjectthesuper classThe pointer still points to itself)
  4. The top of theNSObject Class's super ClassPoint to nil

Chapter three: Interface and API design

Use prefixes to avoid namespace conflicts

Objective-c doesn’t have namespaces like those in other languages, such as PHP

<? php namespace Root\Sub\subnamespace;Copy the code

This can cause you to accidentally implement two classes with the same name, or import two relatively independent libraries into a project where they happen to have the same Class and the corresponding symbol and Meta Class symbol are defined twice. So it’s very easy to have this kind of naming conflict, which causes the program to appear in the link process to have the duplicate symbol to cause the error. To avoid this, try to add prefixes to class names, classes, classification methods, macro definitions, etc., depending on your project

Article 16: Provide “universal initialization Methods”

If there is more than one way to create instances of the class, the class will have multiple initialization method, do it very well, but still want a method in which selected as universal initialization method, the rest of the rest of the initialization method is to call it, the advantage is later if the initialization logic change just change a can, The NSDate class defines a universal initialization method in nsdate.h:

- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
Copy the code

The rest of the similar initialization methods are defined in the NSDate (NSDateCreation) classification

- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
Copy the code

There is one in the NSDate document: If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, You must do these things

Override [initWithTimeIntervalSinceReferenceDate:
](apple-reference-documentation://hcslylvSCo), one of the designated initializer methods`
Copy the code

This is something we should learn as we organize code!

Article 17: Implement description method

You can override the description method or the debugDescription method to output more custom information during NSLog printing or LLDB printing. Note: Do not use NSLog(“%@”,self) in description; (Data and dictionaries can be overridden descriptionWithLocale: method.) I have an interesting idea here, but I haven’t fully implemented it yet, which is that by overwriting description, I can completely record the name of the property value of any object, the property value, and you can click to see it

Article 18: Use immutable objects whenever possible

The default value is readWrite. In this way, the external data can only be read, but can not be modified. This makes the data held by the instance of this class more secure. If the outside world wants to make changes, it can provide methods to do so. Do not expose mutable collections as attributes. Instead, provide methods to modify mutable collections in objects (this is only necessary for common and important classes, because it adds a lot of code). For example:

//Language.h
@property (nonatomic, strong) NSSet *set;
Copy the code

Should be changed to

//Language.h
@property (nonatomic, strong, readonly) NSSet *languages;
- (void)addLanguage:(NSString *)language;
- (void)removeLanguage:(NSString *)language;
//**.m
@implementation Language {
    NSMutableSet *mutableLanguages;
}
- (NSSet *)languages {
    return [_mutableLanguages copy];
}
- (void)addLanguage:(NSString *)language {
    [_mutableLanguages addObject:language];
}
- (void)removeLanguage:(NSString *)language {
     [_mutableLanguages removeObject:language];
}
Copy the code

Use clear and coordinated naming

I don’t want to emphasize this too much, but I can also refer to some of the Objective-C programming specifications and recommendations THAT I’ve drawn up before, which may be updated in the future

Rule 20: Prefix private method names

Private methods in a class should be prefixed in order to distinguish them from each other. This feeling varies from person to person, as long as you don’t expose private methods in a.h file, it is acceptable.

# 21: Understand the Objective-C error Model

Many languages have exception handling mechanisms, and Objective-C is no exception. Objective-c also has a similar @throw, but using @throw in OC can cause a memory leak, possibly due to the context in which it was designed to be used. It is recommended that @throw be used only for handling fatal errors. NSError can be used for handling nonfatal errors.

Article 22: Understand the NSCopying agreement

In OC development, it is often necessary to copy an object when using it, which we will do by copy/mutbleCopy. If you want your classes to support copying, you must implement the NSCopying protocol. You only need to implement one method:

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

Of course, if you want to return an object of mutable type, you need to use the NSMutableCopying protocol, corresponding method

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

When copying objects, you need to pay attention to whether the copy is a shallow copy or a deep copy. Deep copy When copying an object, the underlying data of the object is also copied. Shallow copy is the creation of a new object pointing to the content to be copied. In general, shallow copies should be performed as much as possible.


Chapter four: Agreement and classification

Article 23: Object to object communication through delegate and data source protocol

This article is also about the basic, is the basic delegate, protocal use. Just a little bit: Delegate mode can be used when one object needs to fetch data from another object, This usage is often referred to as “DataSource Protocol” similar to UITableview UITableview datasource and another important idea in Swift is protocol oriented programming. Of course, protocols can also be used in OC to reduce code coupling, and can replace inheritance if necessary, because classes that follow the same protocol can be any class, not the same inheritance system.

Article 24: Divide the implementation code of a class into manageable categories

Classes can be broken up into manageable chunks using a taxonomy mechanism. There are also some preconditions, maybe this class business is complex, need to thin, need to decouple, etc. The authors also recommend unifying Private methods in Private categories to hide implementation details. This guy thinks it depends.

Rule 25: Always prefix the class names of third-party classes

Add your own special prefix to the class name of the third party class, needless to say 😜

Do not declare attributes in a classification

Do not declare attributes in a class, except for a class-continuation. What is a class-continuation class? This is what we often use in.m files, for example:

// swift.m@interface Swift () // This is a continuation @end@implementation Swift @endCopy the code

Use a “class-continuation” classification to hide implementation details

This is a bit of a repeat of the previous one, but the ultimate goal is to minimize the exposure of the public interface, hide the implementation details, and just tell how to call, how to use. The modifiable permissions of the implementation and properties are hidden as much as possible.

Article 28: Provide anonymous objects by agreement

  1. Protocols can provide anonymous objects to some extent, for exampleid<someProtocal> object. The type of an object is unlimited, as long as it complies with the protocol that defines the methods that the object should implement.
  2. If the specific type is not important but the ability of the object to handle certain methods is important, you can use this protocol to anonymize the object.

Chapter 5: Memory Management

Article 29: Understand reference counting

  1. To understand reference counting, you can use an example from the Book Advanced Programming in Objective-C, which looks like this:

Work done on lighting equipment Actions taken on OC objects
Turn on the light To generate the object
Need lighting hold
No lighting required The release of
Turn off the lights abandoned
Way to think about memory management Corresponding OC method
Self generated objects, self held Alloc/new/copy/mutableCopy, etc
Objects that are not self-generated (such as [NSArray Array]) can also be held by themselves retain
Release when you no longer need to hold objects yourself release
Deprecated when an object is not held by any other object dealloc
  1. Autoreleasepool: You can see that there is a layer of Autoreleasepool wrapped in the main function of the entry file main.m in our program
int main(int argc, char * argv[]) {
    @autoreleasepool {
        returnUIApplicationMain(argc, argv, nil, NSStringFromClass([HSAppDelegate class])); }}Copy the code

Autoreleasepool can extend the life of an object for a while after it crosses the method call boundary, usually on the next “event loop,” though it may be executed earlier. 3. Retention loop: Also called retain cycle, circular reference. The reason is that objects use strong references to each other, which makes it impossible to release all of them. The usual solution is to use weak references.

Rule 30: Simplify reference counting with ARC

With ARC, reference counting can be omitted, so an error is reported when ARC calls the retain, release, autorelease, dealloc methods of an object. Note that CoreFoundation objects are not managed by ARC and should be created and released by the developer. Call CFRetain/CFRelease if necessary.

Article 31: In the delloc method only the reference is released and the listener is unlistened

Do not call other methods in a delloc method, especially if you need to perform some tasks asynchronously and then need to call back. This is dangerous behavior. It is possible that the object will be destroyed by the time the callback is completed asynchronously. In the delloc method, there should be some release related things, including but not limited to some KVO unsubscribe, remove notification, etc.

Rule 32: Be aware of memory management problems when writing exception safe code

This is a bit repetitive, as we’ve already said, but OC can cause a memory leak when it throws an exception. Be careful when you use it, or be careful to clean it up when you catch an exception at @try.

Rule 33: Avoid retaining rings with weak references

This one is relatively simple, and the main idea is the title: Retain Cycle with weak References

Rule 34: Use @autoreleasepool to reduce memory peak value

Automatic release pooling can be used to reduce memory spikes when traversing large arrays or dictionaries, for example:

NSArray *people = /* A large array */ NSMutableArray *employeesArray = [NSMutableArray new];for (NSStirng *name inpeople) { @autoreleasepool { MLEmployee *employee = [MLEmployee alloc] initWithName:name]; [employeesArray addObject:employee]; }}Copy the code

Article 35: Use zombie objects to debug memory management problems

Rule 36: Do not use retainCount

RetainCount has been deprecated since ARC was introduced by Apple, so never call this retainCount method to check the reference count, because this value is actually inaccurate. But it can still be used normally under MRC


Chapter 6: Block and GCD

Article 37: Understand the Block

Blocks are divided into three types based on their location in memory:

  1. NSGlobalBlock global block: This block runs without obtaining any external state. The memory region used by the block is fully determined by the compiler, so the block is declared in global memory. If the global block does copy, it does nothing. Global blocks for example:
void (^block)() = ^{
    NSLog(@"I am a NSGlobalBlock");
}
Copy the code
  1. NSStackBlock: The stack block is stored in the stack area, outside the scope of variables, the stack block and __block variables are destroyed. Such as:
NSString *name = @"PHP";
void (^block)() = ^{
    NSLog(@"The best programming language in the world is %@", name);
};
NSLog(@"% @", block);
Copy the code

Run it and you’ll see that the console prints:

<__NSStackBlock__: 0x7fff5480fa18>
Copy the code

What, what are you saying, you printed out __ NSMallocBlock __? -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) -fno-objc-arc (-fno-objc-arc) NSMallocBlock: I have been exposed, why am I introduced last!! Heap block memory is stored in the heap and is unaffected at the end of the variable scope. We have seen __ NSMallocBlock __ from the previous output under ARC. So we often use the copy modifier when defining block attributes. This modifier is actually unnecessary. The system already does a copy for us in ARC, but it is recommended to write copy.

Article 38: Create a typedef for commonly used block types

This is important because the code is more readable.

- (void)getDataWithHost:(NSString *)host success:(void (^)(id responseDic))success; // Typedef void (^SuccessBlock)(id responseDic); - (void)getDataWithHost:(NSString *)host success:(SuccessBlock)success;Copy the code

Article 39: Use handler blocks to reduce code fragmentation

In iOS development, it is often necessary to perform tasks asynchronously and then wait for the task to complete and notify the methods. There are many ways to implement this requirement, for example some people may choose to use a delegate protocol. When you do some tasks asynchronously and then call the proxy after the execution, the code might be scattered. When multiple tasks need to be asynchronous, and so on, it makes less sense. Consider using blocks so that business-specific code is more compact and less cluttered.

Rule 40: Block references to the object they belong to do not have retention rings

This is basic, but it should be mentioned briefly that weakSelf is not necessarily used in a block, such as the following:

[YTKNetwork requestBlock:^(id responsObject) {
      NSLog(@"% @",self.name);
  }];
Copy the code

A block is not held by self, so self can be used within a block

41: use more queues and less synchronous locks

In iOS development, if you have multiple threads executing the same code, you may need to lock it to implement some kind of synchronization mechanism. One might first think of @synchronized(self), for example:

- (NSString*)someString {
    @synchronized(self) {
        return _someString;
    }
}
- (void)setSomeString:(NSString*)someString { @synchronized(self) { _someString = someString; }}Copy the code

Writing this way is inefficient and does not guarantee that the thread feels safe. If there are many attributes, the synchronized block for each attribute will wait for the other synchronized blocks to finish executing. GCD should be used instead:

_syncQueue = dispatch_queue_create("syncQueue", DISPATCH_QUEUE_CONCURRENT); // Read string - (NSString*)someString {__block NSString*localSomeString;
     dispatch_sync(_syncQueue, ^{
        localSomeString = _someString;
    });
     return localSomeString;
}
- (void)setSomeString:(NSString*)someString {
     dispatch_barrier_async(_syncQueue, ^{
        _someString = someString;
    });
}
Copy the code

Rule 42: use GCD more than performSelector

Objective-c is essentially a factory-dynamic language, in which developers can specify any method to call, defer invoking some methods, or specify the thread on which the method is to run. Normally we think of performSelector, but after GCD comes out you don’t really need performSelector that much, and performSelector has a lot of disadvantages:

  1. Memory management issues: Use under ARCperformSelectorWe often see the compiler issue warnings like this:warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]
  2. performSelectorThe return value of can only be void or object type.
  3. performSelectorCannot handle selectors with more than one argument, can only handle two arguments at most. To change this, we can do the following
dispatch_async(dispatch_get_main_queue(), ^{
        [self doSomething];
});
Copy the code

replace

[self performSelectorOnMainThread:@selector(doSomething) 
                       withObject:nil 
                    waitUntilDone:NO];
Copy the code

And then you can use it

Dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^(void){ [selfdoSomething];
});
Copy the code

replace

[self performSelector:@selector(doSomething) withObject: nil afterDelay: 5.0];Copy the code

Rule 43: Know when to use GCD and operation queue

GCD technology is great, but there are some limitations, or scenarios that just don’t work. For example, you want to cancel an operation in a queue, or you need to perform a task in the background. There’s another technology called NSOperationQueue, and NSOperationQueue actually has a lot in common with GCDS. NSOperationQueue predates the GCD, which was built on some of its principles. GCD is a C-level API, while NSOperation is a heavyweight Objective-C object. Advantages of using NSOperation and NSOperationQueue:

  1. Cancelling an operation is supported: it can be called on the NSOperation object before running the taskCancel methodTo indicate that this task does not need to be performed. However, tasks that have been started cannot be cancelled. GCD queues cannot be cancelled, GCD is “Fire and forget”.
  2. Support for specifying dependencies between operations: an operation can depend on multiple other operations, such as downloading and processing files from the server can be represented as operations, and “manifest files” must be downloaded before processing other files. Subsequent downloads will depend on the list files downloaded first. If the operation queue allows concurrent execution, subsequent downloads can be performed on other dependenciesDownload manifest file operationThe simultaneous execution starts after the execution is complete.
  3. You can use KVO to monitor the properties of NSOperation objects: Check whether the task has been cancelled using the isCancelled property and check whether the task has completed using the isFinished property.
  4. You can specify the priority of an operation: The priority of an operation indicates the priority relationship between this operation and other operations in the queue. Operations with a higher priority are executed first, and those with a lower priority are executed later. GCD queues also have priority, but not for the entire queue.
  5. Reuse NSOperation objects. You can subclass NSOperation during development or create your own NSOperation object to hold some information. You can define methods in the class so that your code can use them more than once. Don’t repeat yourself.

Article 44: Perform tasks according to system resource status through the Dispatch Group mechanism

This article introduces the functions of the Dispatch Group. He can group tasks into groups, and then wait for a notification when the set of tasks is complete, so the developer can get the results and move on. In addition, when multiple tasks are executed simultaneously on the concurrent queue through the Dispatch Group, GCD will help to schedule these concurrent tasks according to the system resource status.

Rule 45: Use dispatch_once to execute thread-safe code that only needs to be run once

This is dispatch_once

+ (id)sharedInstance { static EOCClass *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{  sharedInstance = [[self alloc] init]; });return sharedInstance;
}
Copy the code

Dispatch_once is efficient and has no heavyweight synchronization mechanism.

Do not use dispatch_get_CURRENT_queue

  1. The dispatch_get_current_queue function often behaves differently than expected by developers and is deprecated and should only be used for debugging.
  2. Because THE GCD is organized hierarchically, there is no single queue object to describe the concept of “current queue”.
  3. The dispatch_get_current_queue function is used to resolve deadlocks caused by code that cannot be reentrant and can then be used to resolve the problem, usually with queue-specific data.

Chapter 7: System framework

Rule 47: Familiarize yourself with the system framework

In objective-c there are many system libraries in addition to Foundation and CoreFoundation, including but not limited to the following:

  1. CFNetwork: This framework provides C-level network communication capabilities by abstracting BSD sockets into easy-to-use network interfaces. Foundation encapsulates parts of the framework as objective-C interfaces for network communication.
  2. CoreAudio: This framework provides a C API that can be used to manipulate audio hardware on a device.
  3. AVFoundation: Objective-C objects provided by this framework can be used to revisit and record audio and video, such as the ability to play video in a UI view class.
  4. CoreData: This framework provides an Objective-C interface to put objects into a database and persist data.
  5. CoreText: This framework provides a C interface to efficiently perform text typesetting and rendering operations.
  6. SpriteKit: Game framework
  7. CoreLocation, MapKit: positioning map related framework
  8. Address Book framework: Use this framework only when you need to use the Address Book
  9. Music Libraries: A Music library-related framework
  10. HealthKit Framework: Health-related framework
  11. HomeKit framework: A framework for intelligent hardware
  12. CloudKit: iCloud related framework
  13. Passbook and PassKit framework: In order to make it easy for users to access the framework provided by the event tickets, travel tickets, coupons and so on they purchased before

Rule 48: Use block enumeration more, use for loop less

  1. There are four ways to traverse elements in a collection. The most basic method is the for loop, followed by NSEnumerator traversal, for in, and block enumeration. Block enumeration is the latest, most advanced way.
  2. Block enumerations are performed concurrently by GCD
  3. If you know in advance what objects the collection to be traversed contains, you should modify the block signature to indicate the specific type of object.

Article 49: Use seamless bridging for collecion that customizes its memory management semantics

Seamless bridging allows you to convert back and forth between classes defined in the Foundation framework and C data structures in the CoreFoundation framework. The following code shows a simple seamless bridge:

NSArray *anNSArray = @[@1, @2, @3, @4, @5];
CFArrayRef aCFArray = (__bridge CFArrayRef)anNSArray;
NSLog(@"Size of array = %li", CFArrayGetCount(aCFArray));
//Output: Size of array = 5
Copy the code

The __bridge in the conversion operation tells ARC how to transfer the OC object involved in the conversion, meaning ARC still has ownership of the OC object. __bridge_retained the opposite. Note that when you run out of arrays, you need to release them yourself, using CFRelease(aCFArray) as mentioned earlier.

Rule 50: Use NSCache instead of NSDictionary when building a cache

When building a cache, use NSCache instead of NSDictionary. NSCache automatically cuts the cache when the system is running out of resources, whereas using NSDictionary can only be handled manually through the system low memory warning method. In addition, NSCache removes the most unused objects as appropriate and is thread-safe.

Simplify the initialize and Load implementation code

  1. Both the Load and Initialize methods should be implemented in a streamlined manner to help keep the application responsive and reduce the chance of dependency loops being introduced
  2. Global constants that cannot be set by the compiler can be initialized in the Initialize method. In addition, if you do not understand load and initialize, you can see here. I have a problem that is a bit stupid and a bit round before (don’t shoot bricks, 😆), you can click here to check

Rule 52: Don’t forget that NSTimer keeps its target objects

Timers are often used in iOS development :NSTimer, because NSTimer generates references to its user, and if the user also references NSTimer, it forms a damn circular reference, as in this example:

#import <Foundation/Foundation.h>

@interface EOCClass : NSObject
- (void)startPolling;
- (void)stopPolling;
@end
@implementation EOCClass {
     NSTimer *_pollTimer;
}
- (id)init {
     return[super init]; } - (void)dealloc { [_pollTimer invalidate]; } - (void)stopPolling { [_pollTimer invalidate]; _pollTimer = nil; } - (void) startPolling {_pollTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: the self selector:@selector(p_doPoll) userInfo:nil repeats:YES]; } - (void)p_doPoll { // Poll the resource } @endCopy the code

If you create an instance of this class and call its startPolling method to start a timer, since the target object is self, you will keep the instance. Since the timer is held as a member variable, self also holds the timer, so there is a hold ring. To break the retention ring, either call stopPolling or have the system reclaim the instance. This is a very common memory leak, so how to fix it? This problem can be solved with blocks. You can add a category like this:

#import <Foundation/Foundation.h>
//.h
@interface NSTimer (EOCBlocksSupport)

+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                         repeats:(BOOL)repeats;
@end
//.m
@implementation NSTimer (EOCBlocksSupport)

+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                        repeats:(BOOL)repeats
{
             return [self scheduledTimerWithTimeInterval:interval
                                                  target:self
                                                selector:@selector(eoc_blockInvoke:)
                                                userInfo:[block copy]
                                                 repeats:repeats];

}
+ (void)eoc_blockInvoke:(NSTimer*)timer {
     void (^block)() = timer.userInfo;
         if (block) {
             block();
        }
}
@end
Copy the code

EOF: Due to my limited ability, there are some omissions or mistakes, please feel free to comment! Thank you very much! If you have any questions, please feel free to leave a comment below. Thank you to Matt Galloway and the translator! For more details, please refer to the book, you can download the PDF here, I also have the original English PDF ~ this article has been synchronized to my blog