Considerations when using writeToFile in IOS development

  • 2021-11-24 03:06:36
  • OfStack

There will always be some pits waiting for you in front

Let's first look at part of the json data returned in the background under 1, and then analyze the problem later. Look carefully at the fields userId and userCode under 1, and don't look at others


"list": [{
   "classId": 5000285,
   "className": " Attendance ( A ) class ",
   "schoolId": 50011,
   "schoolName": " Xingxing Bureau Test Middle School 25",
   "classLeaderUserId": 2000163,
   "parentList": [{
    "userId": 2000790,
    "userName": "zhaomin",
    "gender": "0",
    "mobile": "15071362222",
    "email": "",
    "areaCode": "440105",
    "avatarUrl": "",
    "userCode": "2000790",
    "id": 1542,
    "roleType": 2,
    "nickName": "zhaomin"
   }, {
    "userId": 2000846,
    "userName": " Liu Xuande ",
    "gender": "1",
    "mobile": "18825113388",
    "email": "",
    "areaCode": "440105",
    "avatarUrl": "",
    "userCode": "2000846",
    "id": 1631,
    "roleType": 2,
    "nickName": " Liu Xuande "
   }],

Problem background

This problem is when I integrated the ring letter IM, Due to the need to deal with the user avatar and nickname issues, so will contact the avatar url and user nickname do a local cache, the cache is simply written to plist file to deal with. The reason for using plist, because it is simple and convenient, and can meet the development, so there is no other cache.

The problem is to write to the plist file.

Encounter problems

After getting the contact data returned from the background, I will filter the returned list, only screening out the required user name and avatar address. In the return field, userId and userCode look like one, but in fact, it is resolved that the former is NSNuber type and the latter is NSString type. At that time, I only remembered to use Sqlite statement directly in the background, and userCode = userId, without considering the type problem at all. I thought, in this case, it is better to use userId directly, so I put '[userNameDict setObject: dict [@ "userName"] forKey: dict [@ "userCode]]; 'Replaced with' [userNameDict setObject: dict [@ "userName"] forKey: dict [@ "userId"]]; '. The problem is that one field is changed.

At first, no problem was found, because the value of userCode field was used as key of dictionary in the previous 1, so it was cached locally. Until one day, when reinstalling App test, it was discovered that the avatar and nickname of chat interface were not displayed, and finally it was thought that the value of one field was changed.

However, after replacing it with userId, The printed dictionary is one model and one sample, That is, writeToFile always fails when writing plist. Later, comparing two dictionaries with isEqualToDictionary method is different. The problem is really hard to find. Of course, the solution is to switch to the original userCode. However, I don't want to solve the problem by avoiding it, so I checked the reason and even compared all the key and value values, and found that it was still 1. Finally, I felt that I couldn't find the problem, so I looked at the returned data, and found that the types of Value values corresponding to the fields userId and userCode were not 1. This led to a conclusion

If it is a mutable dictionary, writeToFile will fail if key uses key of type NSNumber when using the 'setObject: forKey' method.

As for why this is the case, it needs to be further studied. Of course, if someone has encountered it and found out the reasons, they can also reply to it, learn from each other and make progress together.

Attach the code at that time


- (void)saveContactListDict:(id)list {
 NSMutableArray *contactListArray = [NSMutableArray array];
 for (NSDictionary *dict in list) {
  for (NSString *key in dict) {
   if ([dict[key] isKindOfClass:[NSArray class]]) {
    [contactListArray addObjectsFromArray:dict[key]];
   }
  }
 }
 NSMutableDictionary *userNameDict = [NSMutableDictionary dictionary];
 NSMutableDictionary *avatarurlDict = [NSMutableDictionary dictionary];
 NSMutableDictionary *avatarurlAndNameDict = [NSMutableDictionary dictionary];
 for (NSDictionary *dict in contactListArray) {
  if (dict[@"userId"] == nil) {
   return;
  }
  [userNameDict setObject:dict[@"userName"] forKey:dict[@"userId"]];
  NSString *url =dict[@"avatarUrl"];
  NSString *avatarUrl = [CPUtil getThumUrl:url size:CGSizeMake(200, 200)];
  [avatarurlDict setObject:avatarUrl forKey:dict[@"userId"]];
  if (dict[@"userName"] == nil) {
   return;
  }
  [avatarurlAndNameDict setObject:avatarUrl forKey:dict[@"userName"]];
 }
 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
 NSString *userNameDictPath = [path stringByAppendingPathComponent:@"userNameDict.plist"];
 NSString *avatarurlDictPath = [path stringByAppendingPathComponent:@"avatarurlDict.plist"];
 NSString *avatarurlAndNameDictPath = [path stringByAppendingPathComponent:@"avatarurlAndNameDict.plist"];
 [userNameDict writeToFile:userNameDictPath atomically:YES];
 [avatarurlDict writeToFile:avatarurlDictPath atomically:YES];
 [avatarurlAndNameDict writeToFile:avatarurlAndNameDictPath atomically:YES];
}

Analyze a problem

In actual development, There are always details, Although sometimes I feel that, These things are too basic, But on the basis of this basic knowledge, We missed one point that we should have noticed. For example, when we clearly know that when adding elements to an array, Element cannot be empty, Remember to consider nil, The situation of null. Everyone knows this, But it is most easily overlooked, Because you can't be sure what the background data returns, Including those fields that the specification document explicitly requires cannot be nil, It is possible to return an nil or Null. At this time, I began to want to be quiet. Understand that there is nothing necessary in this world. In addition, Array out-of-bounds problem is also 1 straight, of course, in order to prevent App from flashing back directly, you can choose the method to overwrite the system...... OK, let's get down to business. Let's look at the official Apple document 1 and review the basic things 1. There are two paragraphs about NSDictionary and writeToFile in the document.

NSDictionary

* A key-value pair within a dictionary is called an of one object called key ES1116EN ES1116EN ES1118EN IM s 's value. Within a dictionary, the keys keys are value EN is, no two keys in a single dictionary are equal (_:). In general, a key can be any object (provided that conforms to the protocol-see below), but note that below 7EN using key-value coding the key must be a string (see Accessing Object Properties). Neither a key nor a value can be nil; if you need to represent a null value in a dictionary, you should use NSNull. *

Here, the key in the dictionary can be any object type that complies with the NSCopying protocol, but the key in key-value coding must be an string.

'- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;'

This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.

The object requirements for writing files are described here, that is, the commonly used types of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary, but of course, custom types are not allowed.

Solve a problem

Of course, the final processing is to format NSNumber as NSString, and look at the code


 NSString *dictKey = [NSString stringWithFormat:@"%@",dict[@"userId"]];
 [userNameDict setObject:dict[@"userName"] forKey:dictKey];

Related articles: