## Effective Objective-C 2.0 Effective Objective-C 2.0
Chapter 3: Interface and API design
๐ช๐ช Rule 15: Use prefixes to avoid namespace conflicts
- Prefix your class name with the name associated with your company, application, or both, and use this prefix in all code
- If a third party library is used in the library you are developing, prefix its name
Just as its name implies is to say in their own development of the class need prefixing, iOS programmer development engineers commonly used double letter prefix, like I used to when I was in development prefixing XW, in fact, this is unscientific, because apple dad company reserves the right to use all of the “two letter prefix”, so your prefix should be three letters, not just the name of the class, There are categories, global variables…
๐ฆ๐ฉ Article 16: Provide “universal initialization methods”
- Provide a universal initialization method in the class and specify it in the documentation. All other initialization methods should call this method.
- If the universal initialization method is different from the superclass, the corresponding method in the superclass needs to be overridden
- If the initialization method of a superclass is not appropriate for a subclass, you should override the superclass method and throw an exception in it
Take a vivid example: The Chinese class
//.h // ไธญๅฝไบบ #import <Foundation/ foundation.h > @interface Chinese: NSObject @property (nonatomic, copy, readonly) NSString *firstName; @property (nonatomic, copy, readonly) NSString *lastName; @property (nonatomic, assign, readonly) NSUInteger age; - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age; + (instancetype)chineseWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age; + (instancetype)chineseWithFirstName:(NSString *)firstName lastName:(NSString *)lastName; @end //.m #import "Chinese.h" @interface Chinese() @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property (nonatomic, assign) NSUInteger age; @end@implementation Chinese - only universal initializers can perform assignments - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age { if (self = [super init]) { self.firstName = firstName; self.lastName = lastName; self.age = age; } return self; } + (instancetype)chineseWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age { Chinese *people = [[self alloc] initWithFirstName:firstName lastName:lastName age:age]; return people; } - (instanceType)init {return [self initWithFirstName:@" lastName:@" age:1]; } + (instancetype)chineseWithFirstName:(NSString *)firstName :(NSString *)lastName {return [self chineseWithFirstName:firstName lastName:lastName age:1]; } @endCopy the code
The Student class inherits from Chinese
#import "chinese.h" @interface Student: Chinese @property (nonatomic, strong, readonly) NSArray *homework; - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age homework:(NSArray *)homework; + (instancetype)studentWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age homework:(NSArray *)homework; + (instancetype)studentWithHomework:(NSArray *)homework; @end //.m #import "Chinese.h" @implementation Student { NSMutableArray *p_homework; } // subclass overrides the parent class universal initializer function - change the default value! - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age { return [self initWithFirstName:firstName lastName:lastName age:age homework:@[]]; } /// specify the initializer - call the parent initializer directly - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age homework:(NSArray *)homework { if (self = [super initWithFirstName:firstName lastName:lastName age:age]) { p_homework = homework.mutableCopy; } return self; } /// specify the initialization class method + (instancetype)studentWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age homework:(NSArray *)homework { return [[self alloc] initWithFirstName:firstName lastName:lastName age:age homework:homework]; } // (instanceType)init {return [self initWithFirstName:@" fatherland "lastName:@" flowerage" age:6 homework:@[]]; } / / / other initialization method + (instancetype) studentWithHomework homework: (NSArray *) {return [self studentWithHomework: homework]; } @endCopy the code
๐ฆ๐ด Article 17: Implementationdescription
methods
- implementation
description
Method returns a meaningful string describing the instance - You want to print a more detailed object description during debugging. Should be implemented
debugDescription
methods
If you print a custom object directly, the console only displays the address of the object, not the details of the object. The address of the object pointer may be useful in program development, but in most cases, we need to know the details of the object inside, so the OC provides description method to achieve this.
@interface Chinese()
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Chinese
- (NSString *)description {
return [NSString stringWithFormat:@"<%@ : %p, %@>",[self class],self,
@{
@"firstName":_firstName,
@"lastName" :_lastName,
@"age": @(_age)
}];
}
@end
Copy the code
It is better to use the dictionary to output each attribute or member variable memory. If you need to add or delete attributes later, you can directly modify the dictionary key-value pairs. In addition, the debugDescription method is used to print object information using the Po command on the console. If the description method has been implemented, the debugDescription method can not be overwritten, because the system will call the description method by default.
๐ฆ๐ฎ Rule 18: Use immutable objects whenever possible
- Try to create immutable objects
- If a property is only available for internal object modification, extend it from a readonly property to a readwrite property in the class-Continuation category
- Instead of exposing collections of mutable objects as properties, provide methods to modify mutable collections in objects
When developing custom classes, it is essential to encapsulate functionality by making properties declared in.h immutable, read-only properties whose contents can only be changed by specific methods. For example, the Student class we declared earlier:
// .h @interface Student : Chinese @property (nonatomic, copy, readonly) NSString *school; @property (nonatomic, strong, readonly) NSArray *homework; - (void)addHomeworkMethod:(NSString *)homework; - (void)removeHomeworkMethod:(NSString *)homework; @end // .m @interface Student() @property (nonatomic, copy) NSString *school; @end @implementation Student { NSMutableArray *p_homework; } - (void)addHomeworkMethod:(NSString *)homework { [p_homework addObject:homework]; } - (void)removeHomeworkMethod:(NSString *)homework { [p_homework removeObject:homework]; } - (instancetype)initWithSchool:(NSString *)school homework:(NSArray *)homework { if (self = [self init]) { self.school = school; p_homework = homework.mutableCopy; } return self; } @endCopy the code
In this way, the external definition can only update the attributes in the object through fixed methods, which facilitates the encapsulation of functions and reduces the probability of bugs. The use of immutable objects also enhances the efficiency of the program.
๐ฆ๐ฌ Article 19: Use clear and coordinated naming
- Follow the standard Objective-C naming conventions to create interfaces that are easier for developers to understand
- The method name should be concise and read from left to right like an everyday sentence
- Do not use abbreviated type names in method names
- The first thing you need to do when naming a method is to make sure that the style matches your own code or the framework you want to integrate
That is to say, when naming properties, member variables, methods, protocols, etc., you create them by their names.
๐ฆ๐น Rule 20: Prefix private method names
- Prefix the names of private methods so that they can be easily distinguished from public methods
- Do not prefix private methods with an underscore, as this is reserved for Apple
For a written class, if you change the name of a public method, you need to change the name of the method outside of the class, which is more troublesome. Private methods implemented inside the class do not have this problem, so prefix the private method to better distinguish between the two. Facilitate later development. Depending on the developer’s development habits, prefixes starting with underscores are not recommended because they are exclusive to the Apple Dad. It is the author’s custom to prefix private methods with p_, for example:
// this is a private method - (id)p_playAirplaneMethod {id xx = @"**"; return xx; }Copy the code
๐ฆ๐ฝ Rule 21: Understand Objective-C error types
- Exceptions should be used only when there is a serious error that could crash the entire application
- In less serious cases, you can either assign a “delegate method” to handle the error, or you can put the error message in
NSError
Object, returned to the caller via an output parameter
You can customize an error type model in a project:
#import <Foundation/ foundation. h> typedef NS_ENUM(NSUInteger, XWErrorCode) {XWErrorCodeUnknow = -1, // Unknown error XWErrorCodeTypeError = 100,// type error XWErrorCodeNullString = 101,// Empty string XWErrorCodeBadInput = 500,// wrong input}; extern NSString * const XWErrorDomain; @interface XWError : NSError + (instancetype)errorCode:(XWErrorCode)errorCode userInfo:(NSDictionary *)userInfo; @end // .m #import "XWError.h" @implementation XWError NSString * const XWErrorDomain = @"XWErrorDomain"; + (instancetype)errorCode:(XWErrorCode)errorCode userInfo:(NSDictionary *)userInfo { XWError *error = [[XWError alloc] initWithDomain:XWErrorDomain code:errorCode userInfo:userInfo]; return error; } @endCopy the code
Custom error messages can be passed in appropriate callbacks to the debugger.
๐ฆ๐บ Article 22: UnderstandingNSCopying
agreement
- If you want to make the object you write have copy function, it needs to be implemented
NSCopying
agreement - If the custom object is divided into mutable version and immutable version. So it has to be simultaneous
NSCopying
Deal with theNSMutableCopying
agreement - When assigning an object, you need to decide whether to use a shallow copy or a deep copy. Generally, you should perform a shallow copy as much as possible
- If you are writing objects that require deep copies, consider adding a method that performs deep copies exclusively
I want my Student class to have copy properties, so I need to implement the NSCopying protocol, which has only one – (id)copyWithZone:(nullable NSZone *)zone method. As follows:
@interface Student() <NSCopying> @end @implementation Student { NSMutableArray *p_homework; } #pragma mark - NSCopying - (id)copyWithZone:(nullable NSZone *)zone { Student *stuCopy = [[Student allocWithZone:zone] initWithFirstName:self.firstName lastName:self.lastName age:self.age homework:p_homework.copy]; return stuCopy; }Copy the code
Calling Student’s copy method produces a different Student object with the same content
Student * stu = [Student studentWithFirstName: @ "little geek" lastName: @ "learn wei" age: 6 homework: @ [@ "violin," @ "basketball"]]. Student *stu2 = [stu copy];Copy the code
NSMutableCopying :(id)mutableCopyWithZone:(Nullable NSZone *)zone
#pragma mark - NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
Student *stuMtableCopy = [[Student allocWithZone:zone] initWithFirstName:self.firstName lastName:self.lastName.mutableCopy age:self.age homework:p_homework.copy];
return stuMtableCopy;
}
Copy the code
Add a list of Array and Dictionary types that point to shallow and deep copies, respectively:
Array
First declare two arrays:
NSArray *array = @[@1,@2];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
Copy the code
Shallow and deep copies are executed. The following output is displayed:
2018-08-01 11:46:32.255187+0800 XWInterviewDemos[80249:5837261] [array copy]:__NSArrayI
2018-08-01 11:46:32.255337+0800 XWInterviewDemos[80249:5837261] [array mutableCopy]:__NSArrayM
2018-08-01 11:46:32.255431+0800 XWInterviewDemos[80249:5837261] [mutableArray copy]:__NSArrayI
2018-08-01 11:46:32.255516+0800 XWInterviewDemos[80249:5837261] [mutableArray mutableCopy]:__NSArrayM
Copy the code
Where __NSArrayI is an immutable array and __NSArrayM is a mutable array. Conclusion:
The original class | operation | Copy the result |
---|---|---|
NSArray | Shallow copy | Immutable (__NSArrayI) |
NSArray | Deep copy | Variable (__NSArrayM) |
NSMutableArray | Deep copy | Immutable (__NSArrayI) |
NSMutableArray | Deep copy | Variable (__NSArrayM) |
Dictionary
First declare two dictionaries:
NSDictionary *dictionary = @{@"key":@"value"};
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary];
Copy the code
Shallow and deep copies are executed. The following output is displayed:
2018-08-01 11:57:20.810019+0800 XWInterviewDemos[80385:5844478] [Dictionary Copy]:__NSSingleEntryDictionaryI 2018-08-01 11:57:20.810162+0800 XWInterviewDemos[80385:5844478] :__NSDictionaryM 2018-08-01 11:57:20.810277+0800 XWInterviewDemos[80385:5844478] [mutableDictionary Copy]:__NSFrozenDictionaryM 2018-08-01 11:57:20. 810374 + 0800 XWInterviewDemos [80385:5844478] [mutableDictionary mutableCopy] : __NSDictionaryMCopy the code
__NSSingleEntryDictionaryI and __NSFrozenDictionaryM are immutable dictionaries. __NSDictionaryM is a variable dictionary.
The original class | operation | Copy the result |
---|---|---|
NSDictionary | Shallow copy | Immutable (__NSSingleEntryDictionaryI) |
NSDictionary | Deep copy | Mutable (__NSDictionaryM) |
NSMutableDictionary | Deep copy | Immutable (__NSFrozenDictionaryM) |
NSMutableDictionary | Deep copy | Mutable (__NSDictionaryM) |
Chapter 4: Protocol and Classification
๐ฒ๐ด Article 23: Communication between objects via delegate and data source protocols
- The delegate pattern provides a set of interfaces for objects to communicate related events to other objects
- The interfaces that delegate objects should support are defined as protocols, in which events that might need to be handled are defined as methods
- The delegate pattern can be used when an object needs to fetch data from another object. In this case, the pattern is also called “data source protocol.”
- If necessary, a structure with a displacement segment can be implemented to cache information about whether the delegate object can respond to protocol methods
Delegate and data source protocols are used a lot when we use UITableView. We can follow its design pattern when we develop and get the data we need from the data source. Callback the event after the operation is performed through the broker; And weakly reference its proxy object.
@class Chinese; @protocol ChineseDelegate <NSObject> @optional - (void)chinese:(Chinese *)chinese run:(double)kilometre; - (void)chinese:(Chinese *)chinese didReceiveData:(NSData *)data; - (void)chinese:(Chinese *)chinese didReceiveError:(NSError *)error; @end@interface Chinese: NSObject - weak reference @property (nonatomic, weak) id<ChineseDelegate> delegate; @endCopy the code
When the object runs, it is called back to the delegate via the proxy method:
- (void)run {double runDistance = 0.0; if (self.delegate && [self respondsToSelector:@selector(chinese:run:)]) { [self.delegate chinese:self run:runDistance]; }}Copy the code
If this method is called hundreds or thousands of times per minute, executing respondsToSelector every time will have some impact on performance, because it is a repeat judgment except the first one, so we can cache whether or not we can respond to this method! As an example shows:
#import "chinese.h" @interface Chinese() {struct {unsigned int didReceiveData: 1; DidReceiveData unsigned int didReceiveError: 1; DidReceiveError unsigned int didRun: 1; Run}_chineseDelegateFlags; } @end @implementation Chinese; SetDelegate :(id<ChineseDelegate>)delegate {_delegate = delegate; _chineseDelegateFlags.didRun = [delegate respondsToSelector:@selector(chinese:run:)]; _chineseDelegateFlags.didReceiveData = [delegate respondsToSelector:@selector(chinese:didReceiveData:)]; _chineseDelegateFlags.didReceiveError = [delegate respondsToSelector:@selector(chinese:didReceiveError:)]; } // run {double runDistance = 0.0; // Run {double runDistance = 0.0; if (_chineseDelegateFlags.didRun) { [self.delegate chinese:self run:runDistance]; } ///if (self.delegate && [self respondsToSelector:@selector(chinese:run:)]) { /// [self.delegate chinese:self run:runDistance]; / / /}}Copy the code
If the agent method can be called back multiple times, this optimization will greatly improve the efficiency of the application!
๐ง๐ง article 24: Divide the implementation code of a class into manageable categories
- Use a classification mechanism to divide the implementation code of a class into manageable chunks
- Name methods that should be considered “private.
Private
To hide implementation details
In the development of a class, all the code is generally put together, even if it is high aggregation and low coupling code, if the program is getting bigger and bigger, it will inevitably feel not elegant. The elegant way is to separate the implementation into different categories according to the function, introduce its classification into the main class, and directly call the method implemented in the classification. It’s also easy to manage. Based on the category name, you can quickly locate the functional area where the code belongs, facilitating expansion and maintenance. In addition, you can create a Private class corresponding to the name of the developed class to hold some Private methods. These methods do not need to be exposed to the outside world, but maintained by the developers themselves.
๐ต๐ฌ rule 25: Always prefix the class names of third party classes
- When adding categories to third-party classes, always give their names your own prefix
- When adding classes to third-party classes, always prefix the method names with your own special prefix
Methods implemented in a class are eventually loaded into the list of methods of the class at compile time. If methods with the same name exist, the post-compiled class overwrites the pre-compiled one, so it is necessary to prefix methods in the class.
๐ง๐ธ Article 26: Do not declare attributes in a classification
- All attributes used to encapsulate data are defined in the master interface
- In the class-Continuation class, you can define access methods, but try not to define properties
The original declaration of a property in a classification is simply a declaration that automatically generates getter and setter methods for that property. Getter and setter methods for member variables and corresponding properties are not generated, although implementations of getter and setter methods for properties can be added to classes using the Runtime’s associative object approach, allowing classes to define properties. But the essence of classification is to extend the functionality of classes, not to encapsulate data. Use the above way need to write a lot of similar code, and prone to error in memory management, change the type of attributes need to change the related type of the associated object, not conducive to maintenance, the code is not elegant!
๐ต๐ฐ Article 27: Hide implementation details using a “class-CONTINUATION classification.
- Add instance variables to a class using the class-Continuation classification
- If a property is declared “read only” in the main interface and the class internally needs to modify it with a setup method, extend it to “read/write” in the class-Continuation class
- Declare the prototype of a private method in the class-Continuation class
- If you want to keep the protocol that your class follows unknown, you can declare it in the class-Continuation class
Such as:
@interface Chinese: nsobject@property (nonatomic, weak) id<ChineseDelegate> delegate; @property (nonatomic, copy, readonly) NSString *firstName; @property (nonatomic, copy, readonly) NSString *lastName; @end //.m internal declaration is read and write. @interface Chinese() <NSCopying> {struct {unsigned int didReceiveData: 1; DidReceiveData unsigned int didReceiveError: 1; DidReceiveError unsigned int didRun: 1; Run}_chineseDelegateFlags; NSString *p_girlFriend; } @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property (nonatomic, assign) NSUInteger age; @endCopy the code
Or declare C++ objects in the class-continuation class, so that.m is compiled to.mm files and exposed as a pure OC interface. This strategy is often used in the Foundation framework.
๐ต๐พ clause 28: Provide anonymous objects by protocol
- The protocol can provide anonymous typing to some extent. The specific object type can be diluted down to an ID type that complies with a protocol that specifies the methods the object should implement
- Use anonymous objects to hide type names (or class names)
- If the specific type is not important but the object’s ability to respond to methods (defined in the protocol), then anonymous objects can be used
Chapter 5: Memory Management
๐ต๐ธ Rule 29: Understand reference counting
- The reference counting mechanism manages memory by incrementing and decrementing counters. After the object is created, its retention technique is at least 1. If the retention count is positive, the object continues to live. When the retention count drops to zero, the object is enchanted.
- During the object’s life, the rest of the objects retain or release the object by reference. The retention and release operations increase and decrease the retention count, respectively
Reference counting is represented by a graph:
Figure 1.3 from Objective-C Advanced Programming +iOS and OS+X Multithreading and Memory Management
Now that you’ve almost understood reference counting, OK, end of this rule…
In addition, the concept of auto-release pooling is added. Auto-release pooling allows the life cycle of an object to cross the “method call boundary” and survive to the next event cycle of the Runloop.
๐ง๐ญ clause 30: Simplified reference counting with ARC
- With ARC, programmers don’t have to worry about memory management. Using ARC saves a lot of boilerplate code in classes
- ARC manages the life cycle of objects basically by inserting “save” and “release” operations where appropriate. In an ARC environment, the memory management semantics of variables can be specified by modifiers, which would otherwise require manual “save” and “release” operations
- The memory-management semantics of objects returned by methods are always represented by method names. ARC has identified this as a rule that developers must follow
- ARC is only responsible for managing memory for Objective-C objects. In particular, CoreFoundation objects are not managed by ARC and developers must call CFRetain/CFRelease when appropriate
ARC is set in a safe way by keeping the new value, releasing the old value, and finally setting the instance variable, where you can change the semantics of local and instance variables with the following modifiers:
__unsafe_unretained the value of __strong. This may not be safe. The __weak may have been reclaimed by the time the variable is used again, but it is safe to use the variable. Variables are also automatically emptied – to avoid the use of this special modifier when cyclic reference __autoreleasing passes objects “by reference” to methods. This value is automatically released when the method returns
๐ต๐ฆ Article 31: atdealloc
Method to release only references and unlisten
- in
dealloc
The only thing that should be done is to release references to other objects and unsubscribe from the KVO or subscriptionNSNotificationCenter
Don’t do anything else until you’re told - If the object holds system resources such as file descriptors, you should write a special method to interpret such resources. Such classes have a convention with their users that they must be called when they run out of resources
close
methods - Methods that execute asynchronous tasks should not be in
dealloc
In the call; Methods that can only be executed in normal state should also not be executed indealloc
, because the object is already in the collection state
The dealloc method is called to release the object. If the member variable of the object is used, it may already be released. If the asynchronous callback contains self, the program will crash.
- (void)dealloc {// Remove notification [[NSNotificationCenter defaultCenter] removeObserver:self]; // release resources that need to be released manually //CFRelease(coreFoundationObject); }Copy the code
And even though the object is freed, in very rare cases the dealloc method is not called, Will call at the termination of the program is in the application of delegate – (void) applicationWillTerminate: (UIApplication *) application method, if must clean up some objects, Can be handled in this method.
๐ง๐ท rule 32: Be aware of memory management issues when writing exception safe code
- When catching exceptions, it is important to clean up the objects created in the try block
- By default, ARC does not generate the cleanup code needed to safely handle exceptions, which can be produced when the compiler flag is turned on, but can result in a larger application and less efficient execution
๐ง๐พ Article 33: Avoid retaining rings with weak references
- Set some references to
weak
, can avoid “retention ring” weak
References may or may not be automatically emptied. Automatic emptying is a new feature introduced with ARC and implemented by run-time systems. On weak references with auto-cleaning, the data can be read at will because they do not point to objects that have already been reclaimed
If two objects refer to each other, they form a reserved ring (circular reference), as shown in the figure below:
Reserved rings cause memory leaks, and all objects in the reserved rings cannot be released due to the mutual holding of objects. The best way to avoid retaining a ring is by weak references, which break the ring by declaring a “non-owning relationship”. This relationship can be implemented using weak and unsafe_unretained. But unsafe_unretained does not. Across freed objects, it still points to the freed block of memory. As shown in figure:
@property (nonatomic, weak) id<ChineseDelegate> delegate; __weak typeof(self) weakSelf = self; [NSTimer xw_timerTimeInterval:1.0 block:^{[weakSelf timerMethod];} repeats:YES];Copy the code
๐ง๐ฒ Clause 34: Reduce peak memory with automatic release of pool blocks
- The auto-release pool is placed on the stack and the object is received
autorelease
After the message, the system drops it into the topmost pool. - Using automatic pooling properly can reduce the application’s memory peak
@autoreleasepool
This new writing allows for the creation of lighter, automatic release tanks
Both the main thread and threads in the GCD mechanism have automatic release pools by default, which the programmer does not need to create manually, and which are automatically emptied by the system when the runloop is executed next time. If n objects need to be created in a large loop body, use auto Free pool block to reduce the memory peak, as shown in the example:
Unoptimized ways:
- (void)testFor1 { NSMutableArray *arrayM = [NSMutableArray array]; for (int i = 0; i < 100000; i++) { NSString *str = [NSString stringWithFormat:@"%d",i]; [arrayM addObject:str]; NSLog(@"%@",str); }}Copy the code
Memory usage at this time:
Using the “auto Release pool block” optimization:
- (void)testFor2 { NSMutableArray *arrayM = [NSMutableArray array]; for (int i = 0; i < 100000; i++) { @autoreleasepool { NSString *str = [NSString stringWithFormat:@"%d",i]; [arrayM addObject:str]; NSLog(@"%@",str); }}}Copy the code
Optimized memory usage:
Optimized CPU usage:
Obviously, according to Xcode, ** : does not work. In principle, this rule can reduce the peak memory usage, but the actual situation is not much different between the two, whether it can be optimized needs to be observed in the future.
๐ง๐ฌ Clause 35: Debug memory management problems with zombie objects
- When the system recycles an object, it can convert it to a zombie object instead of actually recycling it. By environment variables
NSZombieEnabled
This function can be enabled - The system will modify the object
isa
Pointer to a special zombie class, making the object a zombie. The zombie class responds to all selectors by printing a message containing the contents of the message and its recipient, and then terminating the application
Check Zombie Objects in Xcode to enable Zombie object detection. Sending a message to a Zombie object will print the relevant information on the console:
๐ฒ๐ต 36 Do not useretainCount
- Retention counts for objects may seem useful, but they are not, because an “absolute retention count” at any given point in time does not capture the full picture of an object’s lifetime
- With the introduction of ARC,
retainCount
Method is officially deprecated, and calling it in ARC will result in an error from the compiler.
RetainCount itself was not used in the ARC era, and several of the situations described in the book only occur in MRC programming environments. For example, objects are released when retainCount may not be zero, so the retainCount reference count may never be zero. This is caused by the release behavior of the system optimization object.
After the end of the first five 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…