- 字典
(1):key或value为nil 会崩溃:
我下面所写的 NSDictionary *dic = @{@“key”: nil}; 大部分可能认为他会崩溃,非常小心, 如果是NSDictionary *dic = @{@“key”: str} 这种写法,大家有可能会忽略str是否为空,造成崩溃;
字典的key或value为nil,字典的key只要为nil任何写法一定会崩,当value为nil的时候,只有NSDictionary *dic = @{@“key”: nil}这种写法会崩, 例如mutableDic[@“key”] = nil 这种写法是不会崩的;
eg:
NSDictionary *dic = @{@"key": nil}; // 崩溃
[mutDic setObject:nil forKey:@"key"]; // 不崩溃
mutDic[@"key"] = nil;
原理:
通过key值得到安全的value值,字符串(NSString)或数组(NSArray)数据为NUll 崩溃
解决方案
- (NSString *)stringForKey_zmm:(NSString *)key
{
NSString *value = [self objectForKey_cmbc:key
withValueClass:[NSString class]];
if ([NSString strNilOrEmpty_cmbc:value])
{
value = @"";
}
return value;
}
(2):当字典通过key取出的值已数组接收,但其实是字符串崩溃:
eg:
NSDictionary *dic = @{@"key": @"a"};
NSArray *arr = dic[@"key"]; // arr其实是字符串,但编译器不会报错
NSLog(@"%@", arr[0]); // 崩溃
解决方案
- (NSArray*)arrayForKey_zmm:(id)key
{
id object = [self objectForKey:key];
if (!object) { return nil; }
if (![object isKindOfClass:[NSArray class]]) {
object = [NSArray arrayWithObject:object];
}
return object;
}
- (id)objectForKey_zmm:(NSString *)key withValueClass:(Class)valueClass
{
if (![key isKindOfClass:[NSString class]]) {
return nil;
}
if (valueClass == NULL) {
return nil;
}
id value = [self objectForKey:key];
if (!value) {
return nil;
}
if (valueClass == [NSArray class] && ![value isKindOfClass:[NSArray class]]) {
return [NSArray arrayWithObjects:value, nil];
}
if (![value isKindOfClass:valueClass]) {
return nil;
}
return value;
}
2.数组:index>数组的count造成崩溃
(1)数组越界崩溃
eg:
NSArray *array= @[@1, @2, @3];
NSNumber *num = array[3]; // 崩溃
解决方案
方案:
- (id)objectAtIndex_zmm:(NSUInteger)index
{
if (self.count == 0) {
return nil;
}
if (index >= self.count) {
return nil;
}
return [self objectAtIndex:index];
}
3.NSMutableArray(可变数组的取值和插入与删除)
``
解决方案`
-
(void)removeObjectAtIndex_zmm:(NSUInteger)index
{
if (self.count == 0) {
return;
}
if (index >= self.count) {
return;
}
[self removeObjectAtIndex:index];
} -
(void)addObject_zmm:(id)anObject
{
if (!anObject) {
return;
}
[self addObject:anObject];
} -
(void)insertObject_zmm:(id)anObject atIndex:(NSUInteger)index
{
if (!anObject) {
return;
}
if (index > self.count) {
return;
}
[self insertObject:anObject atIndex:index];
}
4、对于数组遍历
比如像需要遍历某个数组发通知,有两点需要注意。
第一,如果数组的内容可能被多个线程修改,则需要先将数组拷贝一份,对拷贝之后的数组进行遍历。这种方法可以避免在在遍历过程中,数组被其他修改,数组取值出现异常, 导致闪退。
第二,是如果这个数组是使用了unretain弱引用的话,由于数组的对象可能在遍历的过程中会被释放,一旦被释放,unretain的内容就会变成野指针,直接取值也会闪退。一定要先使用arrayWithArray生成一个NSArray对象,该对象会强引用对象,在遍历的过程中不会闪退。
eg: 某数组self.arr
NSArray *array = [NSArray arrayWithArray:self.arr];
[array enumerateObjectsUsingBlock:^( _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
2.字符串
字符串不存在,判断字符串是否为空,
解决方案
+ (NSString *)safeGet_zmm:(NSString *)str
{
if (!str) {
return @"";
}
return str;
}
+ (BOOL)strNilOrEmpty_zmm:(NSString *)str
{
if (!str||![str isKindOfClass:[NSString class]])
{
return YES;
}
return [str isStringEmpty_cmbc];
}
- (BOOL)isStringEmpty_zmm
{
return (0 == self.length) ? YES : NO;
}
5.NSKeyedUnarchiver(归档关于数据类型)
+(nullable id)unarchiveObjectWithData:(nonnull NSData *)data objectType_zmm:(Class _Nullable )classObject
{
@try {
if (![data isKindOfClass:[NSData class]]) {
return nil;
}
id result = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (!classObject || [result isKindOfClass:classObject]) {
return result;
}
return nil;
}
@catch (NSException * e) {
return nil;
}
}
- 用try - catch
对于一些c类使用,有时抛出的异常崩溃,需要把某段逻辑写到try里面;一些由于个别业务,触发的某种异常处理,也需要把代码写到try里。
@try {
} @catch (NSException *exception) {
} @finally {
}
7.还有权限
****控制台报忠告:
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.**** | |
---|---|
大致意思就是:App缺少一个获取私有数据的权限描述,需要我们在info.plist文件中必须含有一个名字叫做NSPhotoLibraryUsageDescription的值来解释为什么应用需要使用这个数据,没错,获取相册资源的键值就是NSPhotoLibraryUsageDescription
通过类似事情,说明iOS10对用户的隐私又做了进一步加强,就好像当初iOS8对定位隐私进行加强一样,作为开发者的我们貌似也是应该时刻保持这种对新知识警觉性的。
除了相册的权限,iOS10之后如下的权限请求也是需要我们填写请求描述的,在这里也给大家提醒一下:
Privacy - Microphone Usage Description //麦克风权限
Privacy - Contacts Usage Description //通讯录权限
Privacy - Camera Usage Description //摄像头权限
Privacy - NSSiriUsageDescription //Siri的权限
Privacy - Bluetooth Peripheral Usage Description //蓝牙
Privacy - Reminders Usage Description //提醒事项
Privacy - Motion Usage Description //运动与健康
Privacy - Media Libaray Usage Description //媒体资源库
Privacy - Calendars Usage Description //日历
内存溢出或泄露,分为下面几个方面
-
定时器没有释放或通知没有移除,或者循环引用,造成界面无法释放,内存增加
-
在for循环创建了很多局部变量,当遍历次数过多时造成内存急剧增加,崩溃, 可以通过添加@autoreleasepool解决;
eg:
for (int i = 0; i < 5000000; i++) {
NSObject *obj = [[NSObject alloc] init]; // 内存暴增,局部变量没有释放
}
解决方案
for (int i = 0; i < 5000000; i++) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init]; // 内存减少, 运行时间差不多
}
}
- 绘制图片,占用内存过大,例如UIGraphicsGetImageFromCurrentImageContext()这些方法绘制大图片,或者加载大量高清晰体积大的图片
- 调用一些公司用C或C++语言写的静态库,由于不能对他们进行垃圾回收,造成内存泄露或溢出,用Instruments 来检查一下,如果你的项目是要支持长时间运行的,一定要仔细观察一下;
- 调用底层C语言框架,没对变量进行释放,造成内存泄露;
13.截取字符串、数组、NSData越界
NSString *str = @"abcdefg";
[str substringWithRange:NSMakeRange(0, 8)]; // 崩溃
解决方案:
1.判断一下range是否超了字符串的总长度
2.用抓取异
@try {
NSString * resultString = [str substringWithRange:range];
} @catch (NSException *exception) {
NSString * resultString = @"";
} @finally {
}
14.多个按钮同时点击造成崩溃,在AppDelegate中设置[UIButton appearance].exclusiveTouch = YES, 避免按钮同时点击