iOS异常采用处理方式
ios开发过程中我们经常会遇到异常问题
nserror(错误信息)
采用nserror的情况
使用 nserror
的形式可以把程序中导致错误原因回报给调用者,而且使程序正常运行不会造成奔溃的后果
nserror包含的内容
@interface nserror : nsobject <nscopying, nssecurecoding> {
@private
void *_reserved;
nsinteger _code;
nsstring *_domain;
nsdictionary *_userinfo;
}
-
nserror _code
(错误码, 类型nsinteger
) : 独有的错误代码,指明在某个范围内具体发生的错误。可能是一系列的错误的集合,所以可以使用 enum 来定义。 -
nserror *_domain
(错误范围,类型nsstring
) :错误发生范围,通常使用一个特有的全局变量来定义。 -
nserror *_userinfo
(用户信息,类型nsdictionary
) :有关错误的额外信息,其中包含一段“本地化的描述”,还可能包含导致此错误发生的另一个错误。userinfo 可以描述为一条错误的链条
nserror使用的两种情况
在方法实现或者是 api 设计时,我们对 nserror
的使用情形有两种:在协议中传递和“输出参数”返回给调用者
1.通过委托协议来传递错误
- (void)connection:(nsurlconnection *)connection witherror:(nserror *)error;
通过代理协议的方式可以把错误报告信息传递
2.“输出参数”返回给调用者
- (bool)dosomething:(nserror **)error;
错误的信息作为一个指向指针的指针(也就是说一个指针指向错误的对象)
@throw nsexception (异常抛出)
采用 @throw 情况
在 app 运行期间遇到问题,需要对问题进行操作终止程序抛出异常,可以使用 @throw
来进行
采用 @throw 可能产生问题
在异常抛出实例中,如果抛出异常。在实现抛出异常的代码后面的执行释放资源就不会执行,这样末尾的对象就不会被释放。如果想要生成“异常安全”的代码,可以设置编译器的标志 -fobjc-arc-exceptions
来进行实现。不过将要一如加一些额外的代码,在不抛出异常时也会执行代码。
在不使用 arc 时也很难实现异常抛出情况下对内存进行释放。
nserror * error = nil;
bool success = [self dosomething:&error];
if(error && success) {
@throw[nsexception …];
}
[success release];
按照上面实现当在异常抛出的过程时,success 还来不及释放。所以要解决上面的问题可以在异常抛出之前对 success 进行释放,但是当需要释放的资源有很多的情况下,这样操作起来就比较复杂。
在开发过程中异常抛出可以使用在 抽象的基类 实例方法中进行调用。如果基类中的方法不被 override
就设置抛出异常,这样可以保证实例方法被重写。
@try @catch @finally (异常捕捉)
采用 @try @catch @finally 情况
个人感觉 @try @catch @finally 是 @throw nsexception 的加强版。前者可以实现对异常的捕捉,相关异常的输出,和异常输出后执行的 @finally 相关的具体操作。后者是对具体整理 nsexpection 抛出,并没有前者比较完善的流程化操作步骤。
如果在基类中实现 @throw 进行设置 必须 override 基类中的实例方法,那么捕获异常的方法中使用 @try @catch @finally。例如:nsarray,nsdictionary 初始变量使用字面量来获取值时,需要判断返回数据是否为 nil 来防止 app crash。
产生问题和解决方式
当使用 @try @catch @finally
时,有两种情况 mrc
和 arc
。
mrc
(ios 5 之前) 的环境中,上面的代码可以展示为:
someclass *someclass = nil;
@try {
someclass = [[someclass alloc] init];
[someclass dosomethingsthatmaythrow];
}
@catch() {
nslog(… …);
}
@finally {
[someclass release];
}
在 arc 的环境中,上面的代码展示为:
someclass *someclass = nil;
@try {
someclass = [[someclass alloc] init];
[someclass dosomethingsthatmaythrow];
}
@catch( … ) {
nslog(… …);
}
@finally {
}
可以看出在 mrc 的情形下可以实现对于内存的释放,在 arc 的情形下会系统会实现对内存的释放? 这样向正确吗?
答案是:arc 不会自动处理,因为如果要实现自动处理可能要加入大量的代码,才可以清楚对象实现抛出异常时将其清理。但是如果加入代码就会影响运行时的性能,在正常运行时也会如此。
如果在当前实现中开启 -fobjc-arc-exception
的模式可以实现在 @try @catch @finally
在异常情况下实现对未释放的对象进行内存的释放管理
@try@catch@finally 的 c++ 源码
查看异常抛出的源码:
建立项目在 main.m
文件中实现下面代码:
someclass *someclass = nil;
@try {
someclass = [[someclass alloc] init];
[someclass dosomethingsthatmaythrow];
}
@catch( … ) {
nslog(… …);
}
@finally {
}
打开终端在 main.m
终端的文件夹路径执行下面的语句
clang -x objective-c -rewrite-objc -isysroot /applications/xcode.app/contents/developer/platforms/iphonesimulator.platform/developer/sdks/iphonesimulator.sdk main.m
会生成文件 main.cpp
的文件,可以打开查看文 @try @catch @fianlly
nsexpection 属性内容
__attribute__((__objc_exception__))
#ifndef _rewriter_typedef_nsexception
#define _rewriter_typedef_nsexception
typedef struct objc_object nsexception;
typedef struct {} _objc_exc_nsexception;
#endif
struct nsexception_impl {
struct nsobject_impl nsobject_ivars; //实例变量
nsstring *name; //exception 的名字
nsstring *reason; //exception 产生的原因
nsdictionary *userinfo; //exception 展示使用详细的信息
id reserved; //
};
nserror 属性内容
#ifndef _rewriter_typedef_nserror
#define _rewriter_typedef_nserror
typedef struct objc_object nserror;
typedef struct {} _objc_exc_nserror;
#endif
struct nserror_impl {
struct nsobject_impl nsobject_ivars; //实例变量
void *_reserved; //实例调用的方法
nsinteger _code; //error 错误码
nsstring *_domain; //error 错误发生范围
nsdictionary *_userinfo; //error 错误描述的具体信息
};
下面在 main.m
中使用 clang
解析 @try @catch @finally
在 c++ 环境中解析
在没有参数的情况下
int main() {
@try {
} @catch (nsexception *exception) {
} @finally {
}
}
经过 clang 解析后源码如下:
int main() {
{ id volatile _rethrow = 0;
try {
try {
//异常捕获
} catch (_objc_exc_nsexception *_exception) {
nsexception *exception = (nsexception*)_exception;
//捕获异常,并进行抛出
}
}
catch (id e) {
_rethrow = e;
}
{
struct _fin { _fin(id reth) : rethrow(reth) {}
~_fin() { if (rethrow) objc_exception_throw(rethrow); }
id rethrow;
} _fin_force_rethow(_rethrow);
//异常抛出后执行的 finally 的内容
}
}
}
static struct image_info { unsigned version; unsigned flag; } _objc_image_info = { 0, 2 };
在有参数的情况下
int main() {
nsdictionary *dic = @{@"name":@"liang",
@"last name":@"bai",
@"dream":@"to be a bussinessman",
@"work":@"it"};
nsstring *salaryliterals = nil;
nsstring *salaryatindex = nil;
@try {
salaryatindex = [dic objectforkey:@"salary"];
salaryliterals = dic[@"salary"];
} @catch (nsexception *exception) {
nslog(@"error name is %@, reason : %@, userinfo : %@", exception.name, exception.reason, exception.userinfo);
} @finally {
}
}
经过 clang 解析后源码如下:
int main() {
nsdictionary *dic = ((nsdictionary *(*)(class, sel, const objecttype *, const id *, nsuinteger))(void *)objc_msgsend)(objc_getclass("nsdictionary"),
sel_registername("dictionarywithobjects:forkeys:count:"),
(const id *)__nscontainer_literal(4u, (nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_1,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_3,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_5,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_7).arr,
(const id *)__nscontainer_literal(4u, (nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_0,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_2,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_4,
(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_6).arr, 4u);
nsstring *salary = __null;
{ id volatile _rethrow = 0; //使用 volatile 修饰的 _rethrow 记录异常的局部变量
try {
try {
salary = ((id _nullable (*)(id, sel, keytype))(void *)objc_msgsend)((id)dic, sel_registername("objectforkeyedsubscript:"),
(id)(nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_8);
}
catch (_objc_exc_nsexception *_exception) {
nsexception *exception = (nsexception*)_exception;
nslog((nsstring *)&__nsconstantstringimpl__var_folders_tv_4c43vcqx24vcxx6d51n4ntc00000gn_t_main_753a25_mi_9,
((nsexceptionname (*)(id, sel))(void *)objc_msgsend)((id)exception, sel_registername("name")),
((nsstring * _nullable (*)(id, sel))(void *)objc_msgsend)((id)exception, sel_registername("reason")),
((nsdictionary * _nullable (*)(id, sel))(void *)objc_msgsend)((id)exception, sel_registername("userinfo")));
}
}
catch (id e) {
_rethrow = e;
}
{
struct _fin {
_fin(id reth) : rethrow(reth) {}
~_fin() {
if (rethrow) objc_exception_throw(rethrow);
}
id rethrow;
}
_fin_force_rethow(_rethrow);
salary = __null;
}
}
}
static struct image_info { unsigned version; unsigned flag; } _objc_image_info = { 0, 2 };
总结:
(1)遇到奔溃问题或者是错误问题,优先使用
nserror
来对奔溃和错误进行封装,然后使用nslog
对其进行打印(2)
@try @catch @finally
在使用的过程中很方便,但是mrc
中如果变量较多可能会漏掉局部变量内存释放问题和arc
中如果抛出问题,不会自动对局部变量释放(开启-fobjc-arc-expections
模式会进行释放,但是引入代码对性能有所影响)
上一篇: 什么样的枸杞子最好,这些方法少不了
下一篇: python爬虫--模拟12306登录
推荐阅读
-
iOS异常采用处理方式
-
【iOS异常】 JSonKit does not support Objective-C Automatic Reference Counting(ARC)
-
【iOS异常】导入SDWebImage时SDWebImageDownloaderOperation.m错误
-
iOS 抛出异常@throw解决方案
-
springmvc 获取@Requestbody转换的异常处理方式
-
iOS中常用的圆角处理方式总结
-
iOS开发--异常处理
-
iOS 开发异常处理
-
iOS14适配【 采用hook全局性地解决UITableViewCell兼容问题】往cell添加子视图的方式不规范,导致contentView 置于自定义控件的上层,引发界面无响应(注意处理相关方法)
-
iOS异常处理