## 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: Implementationdescriptionmethods

  • implementationdescriptionMethod returns a meaningful string describing the instance
  • You want to print a more detailed object description during debugging. Should be implementeddebugDescriptionmethods

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 inNSErrorObject, 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: UnderstandingNSCopyingagreement

  • If you want to make the object you write have copy function, it needs to be implementedNSCopyingagreement
  • If the custom object is divided into mutable version and immutable version. So it has to be simultaneousNSCopyingDeal with theNSMutableCopyingagreement
  • 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.PrivateTo 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: atdeallocMethod to release only references and unlisten

  • indeallocThe only thing that should be done is to release references to other objects and unsubscribe from the KVO or subscriptionNSNotificationCenterDon’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 resourcesclosemethods
  • Methods that execute asynchronous tasks should not be indeallocIn 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 toweak, can avoid “retention ring”
  • weakReferences 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 receivedautoreleaseAfter the message, the system drops it into the topmost pool.
  • Using automatic pooling properly can reduce the application’s memory peak
  • @autoreleasepoolThis 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 variablesNSZombieEnabledThis function can be enabled
  • The system will modify the objectisaPointer 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,retainCountMethod 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…