preface

As a recent graduate engaged in iOS development, at the beginning, my colleagues and mentors told me to pay attention to abnormal situations when writing code. The bottom line is that I should not write any code that might cause a crash. In fact, there are tools in the project to monitor crashes, and reviews are very strict, so basically any code that might cause a crash is fixed before it goes live.

However, just a few days ago, a large area of the client client flickers back when opened, which has a very serious impact. Later, it was found that the SDK of other departments introduced did not conduct type judgment, resulting in the crash. Perhaps it is the carelessness of the developers, but I think it is more because they have not formed a habit in daily life. They have not taken into account that for an application with tens of millions of users, even if the probability of crash is one in ten thousand, thousands of users will crash, which cannot be tolerated in the competitive Internet market.

I also have no excessive attention, because I always feel that in theory should be impossible to crash, but the actual scene is too much, not may not be absolutely impossible in theory, as a rigorous enough developers, must hold their own bottom line, not just to know what situation will cause the collapse, but to develop a programming habits, so specially analyzes the situation of various kinds of collapse.

An array

NSArray *firstNames = @[@"Roy"The @"Mike"The @"Jordan"]; NSString *name = firstNames[3]; **** Terminating app due to uncaught exception'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 2]'The range of the current array is 0.. Note: In addition to arrays that may be out of bounds, strings can also be out of bounds, such as substringWithRange: messages that pass too large a range will crashCopy the code

Literal arrays and dictionaries insert nil values

An array of

NSString *name;
NSArray *firstNames = @[@"Roy"The @"Mike"The @"Jordan", name]; **** Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3]'* ***** analysis: it is clear from the crash message that nil was inserted when the dictionary was initialized. In fact, literal syntax is a syntactic sugar, essentially creating an array and then adding all the objects in square brackets to the array. The literal syntax makes the code more concise and allows you to detect errors in time, but the array you end up creating is immutableCopy the code

The dictionary

NSNumber *jordanAge;
NSDictionary *ages = @{@"Roy": @ @ 22"Mike": @ @ 24"Jordan":jordanAge}; **** Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[2]'* ***** 解 决 : insert into nil causes insert to crash if key is nilCopy the code

Unrecognized Selector

id person = @"person";
[person objectForKey:@"name"]; **** Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '-[__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x1000010e8'* ***** analysis: The person object could not execute the objectForKey: message, so it crashed When coding in Objective-C, we'll often use the ID type to make it easier to declare variables, but to make sure it responds before executing a message, use respondsToSelector: to check. The most common scenario is to invoke proxy methods. Even if a proxy object is specified, there is no guarantee that the proxy implements the corresponding method (there are alternative implementation methods in the protocol).Copy the code

NaN collapse

float number = NAN;
NSDictionary *dict = @{@"value": @(number)}; NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingSortedKeys error:nil]; **** Terminating app due to uncaught exception'NSInvalidArgumentException', reason: 'Invalid number value (NaN) in JSON write'* ***** Check whether the dict object can be converted to JSON data: BOOL isValidJSONObject = [NSJSONSerialization isValidJSONObject:dict]; The result of isValidJSONObject is NO, which means that dict objects cannot be converted to JSON data. NaN types cannot be used in JSON objects. Not only NaN types but also + INF types can be generated when performing malfunctioning math operations, which do not crash directly but may crash when they are used for other operations. Nan and INF types can be determined using the isnan(x) and isINF (x) methodsCopy the code

The string is empty when rich text is initialized

NSString *text; NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text]; **** Terminating app due to uncaught exception'NSInvalidArgumentException', reason: 'NSConcreteAttributedString initWithString:: nil value'NSMutableString: NSMutableString: NSMutableString: NSMutableString: NSMutableString: NSMutableString: NSMutableString: NSMutableStringCopy the code

Create an unregistered UITableViewCell

UITableViewCell *cell = [tableView    
dequeueReusableCellWithIdentifier:@"reuseIdentifier" 
forIndexPath: IndexPath] / / collapseCopy the code

A crash caused by a server side problem

In one case, the server passes data to the client, which parses it into a model object and then inserts the values from the model into an array or dictionary constructed by the literal syntax. If something goes wrong on the server side and the client doesn’t have protection, which is 99 percent of the time impossible, a lot of people (including me) don’t bother to write extra defensive code. I just gave you an example. We usually agree on data formats and other details with the server side, and most of the time we do some protection, but what I really want to emphasize is that it is better for the client not to crash than for the client to rely on the server side not to crash, as much as possible from the outside world

Other collapse

  1. After iOS 9.0, NSNotificationCenter does not send messages to an observer of dealloc, so it is not necessary to remove notifications every time if the app has a lower version than 9.0, but it is necessary to remove notifications if you need to support lower versions, otherwise it will crash
  2. KVO not removing listeners will cause a crash, so KVO must be added and removed in pairs
  3. Memory leaks, most notably cyclic references to proxy and proxied objects

conclusion

These are all crashes I’ve encountered, but there’s a lot more I don’t know about, given the complexity of technology. There may be tools we can use to check for or avoid crashes, but I would like to emphasize that you should be more careful with your code and more responsible with the project you are responsible for

CrashDemos link to the above code: github.com/iroyzhang/i…