- Unrecognized selector crash
- KVO crash :(KVO observer dealloc is still registered with crash caused by KVO, add KVO to add observer repeatedly or remove observer repeatedly)
- NSNotification crash:(when a notification is added to an object, if the dealloc still holds the notification)
- NSTimer crash:(invalidate the timer at an appropriate time; otherwise, the target cannot be released due to the strong reference of the timer to target, resulting in memory leaks and even crash when the scheduled task is triggered)
- Container type crash:(array, dictionary, common overbounds, insert, nil)
- Wild pointer type of crash
- Non-mainthread UI type :(brushing UI in non-mainthread will cause the app to crash)
Problems and solutions
One: Unrecognized Selector Type Crash protection
Crash of unrecognized selector type occupies a large part in many crash types of app, which is usually caused by an object calling a method that does not belong to its method.
2. KVO Crash Protection (NSNotification)
Causes of kVO crash: There are roughly two kinds
The first type: the observer of KVO still registers the crash caused by KVO when dealloc
Second: adding KVO repeatedly adding observers or repeatedly removing observers (KVO registered observers and removed observers do not match) caused by crash
An object to be observed has several observers, each of which has several keypath.
If the number of observer and keypath is too large, it is easy to lose the whole KVO relationship of the observed object, resulting in the dealloc of the observed object,
There are still some remaining relationships that have not been deregistered, and there may be a mismatch between the KVO registrant and the removed observer
Especially in the case of multi-threading, KVO can repeatedly add or remove observers, which is often hidden and difficult to detect from the code level
KVO Crash protection solution
How to manage a chaotic KVO relationship:
You can have an observer hold a KVO delegate, and all operations related to KVO are managed through a delegate
Create a MAP table to maintain the entire relationship of KVO, as shown below:
There are two advantages to this:
1: If KVO repeatedly adds or removes observers (KVO registrants do not match), delegate can directly prevent these abnormal actions.
2: Before the observed object dealloc, the delegate can automatically cancel all the KVO relations related to the observed object, avoiding the crash caused by the FACT that the observed object dealloc is still registered with KVO
3: NSNotification Crash protection (NSNotification)
Causes of NSNotification crash:
When a notification is added to an object, if the dealloc still holds the Notification, an NSNotification crash will occur
A crash of the NSNotification type occurs when a programmer inadvertently writes code, forgetting to remove the object dealloc after adding an object as an observer to the NSNotificationCenter.
Fortunately, apple has specifically addressed this situation since iOS9, so Notification crashes don’t happen after iOS9 even if developers don’t remove the observer.
However, for users before iOS9, we still need to do the NSNotification Crash protection.
NSNotification Crash protection principle is very simple, using method Swizzling hook NSObject dealloc function, before the object really dealloc call
[[NSNotificationCenter defaultCenter] removeObserver:self].
Note that not all objects need to do this. If an object has never been added as an Observer by NSNotificationCenter, calling removeObserver before its dealloc is unnecessary
4: NSTimer crash prevention (NSTimer)
4.1 Causes of NSTimer Crash
In the process of program development, people will often use regular tasks, but use NSTimer scheduledTimerWithTimeInterval: target: the selector:
userInfo:repeats: There is a problem when an interface performs repetitive scheduled tasks: NSTimer strongly references the target instance. Therefore, you need to invalidate the timer at an appropriate time. Otherwise, the target cannot be released due to the timer strongly references the target, resulting in memory leaks and even crash when the scheduled task is triggered. The representation of a crash depends on the selector performed by the specific target.
At the same time, if NSTimer performs a task infinitely repeatedly, it may cause target’s selector to be repeatedly called and in invalid state, which is an unnecessary waste of CPU, memory and other performance aspects of app. Therefore, it is necessary to design a scheme that can effectively prevent the abuse of NSTimer.
4.2 NSTimer Crash Prevention Schemes
As can be seen from the above analysis, the main cause of NSTimer’s problems is that it does not have a proper time to invalidate, as well as memory leakage caused by NSTimer’s strong reference to target.
The key to solving the NSTimer problem lies in the following two points:
>1. Whether NSTimer can not strongly reference its target
> < span style = “box-sizing: border-box; color: RGB (51, 51, 51); line-height: 20px; font-size: 13px! Important; word-break: inherit! Important;
On the first issue, target’s strong reference problem. It can be solved by the following scheme:
Add a layer of stubTarget between NSTimer and Target. StubTarget acts as a bridge layer for communication between NSTimer and Target.
At the same time, stubTarget strongly references stubTarget, while stubTarget weakly references Target. Thus, the relationship between Target and NSTimer is weak reference, which means target can be released freely, thus solving the problem of circular reference.
As mentioned above, stubTarget is responsible for the communication between NSTimer and Target. The specific implementation process is further divided into two steps:
In step 1. Swizzle NSTimer scheduledTimerWithTimeInterval: target: the selector: the userInfo: repeats: In the new method, stubTarget object is dynamically created, stubTarget object weak reference holds the original target, Selector, timer, targetClass and other properties. Then distribute the original target on stubTarget, and the selector callback is stubTarget’s fireProxyTimer
Step 2. Use stubTarget’s fireProxyTimer: to process and distribute the callback function selector
When NSTimer’s callback function fireProxyTimer: When executed, it will automatically determine whether the original target has been released. If released, it means that the NSTimer is invalid. In this case, if you continue to call the original target’s selector, it is likely to cause crash, and it is not necessary. Therefore, it is necessary to invalidate the NSTimer and report the error data. This allows NSTimer to automatically invalidate when appropriate
Five: Container Type Crash prevention (Container)
5.1 Cause of Container Crash
A crash of the Container type refers to a crash of a Container class. Common examples are NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache. Some common error operations such as crossing boundaries and inserting nil will lead to such crashes. Because the causes are relatively simple, I will not expand to describe.
Although this kind of crash is easy to detect, the probability of app crash is still quite high, so it is necessary to protect it
5.2 Container Crash Prevention Scheme
The Container Crash protection scheme is also simple. It applies to NSArray/NSMutableArray/NSDictionary/NSMutableDictionary /
Swizzling NSCache’s common crash-causing APIS with method swizzling, and then adding some constraints and judgments to Swizzle’s new method,
To make these apis secure, I won’t go into detail here.
6: wild pointer type crash
6.1: Causes of wild Pointers
In all crashes of App, Crash caused by access to wild pointer accounts for a large part. The manifestation of wild pointer Type Crash is: Exception Type:SIGSEGV, Exception Codes: SEGV_ACCERR
Solving the crash caused by wild Pointers is often a tricky thing. On the one hand, the scene that caused the crash is not easy to reproduce, and on the other hand, the information of console after the crash can provide limited help.
XCode itself provides a Zombie mechanism to alert classes that have wild Pointers when they occur.
Thus solve the problem of wild pointer in development stage. However, there is still no good way to locate the wild pointer problem on the line.
Therefore, because the occurrence probability of wild Pointers is high and it is difficult to locate the problem, it is necessary to make a special layer of protection measures for wild Pointers
6.2 Protection Scheme for Wild Pointer Crash
In fact, the methods proposed online are not perfect, and quite complex. Most of the time on the web, you do a tag when you initialize your class init, and then you do a tag again with dealloc, and then you do a tag twice to see if there’s any memory, and for UIView, UIImageview, it’s a lot of memory consumption, so it’s not a perfect solution
Here’s a little trick:
Do you know how to tell if an instance’s memory has been freed? This method, which I discovered and tested personally, is very effective for determining whether the memory of the current pointer is still there
if(! malloc_zone_from_ptr((__bridge const void *)(strongself)))return;
But it doesn’t solve the whole problem
Because we don’t know when to call a class function and when to call a property, right
Crash protection (UI not on Main Thread)
The current preliminary processing scheme is the following three methods of swizzle UIView class:
-(void)setNeedsLayout;
-(void)setNeedsDisplay;
-(void)setNeedsDisplayInRect:(CGRect)rect;
Dispatch_async (dispatch_get_main_queue(), ^{// call the original method});
To transfer the corresponding UI brushing operation to the main thread, while counting error messages.
However, after the implementation, it is found that these three methods cannot completely cover all the UI brush operations related to UIView. However, if all the UI brush methods to UIView are counted and swizzle, it feels a little awkward and inefficient. However, the proportion of such crashes is not high, and our important purpose is to reduce the crash rate again and again. It is not completely eliminated, and we cannot completely eliminate it at present. Only when we master the underlying principle, can we deal with problems flexibly!