Oracle异常分类小记
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle异常分类小记相关的知识,希望对你有一定的参考价值。
参考技术A前几天看《Oracle PL/SQL Programming》的异常处理部分 粗粗看了一遍 觉得有点乱 根据自己的理解作了一下分类 并相应给了一些简例 不一定很准确 供参考
Oracle 异常
具名 Oracle 异常
Oracle 为此类异常预先指定了异常号 异常信息 异常名称
匿名 Oracle 异常
Oracle 为此类异常预先指定了异常号 异常信息 但未指定异常名称
自定义异常
普通自定义异常
与 Oracle 无关的业务逻辑异常 无异常号 异常信息 异常名称需要手工声明
应用程序异常 ( Raise_Application_Error )
与 Oracle 无关的业务逻辑异常 此方法抛出的异常可以自定义异常号及异常信息 可以通过 Exception_Init 绑定到手工声明的异常名称上
)Oracle异常
Oracle异常总是由Oracle检测并自动抛出的
)具名Oracle异常
Oracle定义了 个具名的Oracle异常 比如
Dup_val_on_index(ora )当中唯一索引所对应的列上键入重复值时
No_data_found(ora )执行select into未返回行 或者引用了索引表未初始化的元素时
流程
A)在Exception模块按异常名进行处理
示例
DECLARE
num number;
BEGIN
num:= / ;
EXCEPTION
when ZERO_DIVIDE then
dbms_output put_line(SQLERRM);
END;
/
ORA : divisor is equal to zero
PL/SQL procedure successfully pleted
)匿名Oracle异常
Oracle中存在大量匿名的异常 比如
ORA : parent key not found
由于PL/SQL的异常处理模块只接受异常名称 不接受异常号(除了WHEN OTHERS语句 它可以捕获任意异常 不论你是否具有异常名称) 为便于处理 需要手工为其指定异常名称
流程
A)声明异常
B)将此异常绑定到Oracle异常号上
C)在Exception模块按异常名进行处理
示例
DECLARE
ex EXCEPTION;
PRAGMA EXCEPTION_INIT(ex );
BEGIN
insert into t values( );
EXCEPTION
when ex then
dbms_output put_line(SQLERRM);
END;
/
ORA : integrity constraint (ADMIN FK ) violated parent key not found
PL/SQL procedure successfully pleted
)自定义异常
自定义异常总是开发者显式抛出来的
)普通自定义异常
流程
A)声明异常
B)使用Raise语句抛出异常
C)在Exception模块按异常名进行处理
示例
DECLARE
ex EXCEPTION;
BEGIN
RAISE ex;
EXCEPTION
when ex then
dbms_output put_line(SQLERRM);
dbms_output put_line( i raised a user defined exception ex );
END;
/
User Defined Exception
i raised a user defined exception ex
)应用程序异常Raise_Application_Error(Num Msg)
普通自定义异常既没有异常号(SQLCODE一律为 ) 也不能指定异常信息(SQLERRM一律为 User Defined Exception ) 如果想要为自定义异常指定异常号与异常信息 需要借助Raise_Application_Error(Num Msg)函数 这里的Num即异常号 范围是[ ] Msg则是异常信息
对于此类异常 由于Num是自定义的 因此应该有相应的管理办法以避免重复使用异常号 比如可以将已使用的异常号保存在一张数据库中表 以便将来检查
流程
A)声明异常
B)将此异常绑定到自定义的异常号上
C)使用Raise_Application_Error(Num Msg)抛出异常 同时指定了异常号及异常信息
D)在Exception模块按异常名进行处理
示例
CREATE OR REPLACE PROCEDURE mtest
is
ex EXCEPTION;
PRAGMA EXCEPTION_INIT(ex );
BEGIN
Raise_Application_Error( raising my exception );
EXCEPTION
when ex then
dbms_output put_line(SQLERRM);
END;
/
Procedure created
SQL> exec mtest
ORA : raising my exception
lishixinzhi/Article/program/Oracle/201311/18175
iOS分类底层实现原理小记
http://www.jianshu.com/p/b7169a5a558e
OS 分类底层是怎么实现的?
本文将分如下四个模块进行探究
- 分类的结构体
- 编译时的分类
- 分类的加载
- 总结
本文使用的runtime源码版本是 objc4 - 680
文中类与分类代码如下
//类
@interface Person : NSObject
@property (nonatomic ,copy) NSString *presonName;
@end
@implementation Person
- (void)doSomeThing{
NSLog(@"Person");
}
@end
// 分类
@interface Person(categoryPerson)
@property (nonatomic ,copy) NSString *categoryPersonName;
@end
@implementation Person(categoryPerson)
- (void)doSomeThing{
NSLog(@"categoryPerson");
}
@end
1.分类的结构体
struct _category_t {
const char *name;//类名
struct _class_t *cls;//类
const struct _method_list_t *instance_methods;//category中所有给类添加的实例方法的列表(instanceMethods)
const struct _method_list_t *class_methods;//category中所有添加的类方法的列表(classMethods)
const struct _protocol_list_t *protocols;//category实现的所有协议的列表(protocols)
const struct _prop_list_t *properties;//category中添加的所有属性(instanceProperties)
};
struct category_t {
const char *name; // 类名
classref_t cls; // 分类所属的类
struct method_list_t *instanceMethods; // 实例方法列表
struct method_list_t *classMethods; // 类方法列表
struct protocol_list_t *protocols; // 遵循的协议列表
struct property_list_t *instanceProperties; // 属性列表
// 如果是元类,就返回类方法列表;否则返回实例方法列表
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) {
return classMethods;
} else {
return instanceMethods;
}
}
// 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,但是...实例属性
property_list_t *propertiesForMeta(bool isMeta) {
if (isMeta) {
return nil; // classProperties;
} else {
return instanceProperties;
}
}
};
2.编译时的分类
2.1分类的属性
// Person(categoryPerson) 属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"categoryPersonName","[email protected]\"NSString\",C,N"}}
};
// Person 属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"presonName","[email protected]\"NSString\",C,N,V_presonName"}}
};
对比上述代码可以发现:在分类中可以声明属性,并且同样会生成一个 _prop_list_t
的结构体
2.2分类的实例变量?
// Person 实例变量列表
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
1,
{{(unsigned long int *)&OBJC_IVAR_$_Person$_presonName, "_presonName", "@\"NSString\"", 3, 8}}
};
因为 _category_t
这个结构体中并没有 _ivar_list_t
所以在编译时系统没有Person(categoryPerson)
没有生成类似的相应结构体,也没有生成 _categoryPersonName
。
2.3分类的实例方法
// Person 实例方法结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"doSomeThing", "[email protected]:8", (void *)_I_Person_doSomeThing},
{(struct objc_selector *)"presonName", "@[email protected]:8", (void *)_I_Person_presonName},
{(struct objc_selector *)"setPresonName:", "[email protected]:[email protected]", (void *)_I_Person_setPresonName_}}
};
// Person(categoryPerson )实例方法结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"doSomeThing", "[email protected]:8", (void *)_I_Person_categoryPerson_doSomeThing}}
};
对比上述方法可以看到:虽然分类可以声明属性,但是编译时,系统并没有生成分类属性的 get/set 方法,所以,这就是为什么分类要利用
runtime 动态添加属性,如何动态添加属性,有兴趣的同学可以查看下面文章 iOS分类中通过runtime添加动态属性
2.4分类的结构体
// Person(categoryPerson ) 结构体
static struct _category_t _OBJC_$_CATEGORY_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_categoryPerson,
};
这是系统在编译时实例化 _category_t
生成的_OBJC_$_CATEGORY_Person_$_categoryPerson
2.5分类数组
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Person_$_categoryPerson,
};
编译器最后生成了一个数组,数组的元素就是我们创建的各个分类,用来在运行时加载分类。
3.分类的加载
3.1加载分类调用栈
_objc_init
└──map_2_images
└──map_images_nolock
└──_read_images
分类加载的调用栈如上述
_objc_init
算是整个 objc4 的入口,进行了一些初始化操作,注册了镜像状态改变时的回调函数map_2_images
主要是加锁并调用map_images_nolock
map_images_nolock
在这个函数中,完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了_read_images
_read_images
方法干了很多苦力活,比如加载类、Protocol、Category,加载分类的代码就写在_read_images
函数的尾部
该调用栈入口函数 void _objc_init(void)
在 objc-os.mm
中,有兴趣的同学可以去看看这些函数里都做了什么
3.2 _read_images
中加载分类的源码
加载分类的源码主要做了两件事
- 把category的实例方法、协议以及属性添加到类上
- 把category的类方法和协议添加到类的metaclass上
略去与本文无关的代码,得到如下代码
// 获取 镜像中的所有分类
category_t **catlist = _getObjc2CategoryList(hi, &count);
// 遍历 catlist
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
if (cat->classMethods || cat->protocols
/* || cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
做上述事情主要用到是如下两个函数
-
addUnattachedCategoryForClass(cat, cls, hi)
为类添加未依附的分类
执行过程伪代码:
1.取得存储所有 unattached 分类的列表NXMapTable *cats = unattachedCategories();
2.从 cats 列表中找倒 cls 对应的 unattached 分类的列表
category_list *list = (category_list *)NXMapGet(cats, cls);
3.将新来的分类 cat 添加刚刚开辟的位置上
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
4.将新的 list 重新插入 cats 中,会覆盖老的 list
NXMapInsert(cats, cls, list);
执行完上述过程后,系统将这个分类放到了一个 cls 对应的 unattached 分类的 list 中(有点绕口....),这个 list 会在
remethodizeClass(cls)
方法用到 -
remethodizeClass(cls)
执行过程伪代码:
1.取得 cls 类的 unattached 的分类列表category_list *cats = unattachedCategoriesForClass(cls, false/*not realizing*/)
2.将 unattached 的分类列表 attach 到 cls 类上
attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/);
执行完上述过程后,系统就把category的实例方法、协议以及属性添加到类上
-
最后再来看一下
attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/)
内部的实现过程
1.在堆上创建方法、属性、协议数组,用来存储分类的方法、属性、协议method_list_t **mlists = (method_list_t **)malloc(cats->count * sizeof(*mlists)); property_list_t **proplists = (property_list_t **)malloc(cats->count * sizeof(*proplists)); protocol_list_t **protolists = (protocol_list_t **)malloc(cats->count * sizeof(*protolists));
2.遍历 cats ,取出各个分类的方法、属性、协议,并填充到上述代码创建的数组中
int mcount = 0; // 记录方法的数量 int propcount = 0; // 记录属性的数量 int protocount = 0; // 记录协议的数量 int i = cats->count; // 从后开始,保证先取最新的分类 bool fromBundle = NO; // 记录是否是从 bundle 中取的 while (i--) { // 从后往前遍历 auto& entry = cats->list[i]; // 分类,locstamped_category_t 类型 // 取出分类中的方法列表;如果是元类,取得的是类方法列表;否则取得的是实例方法列表 method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { mlists[mcount++] = mlist; // 将方法列表放入 mlists 方法列表数组中 fromBundle |= entry.hi->isBundle(); // 分类的头部信息中存储了是否是 bundle,将其记住 } // 取出分类中的属性列表,如果是元类,取得是nil property_list_t *proplist = entry.cat->propertiesForMeta(isMeta); if (proplist) { proplists[propcount++] = proplist; // 将属性列表放入 proplists 属性列表数组中 } // 取出分类中遵循的协议列表 protocol_list_t *protolist = entry.cat->protocols; if (protolist) { protolists[protocount++] = protolist; // 将协议列表放入 protolists 协议列表数组中 } }
3.取出 cls 的 class_rw_t 数据
auto rw = cls->data();
4.存储方法、属性、协议数组到 rw
// 准备 mlists 中的方法 prepareMethodLists(cls, mlists, mcount/*方法列表的数量*/, NO/*不是基本方法*/, fromBundle/*是否来自bundle*/); // 将新属性列表添加到 rw 中的属性列表数组中 rw->properties.attachLists(proplists, propcount); // 释放 proplists free(proplists); // 释放 proplists // 将新协议列表添加到 rw 中的协议列表数组中 rw->protocols.attachLists(protolists, protocount); // 释放 protolists free(protolists); // 释放 protolists // 将新协议列表添加到 rw 中的协议列表数组中 rw->protocols.attachLists(protolists, protocount); // 释放 protolists free(protolists);
4.总结
至此,本文接近尾声
希望本文的读者能够了解到
- 分类的结构体
- 分类中是否能添加属性
- 分类中是否有实例变量
- 分类是如何 attach 到类上的
行笔简陋,如有问题,敬请指正
以上是关于Oracle异常分类小记的主要内容,如果未能解决你的问题,请参考以下文章