Chapter 1: Familiarize yourself with Objective-C

🇨🇳 Article 1: Learn about the origins of Objective-C

  • Objective-c is super for adding object-oriented features to C. Objective-c says that message structures are dynamically bound, that is, object types are checked at run time. It is up to the runtime environment, not the compiler, to decide what code to execute after receiving a message.
  • Understanding the core concepts of C helps you write good Objective-C programs. In particular, learn memory models and Pointers.
NSString *theString = @"Hello World";
NSString *theString2 = @"Hello World";
NSLog(@"theString:%p --- theString:2%p",theString,theString2);
    
Copy the code

Print result:

theString:0x11bb0d158 --- theString:20x11bb0d158
Copy the code

The two variables are the same pointer to the same block of memory. At this point, assign theString2 to “Hello World!!!!”

theString2 = @"Hello World !!!!" ; NSLog(@"theString:%p --- theString:2%p",theString,theString2);Copy the code

Print result:

theString:0x12002e158 --- theString:20x12002e198
Copy the code

At this point, the two become different memory addresses. Therefore, the essence of an object is a pointer to a memory area. The storage location of the pointer depends on the area declared by the object and whether the member variable points to it. If an object is declared inside a method, memory is allocated to the stack and automatically cleaned up as the stack frame pops up. If the object is a member variable, memory is allocated in the heap and the declaration cycle is managed by the programmer. In addition, in the process of exploring the essence of the object, we found that the essence of the object isa pointer declared as isa. A pointer occupies 4 bytes in 32-bit computer and 8 bytes in 64-bit computer. In iOS system, isa pointer actually occupies 16 bytes of memory area. In this article, we use clang to convert OC code into C++ code to explore the actual memory size of an object. For details, please refer to the underlying principles of iOS – NSObject memory size

🇦🇫 Rule 2: Introduce as few other header files as possible in the class header file

  • Do not introduce headers unless absolutely necessary. In general, use forward declarations in the header files of one class to refer to others, and introduce those headers in the implementation files. Doing so minimizes the coupling between classes.
  • Sometimes forward declarations are not possible, such as declaring that a class follows a protocol. Try to move the declaration “this class follows a protocol” into the class-Continuation category. If not, place the protocol in a separate header file and import it.
//Student.h @class Book; @interface Student: nsobject@property (nonatomic, strong) BOOK * BOOK; @end //student.m #import "Book.h" @implementation Student - (void)readBook { NSLog(@"read the book name is %@",self.book); } @endCopy the code

🇦🇬 clause 3: use literal syntax more often than its equivalent

  • Literal syntax should be used to create strings, numbers, arrays, and dictionaries. This is much more concise than the normal method of creating such objects.
  • The element corresponding to an array index or a dictionary key should be accessed by a subscript operation.
  • Create an array or dictionary with literal syntax, and throw an exception if there is nil in the value. So make sure there’s no nil in the value.
0️ literal value
NSNumber *number = [NSNumber numberWithInteger:10086];
Copy the code

Instead of

NSNumber *number = @10086;
Copy the code
1️ literal array
NSArray *books = [NSArray arrayWithObjects:@" Algorithm illustration ",@" High performance iOS application development ",@"Effective Objective-C 2.0", nil]; NSString *firstBook = [books objectAtIndex:0];Copy the code

Instead of

NSArray *books = @[@" algorithm graphics ",@" high performance iOS app development ",@"Effective Objective-C 2.0"]; NSString *firstBook = books[0];Copy the code
2️ literal dictionary
NSDictionary * info1 = [NSDictionary dictionaryWithObjectsAndKeys: @ "geek learn wei," @ "name", [NSNumber numberWithInteger: 18], @ "age",  nil]; NSString *name1 = [info1 objectForKey:@"name"];Copy the code

Instead of

NSDictionary * info2 = @ {@ "name:" @ "geek learn wei," @ "age" : 18, @}; NSString *name2 = info2[@"name"];Copy the code
3️ variable array and dictionary
[arrayM replaceObjectAtIndex:0 withObject:@"new Object"];
[dictM setObject:@19 forKey:@"age"];
Copy the code

Instead of

arrayM[0] = @"new Object";
dictM[@"age"] = @19;
Copy the code
4 ️ ⃣ limitations
1. Objects created by literal syntax must belong to the Foundation framework. Custom classes cannot be created using literal syntax.
2. Objects created using literal syntax can only be immutable. If you want it to be mutable, you can make a deep copy of it
NSMutableArray *arrayM = @[@1,@"123",@"567"].mutableCopy;
Copy the code

🏳️🌈 Rule 4: Use type constants and use them sparkly#definePreprocessing instruction

  • Do not define constants with preprocessor instructions. Constants defined this way contain no type information, which the compiler uses to perform lookup and replace operations before compilation. Even if someone redefines a constant value, the compiler does not generate a warning message ⚠️, which results in inconsistent constant values in the application.
  • Used in the implementation filestatic constTo define constants that are visible only within the compilation unit. Because such constants are not in the global symbol table, there is no need to prefix their names.
  • Used in header filesexternTo declare global constants and define their values in the relevant implementation files. Such constants appear in the global symbol table, so the name should be delimited, usually prefixed by the name of the class associated with them.

Preprocessor instructions are code copies. During compilation, all preprocessor instructions in the code will be expanded and filled into the code. Reducing preprocessor instructions will also speed up compilation.

Private constants
.m
static const NSTimeInterval kAnimationDuration = 0.3;
Copy the code
Global constants
.h
extern NSString * const XWTestViewNoticationName;

.m
NSString * const XWTestViewNoticationName = @"XWTestViewNoticationName";
Copy the code

🇩🇿 rule 5: Use enumeration to represent status, option, status code

  • Enumerations should be used to represent the state of the state machine, the options passed to the method, and the status code equivalents, giving these values easy-to-understand names.
  • If an option passed to a method is represented as an enumeration type, and multiple options can be used at the same time, each option is defined as a power of two so that it can be combined by bitwise or operation.
  • withNS_ENUUMNS_OPTIONSMacro to define enumerated types and indicate their underlying data types. Doing so ensures that the enumeration is implemented using the underlying data type of the developer’s choice, not the compiler’s choice.
  • In dealing with enumerated typesswitchDo not implement in the statementdefaultBranch. That way, after adding a new enumeration, the compiler will prompt the developer:switchStatement does not handle all enumerations.
Typedef NS_OPTIONS(NSUInteger, XWDirection) {XWDirectionTop = 0, XWDirectionBottom = 1 << 0, XWDirectionLeft = 1 << 1, XWDirectionRight = 1 << 2, }; /// Const enumeration typedef NS_ENUM(NSUInteger, SexType) {SexTypeMale, SexTypeFemale, SexTypeUnknow,};Copy the code

Chapter 2: Objects, messages, runtime

🇦🇫 Article 6: Understand the concept of “attributes”

  • You can use@propertySyntax to define the data encapsulated in an object.
  • Attributes specify the correct semantics required to store data.
  • When setting the instance variable corresponding to a property, be sure to follow the semantics declared for that property.

Using properties the compiler automatically generates get and set methods for instance variables and changes. You can also use @synthesize to specify the name of an instance variable, and use @dynamic to keep the compiler from automatically generating get and set methods. Attributes can be divided into four categories, respectively:

1. The atomicity
  • atomicAtomicity, system default. It’s not thread safe,releaseMethods are not constrained by atomicity.
  • nonatomicThe atomic
2. Read and write permissions
  • readwriteReadable and writable, with both get and set methods.
  • readonlyRead-only, only get method.
Memory management semantics
  • assignSimple assignment for basic member types
  • strongRepresents “ownership relationship”. When setting a new value, the new value is retained, the old value is released, and the new value is set to the current property.
  • weakIndicates a non-ownership relationship. When setting a new value, neither the new value is retained nor the old value is released. withassignSimilarly, set nil when the object is destroyed
  • unsafe_unretainedThis is a non-owning relationassign, applies only to object types. The target object does not automatically clear when enchanted.
  • copyThe relationship between expression andstrongSimilar. The difference is that when setting a new value, the new value is not retained, but copied and assigned to the current property.
4. The method name
  • getter=<name>Specify the name of the method to get the getter, as in:@property (nonatomic, getter=isOn) BOOL on;
  • setter=<name>Specifies the method name of a setter.

🇦🇷 Rule 7: Try to access instance variables directly from within the object

  • When reading data from within an object, it should be read directly through instance variables, and when writing data, it should be written through properties.
  • In the initialization method anddeallocMethod should read and write data directly from instance variables.
  • Lazy loading is sometimes used to configure data, in which case properties are required to read the data.

The advantage of using member variables directly inside an object rather than using point syntax is that it is faster to execute without having to go through Objective-C method distribution, in which case the compiler directly accesses the chunk of memory that holds the object instance variable. However, direct access to member variables does not trigger KVO, so whether you use dot syntax to access attributes or use member variables directly depends on the behavior.

🇦🇪 Article 8: Understand the concept of “object equality”

  • If you want to monitor the equality of objects, please provideisEqual:hashMethods.
  • The same object must have the same hash code, but two objects with the same hash code may not be the same.
  • Instead of blindly monitoring each attribute individually, you should tailor your detection plan to your specific needs.
  • writehashMethod should use algorithms that are fast in computation and have a low probability of hash code collisions.

The normal way to compare equality == is to compare whether two object Pointers are the same. Overriding the isEqual method on a custom object can be done this way:

- (BOOL)isEqualToBook:(Book *)object { if (self == object) return YES; if (! [_name isEqualToString:object.name]) return NO; if (! [_author isEqualToString:object.author]) return NO; return YES; }Copy the code

Override hash methods on custom objects using this method:

@implementation Book
- (NSUInteger)hash {
    NSUInteger nameHash = [_name hash];
    NSUInteger authorHash = [_author hash];
    return nameHash ^ authorHash;
}
@end
Copy the code

🇦🇼 Article 9: Hide implementation details in “family pattern”

  • The family of classes pattern hides implementation details behind a simple set of common interfaces
  • Class families are often used in system frameworks
  • Be careful when inheriting subclasses from the common abstract base class of a family of classes, and read development documentation if available

For example, declare a book as a base class, create related classes through the “class family pattern”, and implement related methods in subclasses of the corresponding type. As follows:

.h
typedef NS_ENUM(NSUInteger, BookType) {
    BookTypeMath,
    BookTypeChinese,
    BookTypeEnglish,
};
@interface Book : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *author;
+ (instancetype)bookWithType:(BookType)type;
- (void)read;
@end
Copy the code
.m
@interface BookMath : Book
- (void)read;
@end
@implementation BookMath
- (void)read {
    NSLog(@"read The Math");
}
@end

@interface BookChinese : Book
- (void)read;
@end
@implementation BookChinese
- (void)read {
    NSLog(@"read The Chinese");
}
@end

@interface BookEnglish : Book
- (void)read;
@end
@implementation BookEnglish
- (void)read {
    NSLog(@"read The English");
}
@end

@implementation Book
+ (instancetype)bookWithType:(BookType)type {
    switch (type) {
        case BookTypeMath:
            return [BookMath new];
            break;
        case BookTypeChinese:
            return [BookChinese new];
            break;
        case BookTypeEnglish:
            return [BookEnglish new];
            break;
    }
}
@end
Copy the code

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

  • You can connect two objects through the “associate object” mechanism
  • Memory management semantics can be specified when defining associated objects to mimic the “owning” and “not owning” relationships used when defining attributes
  • Associate objects should be chosen only when other options are not available, as this often introduces hard-to-find bugs

Syntax for associated objects:

#import <objc/runtime.h> // Setter method void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, Id _Nullable value, objc_AssociationPolicy policy) // Getter id objc_getAssociatedObject(id _Nonnull Object, Const void * _Nonnull key) void objc_removeAssociatedObjects(id _Nonnull object)Copy the code

Example 1: The original writing of aggregating declarations and executions using associated objects

- (void)testAlertAssociate {UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@" Delegate :self cancelButtonTitle:@" Cancel "otherButtonTitles:@" wake up early ",@" go to bed early ", nil]; [alertView show]; } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView ClickedButtonAtIndex :(NSInteger)buttonIndex {if (buttonIndex == 1) {NSLog(@" you need to get up early "); }else if (buttonIndex == 2) {NSLog(@buttonIndex == 2); }else{NSLog(@" cancel "); }}Copy the code

Rewrite to:

static void *kAlertViewKey = "kAlertViewKey"; - (void)testAlertAssociate {UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@" Delegate :self cancelButtonTitle:@" Cancel "otherButtonTitles:@" wake up early ",@" go to bed early ", nil]; [alertView show]; Void (^AlertBlock)(NSUInteger) = ^(NSUInteger buttonIndex){if (buttonIndex == 1) {NSLog(@" you need to get up early "); }else if (buttonIndex == 2) {NSLog(@" you should go to bed early "); }else{NSLog(@" cancel "); }}; objc_setAssociatedObject(alertView, kAlertViewKey, AlertBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { void(^AlertBlock)(NSUInteger) = objc_getAssociatedObject(alertView, kAlertViewKey); AlertBlock(buttonIndex); }Copy the code

This allows the implementation and declaration to be processed together, fetching the associated code block to execute at the callback. Makes code more readable.

As we all know, declaring a property in an Objective-C class only automatically generates declarations of getter and setter methods for that property. There is no implementation. So it’s a good way to actually add attributes to a category, using associated objects.

//NSTimer+XW.h
@interface NSTimer (XW)
@property (nonatomic, assign) NSUInteger tag;
@end

//NSTimer+XW.m
#import "NSTimer+XW.h"
#import <objc/runtime.h>
@implementation NSTimer (XW)
static void *kXW_NSTimerTagKey = "kXW_NSTimerTagKey";
#pragma mark - tag / getter setter
/// setter
- (void)setTag:(NSUInteger)tag {
    NSNumber *tagValue = [NSNumber numberWithUnsignedInteger:tag];
    objc_setAssociatedObject(self, kXW_NSTimerTagKey, tagValue, OBJC_ASSOCIATION_ASSIGN);
}
/// getter
- (NSUInteger)tag {
    NSNumber *tagValue = objc_getAssociatedObject(self, kXW_NSTimerTagKey);
    return tagValue.unsignedIntegerValue;
}
@end
Copy the code

🇦🇿 Article 11: Understandingobjc_msgSendThe role of

  • The message consists of the receiver, selectors, and parameters. To send a message to an object is to call a method on that object.
  • All messages sent to an object are processed by a dynamic message dispatch system, which detects the corresponding method and executes its code.

Objc_msgSend Execution process

Note: Teacher SEEMYGO MJ

As we all know, the essence of a method call in OC is to send the message objc_msgSend, whose prototype is:

/// self: message receiver, CMD: select the child that executes the method,... Void objc_msgSend(id self, SEL CMD,...) ;Copy the code

For example 🌰 :

// class id returnValue = [self doSomething:@"param"]; Id returnValue = objc_msgSend(xx, @selector(doSomething:),@"param");Copy the code

OC designed a caching mechanism when implementing this mechanism. Every time a method is called, the method will be cached. Executing the same method again will improve the execution efficiency, so that the speed difference between it and the static binding method will not be so wide.

🇪🇬 Article 12: Understand the message forwarding mechanism

  • If the object cannot respond to a seletor, the message forwarding process is entered
  • With dynamic method resolution at run time, we can add a method to a class when it is needed
  • Objects can pass on certain selectors that they cannot decipher to other objects for processing
  • After the above two steps, if you still can’t handle the selectors, start the full message forwarding mechanism

The whole process of message forwarding:

If an unimplemented method is called, the console throws the classic error message: unrecognized selector sent to instance 0xxx

There is also a little-known history between method calls and exceptions thrown: message forwarding mechanisms. After the above error message is to call the method to realize no underlying forwarded to NSObject doedNotRecognizeSelector: thrown by the method. The specific process of message forwarding is as follows:

Dynamic method parsing
+ (BOOL)resolveClassMethod:(SEL) SEL {return [super resolveClassMethod: SEL]; } / / / call the unrealized instance method + (BOOL) resolveInstanceMethod: (SEL) SEL {return [super resolveInstanceMethod: SEL]; }Copy the code

Indicates whether an instance method can be added to handle this method. If this class needs to be written in advance in the program, it can be added dynamically in the Runtime class_addMethod.

ResolveInstanceMethod :(SEL) SEL {if (SEL == @selector(test)) {resolveInstanceMethod:(SEL) SEL {if (SEL == @selector(test)) {resolveInstanceMethod:(SEL) SEL {if (SEL == @selector(test)) TrendsMethod // Parameter 1: the class to which the new method was added, parameter 2: the name of the new method in the class to which the new method was added, parameter 3: the implementation of the new method // Parameter 4: the parameter return value description of the new method, such as v@: - No parameter no return value i@: - No argument returns Int I @:@ - one argument returns Int class_addMethod(self, sel, (IMP)class_getMethodImplementation([self class], @selector(trendsMethod)), "v@:"); return YES; / / returns YES or NO can} return here [super resolveInstanceMethod: sel]; } - (void)trendsMethod {NSLog(@" this is a dynamically added method "); }Copy the code
Backup receiver
/ / / unrealized instance methods can be forwarded to other class to handle - (id) forwardingTargetForSelector (SEL) aSelector {if (aSelector = = @selector(testInstanceMethod)) { return [Chinese new]; / / message is forwarded to can handle the instance method of a class object} return [super forwardingTargetForSelector: aSelector]; } / / / unrealized class methods can be forwarded to other class to handle + (id) forwardingTargetForSelector aSelector: (SEL) {if (aSelector = = @ the selector (testClassMethod)) { return [Chinese class]; / / message is forwarded to return to deal with the method of class} [super forwardingTargetForSelector: aSelector]; }Copy the code
Complete message forwarding

If none of the above procedures is handled, the program will have one last chance to handle it, which is:

Dynamic forwarding instance method
/ / / the method signature, define the return value, parameter - (NSMethodSignature *) methodSignatureForSelector (SEL) aSelector {if (aSelector = = @selector(testInstanceMethod:)) { /// "v@:@" return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; } return [super methodSignatureForSelector:aSelector]; } /// NSInvocation invocation encapsulates a function call //anInvocation target - Method invocation GetArgument :<#(nonnull void *)#> atIndex:<#(NSInteger)#> - Get the index - (void)forwardInvocation:(NSInvocation) *)anInvocation { if (anInvocation.selector == @selector(testInstanceMethod:)) { return [anInvocation invokeWithTarget:[Chinese new]]; / / will be transferred to another to achieve the object of this method for processing} return [super forwardInvocation: anInvocation]; }Copy the code
Dynamically forward class methods
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if (aSelector == @selector(testClassMethod:)) { /// "v@:@" return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; } return [super methodSignatureForSelector:aSelector]; } + (void)forwardInvocation:(NSInvocation *)anInvocation { if (anInvocation.selector == @selector(testClassMethod:)) { return [anInvocation invokeWithTarget:[Chinese class]]; / / will be transferred to another to achieve the object of this method for processing} return [super forwardInvocation: anInvocation]; }Copy the code

Above method in the forward forwardingTargetForSelector method can achieve the same function, why in the end this process? So, it’s more than that. In fact, the unprocessed method can be implemented directly in the function, as follows:

/ / / the method signature, define the return value, parameter - (NSMethodSignature *) methodSignatureForSelector (SEL) aSelector {if (aSelector = = @selector(testInstanceMethod:)) { /// "v@:@" return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; } return [super methodSignatureForSelector:aSelector]; - (void)forwardInvocation:(NSInvocation *)anInvocation {if (anInvocation. Selector == @selector(testInstanceMethod:)) {/// the unimplemented method NSLog(@" this method %s Student is not implemented!! ,sel_getName(anInvocation.selector)); id param; [anInvocation getArgument:&param atIndex:2]; NSLog(@" passed in: %@ - can make it do something ",param); return; } return [super forwardInvocation:anInvocation]; }Copy the code
Practical application of message forwarding

We can use a message forwarding mechanism so that the program never has a crash like an unrecognized selector sent to instance 0xxx. With the details printed on the console, we can implement a classification of NSObject as follows:

#import "NSObject+XWTool.h" @implementation NSObject (XWTool) - (NSMethodSignature *) methodSignatureForSelector (SEL) aSelector {if ([self respondsToSelector: aSelector]) {/ / / the implemented do not handle return [the self methodSignatureForSelector:aSelector]; } return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } - (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@" in the %@ class, the invocation is not implemented instance method: %@ ",NSStringFromClass([self class]),NSStringFromSelector(anInvocation.selector)); } + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if ([self respondsToSelector:aSelector]) {/// Have not achieve do handle return [self methodSignatureForSelector: aSelector]; } return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } + (void)forwardInvocation:(NSInvocation *)anInvocation {NSLog(@" in the %@ class, the invocation is not implemented: %@ ",NSStringFromClass([self class]),NSStringFromSelector(anInvocation.selector)); }Copy the code

🇪🇹 Clause 13: Debug “black box method” with “Method Blending Technique”

  • At run time, you can add or replace method implementations for selectors to the class
  • Replacing a method implementation with another implementation is a process called “method blending,” and developers often use this technique to add new functionality to an existing class
  • In general, the only time you need to modify a method implementation at run time is when debugging the program, and this practice should not be abused

The essence is to use runtime to implement method substitution at runtime:

Method_exchangeImplementations (Method _Nonnull m1, Method _Nonnull m2);Copy the code

The implementation of the method can be obtained as follows:

// get method implementation CLS: method object, name: Method class_getInstanceMethod(Class _Nullable __unsafe_unretained CLS, SEL _Nonnull name)Copy the code
In practical use, the console prints the currently displayed controller information while the program is running, which is useful during code familiarisation:
//UIViewController+XWDebug.m #import "UIViewController+XWDebug.h" #import <objc/runtime.h> @implementation UIViewController (XWDebug) #ifdef DEBUG + (void)load { static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{originViewDidLoad = class_getInstanceMethod(self, @selector(viewDidLoad)); Method xwViewDidLoad = class_getInstanceMethod(self, @selector(xw_viewDidLoad)); method_exchangeImplementations(originViewDidLoad, xwViewDidLoad); OriginViewDidAppear = class_getInstanceMethod(self, @selector(viewDidAppear:)); Method xwViewDidAppear = class_getInstanceMethod(self, @selector(xw_viewDidAppear:)); method_exchangeImplementations(originViewDidAppear, xwViewDidAppear); }); } - (void)xw_viewDidLoad {dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"********* %@ **** viewDidload ****",self); }); [self xw_viewDidLoad]; } - (void)xw_viewDidAppear:(BOOL)animated { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue (), ^ {NSLog (@ "* * * * * * * * * % @ viewDidAppear * * * * * * * *", the self). }); [self xw_viewDidAppear:animated]; } #else #endif @endCopy the code

🇮🇪 Clause 14: Understand the meaning of “class objects”

  • Each instance has a pointer to a Class object to name its type, and these Class objects form the Class inheritance system
  • If the object type cannot be determined at compile time, the type information query method should be used to find out
  • Use type information queries to determine object types rather than directly comparing class objects, some of which may implement message forwarding

Determine whether the object is an instance of a class:

- (BOOL)isMemberOfClass:(Class)aClass;
Copy the code

Determine whether an object is an instance of a class or a derived class:

- (BOOL)isKindOfClass:(Class)aClass;
Copy the code

For example, to determine an instance of NSDictionary:

NSMutableDictionary  *dict = @{@"key":@"value"}.mutableCopy;
BOOL example1 = [dict isMemberOfClass:[NSDictionary class]];            // NO
BOOL example2 = [dict isMemberOfClass:[NSMutableDictionary class]];     // NO
BOOL example3 = [dict isKindOfClass:[NSDictionary class]];              // YES
BOOL example4 = [dict isKindOfClass:[NSMutableDictionary class]];       // YES
BOOL example5 = [dict isKindOfClass:[NSArray class]];                   // NO
//    BOOL example6 = [dict isKindOfClass:[__NSDictionaryM class]];     // YES
Copy the code

Note that in the judgment of [dict isMemberOfClass:[NSMutableDictionary class]], we actually return NO, although we declare dict as an instance of NSMutableDictionary, But dict is actually an instance of the __NSDictionaryM class, verifiable on the console:

(lldb) po [dict isMemberOfClass:[__NSDictionaryM class]]
YES
Copy the code

Examples written in Effective Objective-C 2.0 are wrong!!

Therefore, it is better to believe in books than to have no books, and believe in the actual verification, which also inspires readers to try to verify the examples in the process of reading, perhaps the author is taking it for granted when writing a book.

After the end of the first two chapters, I will publish my reading/combat notes of the remaining chapters in the following days. I look forward to learning and making progress together with all the gods.

To be continued…