52 effective ways to write high-quality iOS and OS X code

Source: Internet
Author: User
Tags gcd

This is my review of "effective OBJECTIVE-C 2.0" Summary and summary, if in doubt, I will attach an example explanation.

First, familiar with Objective-c
    1. Understand the origins of the Objective-c language
    • Objective-c adds an object-oriented feature to the C language and is a superset of it. Objective-c uses a dynamically bound message structure, which means that the object type is checked at run time. What code should be executed after receiving a message is determined by the runtime environment, not the compiler.
    • Understanding the core concepts of C language helps to write good objective-c programs. In particular, you should master the memory model and pointers.
    1. Minimize the introduction of additional header files in the header file of the class
    • Do not introduce a header file unless it is necessary. In general, you should use forward declarations (forward declaring) in the header file of a class to refer to other classes and introduce the header files of those classes in the implementation file. Doing so minimizes the coupling between classes.
    • It is sometimes impossible to use forward declarations, such as declaring a class to follow a protocol. In this case, try to move the "this class follows an agreement" declaration to the "class-continuation" category. If not, put the protocol in a single header file and introduce it.
    1. Multi-use literal syntax, less use of the equivalent method
      Like more use, NSArray *array = @[@1,@2]; less use.NSArray *array = [NSArray arrayWithObjects:@1,@2,nil];
    • You should use the literal syntax to create strings, values, arrays, and dictionaries. This is more concise than the usual method of creating such objects.
    • The element that corresponds to the key in the array subscript or dictionary should be accessed by removing the label operation.
    • When you create an array or dictionary with the literal syntax, an exception is thrown if there is nil in the value. Therefore, it is important to ensure that the value does not contain nil.
    1. Multi-use type constants, less # define preprocessing directives
    • Do not define constants with preprocessing directives. The defined constants do not contain type information, and the compiler simply performs a find-and-replace operation on this basis before compiling. Even if a constant value is redefined, the compiler does not produce a warning message, which results in inconsistent constant values in the application.
    • Use the static const in the implementation file to define constants that are visible only within the compilation unit. Because such constants are not in the global symbol table, you do not need to prefix their names.
    • Use extern in the header file to declare global constants and define their values in the relevant implementation file. This constant is to appear in the global symbol table, so its name is separated, usually prefixed with the class name associated with it.
    1. Use enumerations to represent States, options, status codes
    • An enumeration should be used to indicate the state of the state machine, the options passed to the method, and the equivalent of the status code, giving the values an understandable name.
    • If the option passed to a method is represented as an enumerated type, and multiple options can be used simultaneously, then each option value is defined as a power of 2 to be combined by a bitwise OR operation.
    • Use Ns_enum and Ns_options macros to define the enumeration type and indicate its underlying data type. Doing so ensures that the enumeration is implemented using the underlying data type chosen by the developer, rather than using the type chosen by the compiler.
    • Do not implement the default branch in a switch statement that handles enumeration types. In this case, after adding the new enumeration, the compiler will prompt the developer: The switch statement does not handle all enumerations.
Ii. objects, messages, runtime
    1. Understand the concept of "attributes"
    • You can define the data that is encapsulated in an object by using the @property syntax.
    • Specifying the correct semantics for storing data through "traits"
    • When setting an instance variable corresponding to a property, be sure to follow the semantics declared by that property.
    • When developing an iOS program, you should use the Nonatomic property because the atomic property can severely affect performance.
    1. Accessing instance variables directly inside the object as much as possible
    • When reading data inside an object, it should be read directly through the instance variable, while writing the data should be written by property.
    • In the initialization method and the Dealloc method, the data should always be read and written directly through the instance variable.
    • Sometimes a piece of data is configured using lazy initialization techniques, in which case the data needs to be read through the property.
    1. Understanding the concept of "object equivalence"
    • If you want to detect the equivalence of objects, provide "isequal:" with the hash method.
    • The same object must have the same hash code, but the same object with two hash codes may not be the same.
    • Do not blindly monitor each attribute individually, but should be based on the specific needs of the development of testing programs.
    • When you write a hash method, you should use an algorithm that is fast and has a low probability of collision with the hash code.
    1. Hide implementation details in "Class family mode"
    • The class family pattern hides the implementation details behind a set of simple public interfaces.
    • Class families are often used in system frameworks.
    • Be careful when inheriting subclasses from the common abstract base class of a class family, if you have a development document, you should read it first.
    1. Use associative objects (associated object) to store custom data in existing classes
    • You can connect two objects by using the association object mechanism.
    • When defining an association object, you can specify memory management semantics to mimic the "owning relationship" and "non-owning relationship" used when defining attributes.
    • Associate objects should be selected only when other practices are not available, because this practice often introduces bugs that are difficult to find.
    1. Understanding the role of objc_msgsend
    • The message consists of the recipient, selector, and parameters. Sending a message to an object is also the equivalent of calling a method on that object.
    • All messages sent to an object are handled by the dynamic message dispatch system, which detects the corresponding method and executes its code.
    1. Understanding Message Forwarding Mechanisms
    • If the object cannot respond to a selector, it enters the message forwarding process.
    • With the dynamic method parsing function at run time, we can add it to the class when we need to use a method.
    • objects can transfer certain selector that they cannot interpret to other object processing.
    • After the above two steps, if still can't handle selector, then start the complete message forwarding mechanism.
    1. Using method swizzling to debug black box methods
    • In runtime, you can add or replace a method implementation of selector to a class.
    • Using another implementation to replace the original method implementation, this process is called method swizzling, developers often use this technique to add functionality to the original line of sight.
    • In general, only when debugging the program needs to modify the method implementation in runtime, this practice should not be abused.
    1. Understanding the purpose of "class object"
    • Each instance has a pointer to a class object to indicate its type, and these class objects form a tired inheritance system.
    • If the object type cannot be determined at compile time, then the type information query method should be used to detect it.
    • Try to use Type information query methods to determine object types, rather than directly comparing class objects, because some objects may implement message forwarding.
Third, interface and API design
    1. Avoid namespace collisions with prefixes
    • Choose the name that is associated with your company, application, or both as a prefix to the class name, and use that prefix in all your code.
    • If a third-party library is used in the library you are developing, you should prefix the name.
    • Apple claims to retain the right to use all two-letter prefixes, so its choice of prefixes is preferably three letters.
    1. Provides an "All-in-all initialization method"
    • Provides an all-in-one initialization method in the class and is indicated in the document. This method should be called by other initialization methods.
    • If the almighty initialization method differs from the superclass, it is necessary to overwrite the corresponding method in the Super class.
    • If the initialization method of the superclass does not apply to subclasses, then the superclass method should be covered and an exception thrown.
    1. Implementing the Description method
    • Implementing the Description method returns a meaningful string that describes the instance.
    • If you want to print more detailed object description information during debugging, you should implement the Debugdescription method.
    1. Use immutable objects as much as possible
    • Try to create immutable objects.
    • If an attribute can only be modified inside an object, it is expanded from the ReadOnly property to the ReadWrite property in the Class-continuation category.
    • Instead of exposing mutable collection as attributes, you should provide a method to modify the variable collection in the object at once.
    1. Use a clear and coordinated naming method
    • The name should follow the standard OBJECTIVE-C naming specification, so that the interface created is easier for developers to understand.
    • The method name should be concise, read from left to right to be like a sentence in everyday language is good.
    • Method fame do not use the abbreviated type name.
    • The first thing to do when you name a name is to make sure its style matches your own code or the framework that you want to integrate.
    1. Prefix a private method name
    • Prefix the names of private methods so that they can be easily separated from public methods.
    • Do not prefix a private method with an underscore, because this practice is reserved for Apple.
    1. Understanding the OBJECTIVE-C Error model
    • An exception is used only if a critical error occurs that can cause the entire application to crash.
    • In the case of a less serious error, you can assign a delegate method to handle the error, or you can put the error message in the Nserror object and return it to the caller via an output parameter.
    1. Understanding the Nscopying Protocol
    • The Nscopying protocol is required if you want the object you write to have a copy function.
    • If a custom object is divided into a mutable version and an immutable version, then both the nscopying and nsmutablecopying protocols are implemented.
    • When copying objects, you should decide whether to use a shallow copy or a deep copy, in general you should do a shallow copy as much as possible.
    • If you are writing objects that require a deep copy, consider adding a new method that specifically performs deep copies.
Iv. protocols and classifications
    1. Inter-object communication through delegation and data source protocol
    • The delegate mode provides a set of interfaces for an object so that it can communicate related events to other objects.
    • Define the interfaces that the delegate object should support as protocols, and define the events that might require strenuous activity in the protocol as a method.
    • Delegate mode is used when an object needs to fetch data from another object. In this case, the pattern is also known as the data source protocol.
    • If necessary, a struct containing a bit segment can be implemented to cache the information that the delegate object responds to the relevant protocol method.
    1. Spread the implementation code of a class into manageable categories
    • Use the classification mechanism to divide the implementation code of the class into manageable chunks.
    • The methods that should be considered private are grouped into categories called Private to hide the implementation details.
    1. Always prefix the category name of a third-party class
    • When you add a taxonomy to a third-party class, you should always prefix the name with your dedicated one.
    • When you add a taxonomy to a third-party class, you should always include the method name with your dedicated prefix.
      26. Do not declare attributes in the classification
    • All the attributes used in the encapsulated data are defined in the main interface.
    • In other categories other than the Class-continuation classification, you can define access methods, but try not to define attributes.
    1. Using Class-continuation to classify hidden implementation details
    • Add an instance variable to the class by Class-continuation classification.
    • If a property is declared read-only in the main interface, and the inside of the class modifies this property with the Setup method, it is extended to read-write in the Class-continuation class.
    • The prototype of the private method is declared inside the class-continuation classification.
    • If you want to make the protocol that the class follows is not known, it can be declared in the class-continuation classification.
    1. Providing anonymous objects through protocols
    • The protocol can provide an anonymous type to some extent. The specific object type can be diluted to conform to some of the ID types, and the protocol specifies the method that the object should implement.
    • Use anonymous objects to hide the type name or class name.
    • If the specific type is not important, it is important that the object be able to respond (defined in the Protocol) to a particular method, then the anonymous object can be used to represent it.
Five, memory management
    1. Understanding reference Counts
    • The reference counting mechanism manages memory through counters that can increment by decreasing. When an object is created, its retention count is at least 1. If the hold count is positive, the object continues to survive. When the hold count is reduced to 0 o'clock, the object is destroyed.
    • During the object lifetime, the rest of the objects are referenced to preserve or dispose of this object. The hold and release operations increment and decrement the hold count, respectively.
    1. Simplify reference counting with arc
    • After arc, there is no need for programmers to worry about memory management issues. Using ARC to program can eliminate many boilerplate code in the class.
    • The arc manages the life of the object by essentially inserting the retention and release operations where appropriate. In the ARC environment, the memory management semantics of variables are always represented by the method name. ARC identifies this as a rule that developers must follow.
    • ARC is only responsible for managing the memory of the Objective-c object. In particular, note that Corefoundation objects are not managed by arc, and developers must call cfretain/cfrelease as appropriate.
    1. Only release the reference and dismiss the listener in the Dealloc method
    • In the Dealloc method, the thing to do is to release a reference to other objects, and cancel the original subscription of the key value of observation or nsnotificationcenter, etc., do not do anything else.
    • If the object holds system resources such as file descriptors, a method should be written specifically to release the resource. Such a class would have to agree with its consumer that the Close method must be called after the resource is exhausted.
    • Methods that perform asynchronous tasks should not be called in Dealloc, and those methods that can only be executed in a normal state should not be called in Dealloc because the object is already in the state of being reclaimed.
    1. Beware of memory management issues when writing exception-Safe code
    • When catching exceptions, be sure to clean up the objects created within the try block.
    • By default, ARC does not generate the cleanup code that is required to handle the exception safely. When the compiler flag is turned on, this code can be generated, but it can cause the application to become larger and reduce operational efficiency.
    1. Avoid duplicate references with weak references
    • You can avoid duplicate references by setting some references to weak.
    • The weak reference can be automatically emptied or not automatically emptied. Auto-emptying is a new feature introduced with ARC and is implemented by runtime, where it is possible to read data on weak references with automatic emptying, as this reference does not point to objects that have already been recycled.
    1. Reduce memory spikes with auto-release pool blocks
    • The auto-free pool is arranged on the stack, and after the object receives the Autorelease message, the system puts it into the topmost pool.
    • A reasonable use of the automatic release pool can reduce the memory spikes of the application.
    • @autoreleasepool This new style of writing can create a more portable automatic release pool.
    1. Debugging memory management issues with zombie objects
    • When the system recycles the object, it can not really recycle it, but instead converts it into a zombie object. This feature can be turned on with the environment variable nszombieenabled.
    • The object's ISA pointer is modified to point to a special zombie class, making the object a zombie object. The Zombie class responds to all selector by printing a message containing the message content and its recipients, and then terminating the application.
    1. Do not use Retaincount
    • The retention count of an object may seem useful, but it is not, because the absolute hold count at any given point in time does not reflect the full picture of the object's lifetime.
    • After the introduction of Arc, the Retaincount method is formally abolished, and in the arc downward using this method will cause the compiler to error.
Vi. Block and GCD
    1. Understanding the concept of block
    • Block is a lexical closure in C, C + +, and objective-c.
    • The block can receive parameters or return values.
    • Blocks can be allocated on stacks or heaps, or they can be global. Blocks allocated on the stack can be copied to the heap, which, like the standard Objective-c object, has a reference count.
    1. Create a typedef for a commonly used block type
    • Redefining the block type with typedef makes the block variable easier to use.
    • You should follow the existing naming conventions when defining new types, and do not conflict their names with other types.
    • You might want to define more than one type alias for the same block signature. If the code you are refactoring uses an alias of block type, you can simply modify the block signature in the corresponding typedef without altering the other typedef.
    1. Reduce code dispersion with handler blocks
    • When creating an object, the associated business logic is declared with the inline handler block.
    • When there are multiple instances that need to be monitored, if the delegate mode is used, it is often necessary to switch from the incoming object, and if you use a handler block instead, you can put the block directly together with the related object.
    • If you use the handler block when designing the API, you can add a parameter that allows the caller to decide which queue the block should be scheduled to execute on.
    1. Do not have circular references when referencing objects that they belong to by using block
    • If the object that the block captures directly or indirectly retains the block itself, then you have to be careful about the problem of circular referencing.
    • Be sure to find an appropriate time to dismiss the circular reference, rather than pushing the responsibility to the caller of the API.
    1. Multi-use dispatch queue, less use of synchronous lock
    • The dispatch queue can be used to express synchronization semantics, which is easier than using @synchronized blocks or Nslock objects.
    • Combining synchronization with asynchronous dispatch allows for the same synchronous behavior as the normal locking mechanism, which does not block the thread that executes the asynchronous dispatch.
    • With synchronization queues and fence blocks, you can get a more efficient synchronization behavior.
    1. Multi-use GCD, less use of Performselector series method
    • The Performselector series approach is prone to lapses in memory management. It cannot determine what the selector is going to do, so the arc compiler cannot insert an appropriate memory management method.
    • The Performselector series method can handle selector that are too limited, selector the return value type and the number of parameters sent to the method are limited.
    • If you want to put the character on another thread, then it is better not to use the Performselector series method, but rather to encapsulate the task into a block and then call the GCD mechanism to implement the relevant method.
    1. Mastering the timing of GCD and operation queues
    • The dispatch queue is not the only scenario when troubleshooting multi-threading and task management issues.
    • The operations queue provides a set of high-level OBJECTIVE-C APIs that implement most of the functionality of pure gcd, and can perform more complex operations that require additional code if the operation is implemented using GCD instead.
    1. Perform tasks based on system resource status through the dispatch group mechanism
    • A series of tasks is valuable as a dispatch group. Developers can be notified when this set of tasks is completed.
    • With dispatch group, you can perform multiple tasks simultaneously in a concurrent dispatch queue. At this point GCD will dispatch these concurrently executed tasks according to the system resource status. Developers need to write a lot of code if they want to implement this feature themselves.
    1. Use Dispatch_once to execute thread-safe code that runs only once
    • It is often necessary to write thread-safe code that executes only once. With the Dispatch_once function provided by GCD, this function can be implemented easily.
    • tags should be declared in the static or global scope, so that when you pass a block that only executes once to the Dispatch_once function, the token passed in is the same.
    1. Do not use Dispatch_get_current_queue
    • The behavior of the Dispatch_get_current_queue function is often different from what the developer expected. This function is deprecated and should only be used for debugging purposes.
    • Because the dispatch queue is organized hierarchically, it is not possible to describe the concept of the current queue with a single queue object alone.
    • The Dispatch_get_current_queue function is used to resolve deadlocks caused by non-reentrant code, but problems that can be resolved with this function can often be resolved using queue-specific data instead.
Vii. framework of the system
    1. Familiar with the system framework
    • Many system frameworks can be used directly. The most important of these is foundation and corefoundation, which provide many of the core capabilities needed to build an application.
    • Many common tasks can be done using frames, such as audio and video processing, network communication
      Data management, and more.
    • Keep in mind that a framework written in pure C is as important as objective-c written, and if you want to be a good objective-c developer, you should master the core concepts of C language.
    1. Multi-use block enumeration, less for loop
    • There are 4 ways of traversing collection. The most basic method is the For loop, followed by the Nsenumerator traversal method and the fast traversal method, the newest and most advanced Way is the block enumeration method.
    • The Block enumeration method itself can perform traversal operations concurrently through GCD, without having to write code separately. This is not easily achieved with other traversal methods.
    • If you know in advance what object the collection is to traverse, you should modify the block signature to indicate the specific type of object.
    1. Teams customize their memory management semantics of collection using seamless bridging
    • With the seamless bridging technology, you can convert Objective-c objects in the foundation framework to and from the C language data structures in the corefoundation framework.
    • When creating collection at the corefoundation level, you can specify many callback functions that represent how this collection should handle its elements. The seamless bridging technology can then be used to transform it into a Objective-c collection with special memory management semantics.
    1. Building a cache is choosing Nscachae instead of nsdictionary
    • You should use Nscache instead of Nsdictionary objects when implementing the cache. Because Nscache can provide graceful auto-deletion functionality and is thread-safe, in addition, it differs from the dictionary and does not copy keys.
    • You can set an upper limit on the Nscache object to limit the total number of objects in the cache and the total cost, and these scales define the timing of the cache's deletion of objects. But never take these scales as a reliable hard limit, they only guide the Nscache.
    • The Nspurgeabledata is used in conjunction with the Nscache to automatically erase data, that is, when the Nspurgeabledata object's memory is discarded by the system, the object itself is removed from the cache by 4.
    • If the cache is used properly. The response speed of the application can be improved. Only data that is computationally cumbersome can be worth putting in caches, such as those that need to be fetched from the network or read from disk.
    1. Streamlining the implementation code for initialize and load
    • In the load phase, if the Load method is implemented, the system calls it. This method can also be defined in the classification, and the class load method is called first in the classification. Unlike other methods, the Load method does not participate in the overwrite mechanism.
    • The system sends a initialize message to a class before it is first used. Since this method adheres to ordinary replication rules, it is often a matter of determining which class is currently initialized.
    • Both the load and initialize methods should be streamlined, which helps to keep the application responsive and reduces the chance of introducing dependency loops.
    • Global constants that cannot be set at compile time can be initialized in the Initialize method.
    1. Don't forget, Nstimer will keep its target object.
    • The Nstimer object retains its target until the timer itself fails, the call to the Invalidate method invalidates the timer, and the one-time timer expires after the task is triggered.
    • A timer that executes a task repeatedly, it is easy to introduce circular references, and the target object that enters the timer retains the timer itself, which is sure to cause a circular reference. This circular reference may occur directly or indirectly through other objects in the object graph.
    • You can expand the functionality of Nstimer and use blocks to break circular references. However, unless Nstimer will provide this functionality in a public interface in the future, you must create a taxonomy to add the relevant implementation code to it.


Thousand Huang 89
Links: https://www.jianshu.com/p/d440d06994ac#
Source: Pinterest
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.

52 effective ways to write high-quality iOS and OS X code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.