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
Objective-C
From the Smalltalk languageSmalltalk
Language evolved,Smalltalk
Is the originator of message language.Objective-C
isThe C language
Superset of, inThe C language
Object oriented and so on, and the syntax might seem a little strange to you at first, becauseObjective-C
Using dynamic bindingMessage structure
And theJava
.C++
And so forth use function calls.Message structure
withA function call
The key difference is the language in which the function is called at compile timeThe compiler
Generate someVirtual method table
Find the method to execute from the table at run time. While using dynamic bindingMessage structure
When 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
- 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#import
If you import all of them, it will appeara.h
Import theb.h
whenc.h
And the importa.h
When theb.h
They’re all imported, and if you just use the class name, it’s really wasteful and not elegant - Sometimes it doesn’t work
@class
Forward 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:
- 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 modified
readonly
. atomic
There 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 asnonatomic
Because atomic has a significant impact on performance, but development on Mac OSX usually doesn’t have this performance problem- 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
- 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.
- 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.
- 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.
- 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 is
objc_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:
- When you send a message to an object, the Runtime processes it by looking in the list of methods of the object’s class
- 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:
- each
Class
There is aIsa pointer
Point to a uniqueMeta Class
- each
Meta Class
theIsa pointer
They all point to the topMeta Class
theMeta Class
isThe Meta Class NSObject
. (includingThe Meta Class NSObject
theIsa pointer
Also pointingThe Meta Class NSObject
That’s self, it’s a closed loop. - each
Meta Class
thesuper class
The pointer points to itOriginally the Class
theMeta Class of Super Class
It’s on the topThe Meta Class NSObject
thesuper class
The pointer still points to itself) - The top of the
NSObject Class's super Class
Point 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
- Protocols can provide anonymous objects to some extent, for example
id<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. - 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
- 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 |
- 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:
- 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
- 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:
- Memory management issues: Use under ARC
performSelector
We often see the compiler issue warnings like this:warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]
performSelector
The return value of can only be void or object type.performSelector
Cannot 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:
- Cancelling an operation is supported: it can be called on the NSOperation object before running the task
Cancel method
To 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”. - 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 dependencies
Download manifest file operation
The simultaneous execution starts after the execution is complete. - 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.
- 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.
- 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
- The dispatch_get_current_queue function often behaves differently than expected by developers and is deprecated and should only be used for debugging.
- Because THE GCD is organized hierarchically, there is no single queue object to describe the concept of “current queue”.
- 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:
- 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.
- CoreAudio: This framework provides a C API that can be used to manipulate audio hardware on a device.
- 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.
- CoreData: This framework provides an Objective-C interface to put objects into a database and persist data.
- CoreText: This framework provides a C interface to efficiently perform text typesetting and rendering operations.
- SpriteKit: Game framework
- CoreLocation, MapKit: positioning map related framework
- Address Book framework: Use this framework only when you need to use the Address Book
- Music Libraries: A Music library-related framework
- HealthKit Framework: Health-related framework
- HomeKit framework: A framework for intelligent hardware
- CloudKit: iCloud related framework
- 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
- 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.
- Block enumerations are performed concurrently by GCD
- 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
- 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
- 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