[Effective Objective-C] - Reading Notes

Posted by jackyhuphp on Sat, 15 Jan 2022 22:37:33 +0100

Article 1: understand the origin of Objective-C

  1. Objective-C evolved from Smalltalk (the ancestor of message language), so Objective-C uses "message structure" rather than "function call".

The difference between the two is as follows:

//Messaging (Objective-C)
Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];

//Function calling (C++) 
Object *obj = new Object;
obj->perform(parameter1, parameter2);
  1. Objective-C is a "superset" of C, so all functions in C language are still applicable when writing Objective-C code.
  2. Pointers in Objective-C language are used to indicate objects, and the memory occupied by Objective-C objects is always allocated in "heap space", never on "stack".
    For example, to declare an object of NSString type, you can use the following syntax:
NSString *something = @"The string";

It can't be written like this:

NSString *something;
  1. The memory allocated in the heap must be managed directly, and the memory allocated on the stack to save variables will be cleaned up automatically when its stack frame pops up
  2. Objective-C abstracts heap memory management, and does not need malloc and free to allocate or release the memory occupied by objects. This part of the work is abstracted into a set of memory management architecture called "reference counting".
  3. In Objective-C, variables without * in the definition are sometimes encountered. They may use "stack space. These variables do not store Objective-C objects", such as CGRect.
  4. Objective-C adds object-oriented features to the C language,

Article 2: introduce as few other header files as possible into the class header file

  1. If you need to reference a class Person, you don't need to know all the details of this class. You only need to know a class name, so you don't need to use:
#import "Person.h"

It only needs:

@class Person; 

This is called the "forward declaration" class.
The implementation file needs to use all the interface details in the Person class, so you need to introduce the header file of the Person class. Delay the time of importing header files as much as possible and only import them when necessary, so as to reduce the number of header files required by class users and reduce the compilation time.

  1. Sometimes forward declarations cannot be used, such as declaring that a class follows a protocol. In this case, it is best to transfer the declaration of "this class follows a protocol" to the "class continuation classification"; If not, put the protocol in a separate header file and import it.

Article 3: use more literal grammar and less equivalent methods

Advantages: practical literal syntax can reduce the length of source code, make it easier to read, facilitate operation, and avoid program crash caused by the existence of nil.

Literal value

NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
int x = 5;
float y = 6.32f;
NSNumber *experssionNumber = @(x * y);

Literal array

NSArray *animals = @[@"dog", "cat", "mouse"];
NSString *dog = animals[0];

//Create literal array
id object1 = @"dog";
id object2 = nil;
id object3 = object1;
NSArray *array1 = [NSArray arrayWithObjects:object1,object2,object3,nil];

NSArray *array2 = @[object1, object2, object3];

The first of the above two creation methods does not report an error, but the array array1 has only one element: object1. When using the "arrayWithObjects" method, each parameter will be processed in turn until nil is found, so the method will stop in advance; When using literal syntax, an exception is thrown because object2 is nil.

Literal dictionary

NSDictionary *personData = @{@"firstName":@"Jack",
                             @"lastName":@"Galloway",
                             @"age":@28};
//The objects and keys in the dictionary must be Objective-C objects, so "28" must be encapsulated in the NSNumber instance.
//To access keys in the dictionary:
NSString *lastName = personData[@"lastName"];

Variable arrays and dictionaries

The standard practice for modifying the contents of variable arrays and dictionaries is:

 mutableArray[1] = @"dog";
 mutableDictionary[@"lastName"] = @"Galloway";

limitations

Strings, arrays and dictionary objects created by practical literal syntax are immutable. If you want a mutable object, you need to copy it.

NSMutableArray *mutable = [@[@1, @2, @3, @4] mutableCopy];

Article 4: use more type constants and less #define preprocessing instructions

  1. The constants defined with the #define preprocessing instruction have no type information, so try to use the following syntax to define constants:
static const NSTimeInterval kAnimationDuration = 0.3;
  1. Note the constant name. The common naming method is: if the constant is limited to an "implementation file", add the letter k in front of it; If constants are visible outside the class, they are usually prefixed with the class name.
  2. Variables must be declared with both static and const. When trying to modify a variable declared by the const modifier, the compiler will report an error, while the static modifier means that the variable is visible only in the compilation unit that defines the variable.
  3. Use the extern keyword to "declare" a global variable in the header file and "define" its value in the implementation file. This constant should appear in the global symbol table, so its name should be separated, usually prefixed with the class name it wants to be related to.

Article 5: use enumeration to represent status, options and status codes

  1. Use enumerations to represent the state of the state machine, the options passed to the method, and the status code equivalent, and give these values easy to understand names.
  2. If the option passed to a method is represented as an enumeration type, and multiple options are used at the same time, the option values are defined as powers of 2, so that they can be combined by bitwise OR operation.
  3. With NS_ENUM and NS_OPTIONS macro to define the enumeration type and indicate its underlying data type. This ensures that the enumeration is implemented with the underlying data type selected by the developer, not the type selected by the compiler.
  4. Do not implement the default branch in a switch statement that handles enumeration types. In this way, the developer will be prompted after adding a new enumeration: the switch statement does not handle all enumerations.

Topics: iOS objective-c OC