1: Origin of Objective-C

Objective-c (OC) evolved from the SmallTalk language. OC adopts the syntax of “message structure” and is a dynamic language. In contrast to traditional “function call” languages, the actual actions performed by OC are determined at run time, not compile time. Just as “function call” functions are polymorphic.

OC’s *** objects *** are always allocated on the heap. But instead of using malloc and free to allocate and free this memory, this is done automatically by OC’s “reference counting”.

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

We already know this rule in C, known as forward declaration. When you don’t need to know the details of a class, it’s best to pre-declare the class in a header file and then reference the class’s header file in an implementation file. If the EPerson class has an EEmployer member:

//EPerson.h @class EEmployer; // @interface EPerson: NSObject... @property (nonatomic, strong) EEmployer *employer; @end //EPerson.mm #import "EPerson.h" #import "EEmployer.h" @implementation EPerson ... @endCopy the code

This has several advantages: first, it optimizes compilation time; Instead, you can avoid header circular references. While the #import directive avoids an infinite loop, it means that one of the class files is not being compiled correctly.

3. Use string literal syntax

Use c-like syntax, such as:

NSString *somStr = @"This is a string literal"; NSNumber *someNum = @1; NSNumber * floatNum = @ 2.5 f; NSNumber *boolNum = @YES; NSArray *animals = @[@"cat",@"dog",@"mouse"]; NSDictionary *personDic = @{@"firstName":@"Matt", @"lastName":@"Galloway"};Copy the code

Advantages:

  1. Simple and easy to read.
  2. Simple to write and modify.
  3. For arrays and dictionaries, you can also throw exceptions early. For example, if there are nil elements in there, literal syntax will throw an exception directly, but ordinary alloc methods generate arrays or dictionaries that only truncate the elements before nil, misleading us.

Disadvantages:

  1. In addition to strings, objects created by literal syntax must belong to the Foundation framework and not to custom classes.
  2. Objects created by literal syntax are immutable. To make a mutable version of an object, you must make a copy of it.

4. Replace macro definitions with type constants

This was mentioned in C, and the advantage is that you can validate types using compiler features.

If the constant is used only in a compilation unit, it is static const in its.m file

If the constant needs to be globally visible, declare the global variable using extern in a header file and define its value in an implementation file. Such constants appear in the global symbol table and are usually prefixed by the name of the class associated with them.

5. Enumeration types

typedef NS_ENUM(NSUInteger, EOCConnectionState) {
    EOCConnectStateDisconnected,
    EOCConnectStateConnecting,
    EOCConnectStateConnected    
}

typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
    EOCPermittedDirectionUp     = 1 << 0,
    EOCPermittedDirectDown      = 1 << 1,
    EOCPermittedDirectLeft      = 1 << 2,
    EOCPermittedDirectRight     = 1 << 3,
}
Copy the code

6. Understand “attributes”

The @synthesize can change the default instance variable name, but half of it is not recommended in order to make the code more readable.

@dynamic prevents the compiler from automatically synthesizing access methods. No access methods are defined at compile time, and no errors are reported, trusting that these methods can be found at run time.

  • atomic

Used when multiple threads simultaneously access a property. In development, we usually use nonatomic because atomicity requires synchronous locking, which is expensive. In addition, when a thread reads a property value several times in a row, other threads rewrite the value at the same time. Therefore, even if the property is declared as nonatomic, it will still read different property values. Thus, “thread safety” is not guaranteed. If you really want to be “thread-safe,” you need a deeper locking mechanism.

  • Read and write access

  • Memory management semantics

  • The method name

7. Try to access instance variables directly inside objects

There are several differences between using the instance variable _firstName directly and using the access method self.firstname:

1). Direct access to the instance variable does not require message forwarding mechanism, the compiler generates a direct access to the memory region where the instance variable is located, fast.

2). Accessing instance variables directly without calling “set methods” bypasses the “memory management semantics” associated with attributes, which is not good.

3). Direct access to instance variables does not trigger KVO notifications and may cause problems.

4). Use property methods to assist breakpoint debugging

This scenario is: write instance variable, use setting method, read instance variable, direct access. This improves read and write speed while ensuring the “memory management semantics” of the properties.

Note the highlights of this scheme:

1). You should almost always access instance variables directly in an initializer, unless the variable to be initialized is declared in a superclass and cannot be accessed directly in a subclass.

2). After using lazy loading technique, access method is used to access.

8. Understand “Object equality”

When comparing objects, the “==” operator simply compares the Pointers themselves, using either the “isEqual” method or the “equivalence judgment” provided by the object itself, which requires that the object under test belongs to the same class.

In the NSObject protocol, there are two key methods for determining isotropy:

- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
Copy the code

If the isEqual method determines that two objects are equal, then the hash method must also return the same value; If the hash methods of two objects return the same value, the isEqual method does not necessarily consider them equal.

When overwriting hash methods, consider both efficiency and collision rates.

Some specific classes have their own equivalence judgments:

NSString -> isEqualToString NSArray -> isEqualToArray NSDictionary -> isEqualToDictionary

We can judge the isotropy by ourselves, which can not only improve detection speed without checking parameter types, but also make the code more beautiful and easy to read.

For example, NSArray can compare whether each element is equal (depth equivalence) or whether only part of the data is equal. Test plan should be made according to specific requirements.

After putting mutable objects into containers, try not to change the contents of objects, which is a danger.

9. Family pattern

Family patterns can hide implementation details behind abstract base classes.

“Factory mode” is one of them.

There are many classes in the Cocoa system framework, such as UIKit, NSArray, and so on.

10. Associated objects

This is used a lot in method swizzling.

Associating two objects and reading them when needed elsewhere is like adding properties to an object on the fly.

A storage policy is specified when associating, similar to attributes adding memory semantics.

UIAlertView is a good example:

- (void)askUserQuestion { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" message:"What do you want to do?" delegate:self cancelButtonTitle:@"cancel" otherButtonTitle:@"ok", nil]; void (^block)(NSInteger) = ^(NSInteger buttonIndex) { if (buttonIndex == 0) { [self doCancel]; } else { [self doContinue]; }}; objc_setAssociatedObject(alert, EOCMyAlertViewKey, block, BJC_ASSOCIATION_COPY); [alert show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey); block(buttonIndex); }Copy the code

11. Understand message passing (objc_msgSend)

We’re already familiar with the term, objc_msgSend in Objective-C, which uses a dynamic binding mechanism to decide which method to call at run time.

The compiler converts all the messages into a standard C call:

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

Objc_msgSend dynamically invokes the appropriate method based on the recipient and selector type. First, search the “list of methods” in the class to which the receiver belongs. If you find a method that matches the selector name, jump to its implementation code. If not, look up the inheritance system. If none is found, message Forwarding is implemented. At the same time, objc_msgSend caches the matching results in the class’s “quick map table” and executes them when it encounters the same message as the selector.

Each class has a table of Pointers to functions (similar to the virtual function table in C++), Pointers to the function’s implementation address, and selectors whose names are keys to look up the table.