分析ios常见crash
一.常见crash 1.方法调用错误
1
-[<object> <selector>]: unrecognized selector sent to instance 0x1a12be490
2.僵尸引用,野指针
二.runtime 消息调用错误导致unrecognized selector sent to instance
crash,大多数原因是开发人员在编码过程中混淆对象类型,theReceiver无法识别theSelector,消息传递失败,且无法转发。
消息机制
objc_msgSend oc在运行时,编译器会将消息转换为对消息函数 id objc_msgSend(id theReceiver, SEL theSelector, ...)
的调用,根据theReceiver类型,找到SEL对应的方法实现IMP,然后将隐藏参数self,cmd以及指定的参数传递给方法实现 IMP,最后,将方法实现的返回值作为该函数的返回值返回,并且将方法缓存,以便下次调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef struct objc_class {
Class isa;
Class super_class
...
struct objc_cache *cache // 方法缓存
...
} *Class
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_selector *SEL ;
typedef id (*IMP) (id, SEL, ...) ;
typedef struct objc_method *Method ;
typedef struct objc_ method {
SEL method_name;
char *method_types;
IMP method_imp;
};
如果在自身以及所有父类的方法列表中都没有找到对应的 IMP,进行动态方法决议,实现以下NSObject中方法,在其中为指定的 selector 供实现即可(调用运行时函数class_addMethod
来添加,其中Type Encoding ),并返回YES表示不需要进行消息转发。
1
2
3
4
5
+ (BOOL )resolveClassMethod:(SEL)name;
+ (BOOL )resolveInstanceMethod:(SEL)name;
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
在动态方法决议实现并返回NO,则进行消息转发。新建一个消息接收类,实现forwardingTargetForSelector
方法,把消息转给其他接收者来注册并返回该对象。
1
-(id ) forwardingTargetForSelector:(SEL)selector;
当forwardingTargetForSelector
返回nil,若不想程序crash,需要生成一个methodSignature
变量来组装,这个变量包含了方法的参数类型、参数个数以及消息接收者等信息。接着把这个变量组装成一个NSInvocation对象进行最后一次的消息转发,调用接收者的forwardInvocation:
来进行最后的挽救机会
1
2
- (void ) forwardInvocation:(NSInvocation *)anInvocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
1
- (void )doesNotRecognizeSelector:(SEL)aSelector
三.内存管理 对象释放后未置为nil,调用方法抛出异常EXC_BAD_ACCESS
。 1.属性关键字
strong: 引用计数加1
copy:在堆上分配空间,copy对象,用于集合对象,防止子类可变对象篡改值
weak:对象销毁后置为nil
assign:对象销毁后不置为nil,用于基本数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface Test ()
@property (nonatomic , strong ) NSString *strongStr;
@property (nonatomic , copy ) NSString *copyedStr;
@end
@implement Test {
- (void )test {
NSMutableString *tempStr = [[NSMutableString alloc] initWithString:@"abc" ];
self .strongStr = tempStr;
self .copyedStr = tempStr;
NSLog ("self.strongStr: %@, self.copyedStr: %@" , self .strongStr, self .copyedStr);
[tempStr appendString:"de" ];
NSLog ("self.strongStr: %@, self.copyedStr: %@" , self .strongStr, self .copyedStr);
}
}
2.对象置空后发送消息返回nil,不会报错
3.copy与mutableCopy
非集合类对象
复制
结果
immutable
copy
浅copy,immutable
immutable
mutableCopy
深copy,mutable
mutable
copy
深copy,immutable
mutable
mutableCopy
深copy,mutable
集合类对象那个的单层拷贝与非集合类对象类似。 集合对象的深层拷贝方法: 1)集合的深复制有两种方法。可以用 initWithArray:copyItems:
将第二个参数设置为YES
1
NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES ];
2) 归档解档
1
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];