实际应用中很多报表的数据来源于多个不同类型的
数据库,报表数据源跨数据库是报表开发中的常态。目前实现这类跨库关联报表的方式有多种,但都会存在这样那样的问题。
使用报表工具自身多源关联功能
现在大多数主流报表工具都支持多数据源关联,这在某些方面确实为报表用户带来了便利。然而我们也经常会遇到通过报表自身的多源关联功能很难实现一些跨库关联的报表(由于数据结构和业务本身决定)。这当然容易理解,报表工具主要是来做数据展现的,而对数据计算本身来说并不擅长。
即使有的能实现,在报表中做跨库关联计算的效率也较低,远远不如数据库的性能,而且经常因为在实现过程中使用了大量隐藏行列进一步降低报表性能并加大内存占用。
于是一般会采用下面的两种方式。
将数据统一到一个数据库中
将不同数据库的数据统一到一个数据库中,这种做法在各类应用中很常见,目的是使用数据库(
SQL)强大的计算能力。然而,这种做法会增加额外的成本开销,将多个数据库中的数据统一到一个数据库中势必会占用昂贵的数据库空间,并造成该数据库数据过多、管理困难、压力增大等问题,而且还可能负担额外的数据库购买成本以及管理成本。另外,完成ETL迁移数据也是一份不小的
工作量,对实时性要求较高的报表还得用触发器方式来做ETL(一般ETL是定时的),会严重影响原数据库的性能。
使用高级语言实现跨库关联为报表准备数据
基于上面提到两种方式中存在的问题,有些用户使用高级语言(Java等)编程完成跨库运算,为报表自定义数据源。这种做法的优点是灵活,理论上任何运算通过程序都能完成;缺点是太难写。很多像Java这样的高级语言缺乏对集合运算的有效支持,没有相应的类库,导致完成个简单的group也要写很多(循环)代码,更不用说关联以后还要再进行分组汇总等其他运算了。
这种情况下,使用集算器来实现跨库关联报表就是个不错的选择。
集算器如何实现跨库关联报表?
我们通过一个例子来看集算器是如何快速实现跨库关联报表的。
业务描述
企业员工每月应发工资跟员工的基本工资、考勤以及绩效有关,考勤信息来源于人力部门的考勤系统(hsql数据库),基本工资和绩效信息则存储在财务系统(mysql数据库)中。需要将这两类信息合起来计算员工的工资。
实现步骤
编写脚本(crossDB.dfx)完成跨库关联计算,为报表准备数据
在A1、A2中通过connect分别连接hsql和mysql数据源
在A3、A4、A5中通过query语句分别从两个数据库中取出用到的数据,此时数据已全部取出,不再需要与数据库交互,在A6、A7中关闭两个数据库连接
在A8中使用join完成三表的连接
在A9中计算应发工资,算法为:基本工资*(1-考勤系数+绩效系数)
最后通过A10的result生成供报表使用的结果集
报表调用集算器脚本完成报表展现
集算器的类包封装成标准的JDBC,允许报表工具以类存储过程的调用方式调用集算器脚本,如本例中在报表工具中像配置数据库连接一样配置起好集算器的JDBC,然后建立存储过程数据集后使用 call crossDB()即可完成调用。
这里以BIRT为例说明调用过程:
1、 复制集算器JDBC驱动包到相应目录
2、 配置报表数据源
3、 设置DataSet,调用集算器脚本
简单几步完成调用,BIRT即可使用集算器的计算结果直接展现输出。
结语
通过这个例子可以看到,集算器擅长完成跨库计算,并将计算后的结果以标准ResultSet方式返回为报表提供数据源,而报表采用类存储过程调用的方式调用集算器脚本非常简单。
还有一个重点需要关注,就是价格。集算器是个需要付费的商业软件,好在只针对大数据的集群才收费,用作报表数据源功能是免费的,不需要增加成本就能轻松解决多数据库关联报表问题。
//组装一个字符串,把里面的网址解析出来 NSString *urlString = @"sfdshttp://www.baidu.com"; NSError *error; //http+:[^\\s]* 这是检测网址的正则表达式 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"http+:[^\\s]*" options:0 error:&error]; if (regex != nil) { NSTextCheckingResult *firstMatch = [regex firstMatchInString:urlString options:0 range:NSMakeRange(0, [urlString length])]; if (firstMatch) { NSRange resultRange = [firstMatch rangeAtIndex:0]; //从urlString中截取数据 NSString *result = [urlString substringWithRange:resultRange]; NSLog(@"%@",result); } } |
输出结果为:
[1302:403] http://www.baidu.com
可见通过iOS自带的正则表达式的类可以达到过滤和筛选字符串的功能。
还有其他两种正则表达式的用法:
1.利用NSPredicate(谓词)匹配
例如匹配有效邮箱:
NSString *email = @“nijino_saki@163.com”;
NSString *regex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
BOOL isValid = [predicate evaluateWithObject:email];
谓词匹配比较灵活,但是需要有谓词的相关知识。
2.利用rangeOfString:option:直接查找
NSString *searchText = @"// Do any additional setup after loading the view, typically from a nib.";
NSRange range = [searchText rangeOfString:@"(?:[^,])*\\." options:NSRegularExpressionSearch];
if (range.location != NSNotFound) {
NSLog(@"%@", [searchText substringWithRange:range]);
}
options中设定NSRegularExpressionSearch就是表示利用正则表达式匹配,会返回第一个匹配结果的位置。
小结:
第一种匹配需要学习NSPredicate的写法,需要查阅
苹果相关技术文档;如果只关心第一个匹配的结果,第二种匹配较为简洁;如果需要匹配多个结果,同时匹配多次,第三种方式效率会更高。
java的
单元测试中,经常写单元测试方法,测试job任务。而这些job任务,对应电子商务等流水系统来说,比较常见。 它们常用的特点是:实现不同系统之间的
数据库的流向。执行此job,把系统A的数据库执行到系统B的数据库的表中。 中间会有方法进行相关的处理。比如以下是个单元测试,测试生成的job。
@Test public void testCreateSettlement() throws ParseException { //createSettlementTask.createSettlement(null); Calendar ca = Calendar.getInstance(); ca.add(Calendar.DAY_OF_MONTH, -17); SettlementResult res = createSettlementFacade.createSettlementOrder(ca.getTime()); System.out.println("res->"+res); } |
此提现job的作用是:把某个系统A的订单,执行此job后,把数据处理后,插入到系统B后生成结算单。
那么这个是
功能测试时,需要验证系统A的订单数据,是否都生成到了系统B的结算单呢。
首先需要准备各种订单数据,生成不同的结算单。 手工测试的压力太大了。
@before public void before() { 执行此job之前的数据准备的封装 } @test equals(request,response) } @after public void after() { 执行此job之后的数据变化 } |
其实难点还是在于
用例设计和数据准备这块。怎么样的用例可以作为一个场景。而且能覆盖到提交的代码的更新。
开局ASA1
#/mnt/disk0/lina_monitor ciscoasa
> enable
Invalid password
Password:
ciscoasa#
配置IP
R1(config)#int fa0/0 R1(config-if)#ip address 11.0.0.1 255.255.255.0 R1(config-if)#no shut ciscoasa(config)# int e0/0 ciscoasa(config-if)# nameif inside INFO: Security level for "inside" set to 100 by default. ciscoasa(config-if)# ip address 11.0.0.2 255.255.255.0 ciscoasa(config-if)# no shut ciscoasa(config)# int e0/1 ciscoasa(config-if)# nameif outside INFO: Security level for "outside" set to 0 by default. ciscoasa(config-if)# ip address 12.0.0.1 255.255.255.0 ciscoasa(config-if)# no shut R2(config)#int fa0/0 R2(config-if)#ip address 12.0.0.2 255.255.255.0 R2(config-if)#no shut |
指定静态路由:
R1(config-if)#ip route 12.0.0.0 255.255.255.0 11.0.0.2
R2(config)#ip route 11.0.0.0 255.255.255.0 12.0.0.1
验证静态路由:
默认情况下,
ASA对TCP和UDP协议提供状态化连接,ICMP协议提供非状态化的连接(防火墙由于不记录路由所以将数据丢掉);
由于ping命令使用的是ICMP协议,所以R1ping外网ping不通。
在R2上开远程,验证ASA对TCP协议的状态化连接:
R2(config)#line vty 0 4
R2(config-line)#password abc
R2(config-line)#login
在R1上远程R2
1、QC建项目
使用管理员账号登录,创建项目→然后选择想创建的项目,点击下一步。
输入项目名称,点击下一步。会出现要连接的
数据库和服务器(可选),点击下一步即可选择用户,再点击下一步即可创建项目成功。
2、新建用户
系统管理员账号登录,站点用户→点击
弹出添加用户窗口,输入要添加的用户信息后点击确定即可;密码设置在站点用户左侧点击要设置密码的用户,点击密码后重设即可。
3、选择项目用户
1、如果在创建项目时未选择项目用户,登录管理员账号,选择需要修改用户的项目,然后项目用户→可以选择在列表添加还是从另外一个项目复制(如果没有太大出入可以复制然后再进行修改)。
添加即可发现界面左侧有用户选择,双击或者选中点击箭头都可以添加进去。
注意
项目管理员的勾选,不用勾上了不是管理员的克。
4、Qc自定义字段
对项目用户进行分组和各项字段自定义,用此项目给予了管理员权限的账号登录此项目。
工具→自定义→自定义项目实体,如状态,可以点击【转至列表】进行添加删除等操作。
5、给用户不同权限
项目管理员登录此项目,自定义→设置组→新建。可以设置为开发组、测试组、项目管理组然后给予不同的权限。然后可以根据分组把不同的用户归入相应的组内。
6、未点击注销直接关掉网页
会出现问题,有时候是登录出现问题,或者修改的时候无法修改。使用qc管理员账号登录→点击要断掉连接→点击断开连接即可。
Ps、1、登录时不用勾选自动登录2、大多数修改之后注意点击右下角的保存按钮。wKioL1PYoE6zacJ4AAAKq4DwI-Y919.gif俺老是忘记保存~~~
原则上,是否使用测试框架都不会影响
单元测试结果,但是“工欲善其事,必先利其器”使用
单元测试框架更便于我们测试和分析结果。
主要的iOS单元测试框架有:
OCUnit,是开源测试框架,与Xcode工具集成在一起使用非常方便,测试报告以文本形式输出到输出窗口;
GHUnit,是开源测试框架,它可以将测试报告以应用形式可视化输出到设备或模拟器上,也可以以文本形式输出到输出窗口;GHUnit可以测试OCUnit编写的
测试用例;
OCMock,是开源测试框架,它主要为测试提供Mock对象(伪对象)。
添加OCUnit到工程
添加OCUnit到工程中有两种方法,一种是在创建工程时添加,勾选“include Unit Tests”;另一种是在现有工程中添加“Cocoa Touch Unit Testing Bundle”Target来实现。下面我们详细介绍这两种添加过程。
1、创建工程时候勾选“include Unit Tests”
该种方式添加的单元测试属于应用测试(Application Testing)。在创建一个工程时,如果采用“Single View Application”模板,在选项中勾选“include Unit Tests”即可在工程中添加OCUnit框架。
工程创建完,在导航面板中会多一个PITaxTests组(<工程名>Tests),包含PITaxTests测试类。在右边的Target栏中多了一个PITaxTests Target。
但是打开Scheme列表还只有一个PITax,这是我们需要注意的。运行它可以通过:选择菜单Product→
Test或工具栏中Test按钮(下拉Run按钮选择)或快捷键command+U等几种方式。
如果打开Frameworks组会发现添加了SenTestingKit.framework,SenTestingKit.framework就 是OCUnit框架。因为单元测试框架一般命名为xUnit,如Java的单元测试框架是
JUnit,.NET单元测试框架是NUnit等,OCUnit 是Objective-C单元测试框架之意。
2、现有工程中添加Target实现
该种方式添加的单元测试,属于逻辑测试(Logic Testing)。在一个现有工程中,选择菜单File→New→Target…,选择模板iOS→Other中的 “Cocoa Touch Unit Testing Bundle”。
点击Next按钮,在Product Name项目中输入LogicTest,创建完成后,在导航面板中多出了一个LogicTest组,包含LogicTest测试类。在右边的Target栏中多了一个LogicTest Target。
我们在
谷歌所做事情中另外一个让我感到异常有效、有用的制度是严格的
编码规范。
在到
Google工作之前,我一直认为编码规范没有什么用处。我坚信这些规范都是官僚制度下产生的浪费大家的编程时间、影响人们开发效率的东西。
我是大错特错了。
在谷歌,我可以查看任何的代码,进入所有谷歌的代码库,我有权查看它们。事实上,这种权限是很少人能拥有的。但是,让我感到惊讶的却是,如此多的编码规范—缩进,命名,文件结构,注释风格—这一切让我出乎意料的轻松的阅读任意一段代码,并轻易的看懂它们。这让我震惊—因为我以为这些规范是微不足道的东西。它们不可能有这么大的作用—但它们却起到了这么大的作用。当你发现只通过看程序的基本语法结构就能读懂一段代码,这种时间上的节省不能不让人震撼!
反对编码规范的人很多,下面是一些常见的理由,对于这些理由,我以前是深信不疑。
这是浪费时间!
我是一个优秀的程序员,我不愿意浪费时间干这些愚蠢的事。我的技术很好,我可以写出清晰的、易于理解的代码。为什么我要浪费时间遵守这些愚蠢的规范?答案是:统一是有价值的。就像我前面说的—你看到的任何的一行代码—不论是由你写的,还是由你身边的同事,还是由一个跟你相差11个时区的距离人写的—它们都有统一的结构,相同的命名规范—这带来的效果是巨大的。你只需要花这么少的功夫就能看懂一个你不熟悉(或完全未见过)的程序,因为你一见它们就会觉得面熟。
我是个艺术家!
这种话很滑稽,但它反映了一种常见的抱怨。我们程序员对于自己的编码风格通常怀有很高的自负。我写出的的代码的确能反映出我的一些特质,它是我思考的一种体现。它是我的技能和 创造力的印证。如果你强迫我遵守什么愚蠢的规范,这是在打压我的创造力。可问题是,你的风格里的重要的部分,它对你的思想和创造力的体现,并不是藏身于这些微不足道的句法形式里。(如果是的话,那么,你是一个相当糟糕的程序员。)规范事实上可以让人们可以更容易的看出你的创造力—因为他们看明白了你的作品,人们对你的认识不会因不熟悉的编码形式而受到干扰。
所有人都能穿的鞋不会合任何人的脚!
如果你使用的编码规范并不是为你的项目专门设计的,它对你的项目也许并不是最佳方案。这 没事。同样,这只是语法:非最优并不表示是 不好。对你的项目来说它不是最理想的,但并不能表明它不值得遵守。不错,对于你的项目,你并没有从中获得该有的好处,但对于一个大型公司来说,它带来的好处是巨大的。除此之外,专门针对某个项目制定编码规范一般效果会更好。一个项目拥有自己的编码风格无可厚非。但是,根据我的经验,在一个大型公司里,你最好有一个统一的编码规范,特定项目可以扩展自己特定的项目方言和结构。
我善长制定编码规范!
这应该是最常见的抱怨类型了。它是其它几种反对声音的混合体,但它却有自身态度的直接表现。有一部分反对者深信,他们是比制定编码规范的人更好的程序员,俯身屈从这些小学生制定的规范,将会降低代码的质量。对于此,客气点说,就是胡扯。纯属傲慢自大,荒唐可笑。事实上他们的意思就是, 没有人配得上给他们制定规范,对他们的代码的任何改动都是一种破坏。如果参照任何一种合理的编码规范,你都不能写出合格的代码,那只能说你是个烂程序员。
当你按照某种编码规范进行编程时,必然会有某些地方让你摇头不爽。肯定会在某些地方你的编码风格会优于这些规范。但是,这不重要。在某些地方,编码规范也有优于你的编程风格的时候。但是,这也不重要。只要这规范不是完全的不可理喻,在程序的可理解性上得到的好处会大大的补偿你的损失。
但是,如果编码规范真的是完全不可理喻呢?
如果是这样,那就麻烦了:你被糟蹋了。但这并不是因为这荒谬的编码规范。这是因为你在跟一群蠢货一起工作。想通过把编码规范制定的足够荒谬来阻止一个优秀的程序员写出优秀的代码,这需要努力。这需要一个执著的、冷静的、进了水的大脑。如果这群蠢货能强行颁布不可用的编码规范,那他们就能干出其它很多傻事情。如果你为这群蠢货干活,你的确被糟蹋了—不论你干什么、有没有规范。(我并不是说罕有公司被一群蠢货管理;事实很不幸,我们这个世界从来就不缺蠢货,而且很多蠢货都拥有自己的公司。)
1、能发现到目前为止没有发现的缺陷的用例是好的用例:
首先要申明,其实这句话是十分有道理的,但我发现很多人都曲解了这句话的原意,一心要设计出发现“难于发现的缺陷”而陷入盲目的片面中去,忘记了
测试的目的所在,这是十分可怕的。我倾向于将
测试用例当作一个集合来认识,对它的评价也只能对测试用例的集合来进行,测试本身是一种“V&;V”的活动,测试需要保证以下两点:
* 程序做了它应该做的事情
* 程序没有做它不该做的事情
因此,作为测试实施依据的测试用例,必须要能完整覆盖测试需求,而不应该针对单个的测试用例去评判好坏。
2、测试用例应该详细记录所有的操作信息,使一个没有接触过系统的人员也能进行测试;
不知道国内有没有公司真正做到这点,或者说,不知道有国内没有公司能够将每个测试用例都写得如此详细。在我的测试经历中,对测试用例描述的详细和复杂程度也曾有过很多的彷徨。写得太简单吧,除了自己没人能够执行,写得太详细吧,消耗在测试用例维护(别忘了,测试用例是动态的,一旦测试环境、需求、设计、实现发生了变化,测试用例都需要相应发生变化)上的时间实在是太惊人,在目前国内大部分软件公司的测试资源都不足的情况下,恐怕很难实现。但我偏偏就能遇到一些这样的老总或者是项目负责人,甚至是测试工程师本身,全然不顾实际的资源情况,一定要写出“没有接触过系统的人员也能进行测试”的用例。
在讨论这个问题之前,我们可以先考虑一下测试的目的。测试的目的是尽可能发现程序中存在的缺陷,测试活动本身也可以被看作是一个Project,也需要在给定的资源条件下尽可能达成目标,根据我个人的经验,大部分的国内软件公司在测试方面配备的资源都是不足够的,因此我们必须在测试计划阶段明确测试的目标,一切围绕测试的目标进行。
除了资源上的约束外,测试用例的详细程度也需要根据需要确定。如果测试用例的执行者、测试用例设计者、测试活动相关人对系统了解都很深刻,那测试用例就没有必要太详细了,文档的作用本来就在于沟通,只要能达到沟通的目的就OK。
在我担任测试经理的项目中,在测试计划阶段,一般给予测试设计30% - 40%左右的时间,测试设计工程师能够根据项目的需要自行确定用例的详细程度,在测试用例的评审阶段由参与评审的相关人对其把关。
3、测试用例设计是一劳永逸的事情;
这句话摆在这里,我想没有一个人会认可,但在实际情况中,却经常能发现这种想法的影子。我曾经参与过一个项目,软件需求和设计已经变更了多次,但测试用例却没有任何修改。导致的直接结果是新加入的测试工程师在执行测试用例时不知所措,间接的后果是测试用例成了废纸一堆,开发人员在多次被无效的缺陷报告打扰后,对测试人员不屑一顾。
这个例子可能有些极端,但测试用例与需求和设计不同步的情况在实际开发过程中确是屡见不鲜的,测试用例文档是“活的”文档,这一点应该被测试工程师牢记。
4、测试用例不应该包含实际的数据;
测试用例是“一组输入、执行条件、预期结果”、毫无疑问地应该包括清晰的输入数据和预期输出,没有测试数据的用例最多只具有指导性的意义,不具有可执行性。当然,测试用例中包含输入数据会带来维护、与测试环境同步之类的问题,关于这一点,《Effective Software Test》一书中提供了详细的测试用例、测试数据的维护方法,可以参考。
5、测试用例中不需要明显的验证手段;
我见过很多测试工程师编写的测试用例中,“预期输出”仅描述为程序的可见行为,其实,“预期结果”的含义并不只是程序的可见行为。例如,对一个订货系统,输入订货数据,点击“确定”按钮后,系统提示“订货成功”,这样是不是一个完整的用例呢?是不是系统输出的“订货成功”就应该作为我们唯一的验证手段呢?显然不是。订货是否成功还需要查看相应的数据记录是否更新,因此,在这样的一个用例中,还应该包含对测试结果的显式的验证手段:在
数据库中执行查询语句进行查询,看查询结果是否与预期的一致。
好久没写博客了,之前还打算把毕业设计中涉及到的两个算法拿出来说说(脸型分析 + 声音分析),博文都写了一半了,后来实在太忙了,那篇
随笔也就沉在草稿列表中没动过。
我原先是专职 .net 开发的,在公司负责的项目是内部自用的销售管理系统,由于不需要出去"抛头露脸",所以公司干脆什么也没配置(指产品、设计、前端等等,开发设备还是有的),于是所有
工作全包了。原本对自身的计划是,后续会慢慢转产品方面的。
后来,也算是一个契机,公司所有
移动端的人基本走光了(iOS 全走了,
Android 的好像只剩 2 个!!公司内部的事,这里就不多说了),于是上头经理就打算让我们转到移动端开发。.net 和
java 一直都是"死对头"(自己的理解,求别喷),所以我不会选 Android,WP 市场还不够,转过去对自己竞争力的提升不大,于是乎选择了 iOS 开发,于是乎便有了这篇《.net 码农转战 iOS - 初探》。。。
大学时期有一点 C 的基础,但是对 OC 完全不知,这次属于从 0 开始(不选择 swift 开始,其实是怕它还不够成熟。语言这东西,其实说到底是相通的,理解了基础的东西,还怕以后学不会其他的么!)。
我是属于自学的,看视频、查资料一步步慢慢走。
学习真的需要动手敲键盘,单单只是看一下是记不住理解不了的,至少我还没到那个看看就会的地步。。。
· 我从 xcode5 开始,这时候已经少了很多内存管理的代码了(基本不用写 release 了),但是不需要你写,并不代表不需要你了解。所以内存管理需要详看。
· 命名空间,OC 里是没有这个概念的。区分两个不同的类,仅仅是依靠类名。所以一般都会在真正类名前,加一个前缀用来区分,例如 NSString(NS 开头的代表为 MAC OS X 的核心,即 NeXTSTEP 的缩写)、CGPoint。。。许多程序员都会使用自己名字的缩写做类的前缀。
· OC 大量使用指针,而且需显性表示(NSString *str; str 是一个指针变量,前面需加一个 "*" 显性体现,表示它是一个指向 "字符串" 的变量)。.net 中也有这个概念,只是不需要显性表明("*"),参考 .net 中,类、string 的声明。
· @"",OC 中字符串的写法,前面必须加一个 "@" 表示是 NSString 类型;
· NSLog() 方法,即 .net 中 Console.WriteLine 方法,支持字符串的 Format,例如 NSLog(@"%@, %i, %f", @"1", 1, 1.0);(@"%@" 功能很强大,类似于 .net 中 ToString() 方法)。
· 类的声明,创建一个普通的类,包含一个 .h 文件和一个 .m 文件(例如 SLTest.h & SLTest.m),是成对出现的,.h 文件为头文件,一般用来做类的声明,类似 .net 里的抽象类;.m 文件为实现文件,用来实现对应的"抽象类"。
/** SLTest.h **/ //使用import引入头文件 //Foundation为基础框架 //import表示该头文件只被包含一次,无论该命令在整个程序中出现多少次 #import<Foundation/Foundation.h> //用interface标明是类的声明 //该类继承自NSObject,类似.net里的基类object @interfaceSLTest:NSObject { //声明成员变量 } //声明公有方法 @end /** SLTest.m **/ //使用import引入头文件 #import"SLTest.h" //implementation标明是类的实现 @implementationSLTest //定义、实现私有方法 //实现公有方法 @end |
· OC 中声明类使用 interface 关键字,实现类使用 implementation 关键字。其实 OC 中还有一个关键字 "class",它并非 "声明类" 的意思,而是 "包含类" 的意思。功能与 import 相似,但使用 import,编译时会将 import 的文件整个包含进来,而使用 class,编译时只是告诉它已经有这个类,而不会整个包含进来,比 import 更轻量级。多用在只需要知道类名,而不需要使用类中方法的地方(例如变量声明等)。使用方法:@class SLTest;。最大好处,例如有两个类:A、B,A 中有一个变量 B b;,B 中有一个变量 A a;,如果使用 import,那么编译时就是"我中有你你中有我",互相包含,最后造成包含的死循环。
· OC 中方法的调用是使用 "[]",例如 [aaa AAA],在 .net 中即表现为 aaa.AAA(); 。有参数的方法调用例如 [aaa BBB:1 CCC:2],在 .net 中表现为 aaa.BBB:CCC:(1, 2); 。注意,OC 中 ":" 也是方法名,有多少个 ":" 表示有多少个参数,而参数的传入是嵌套在方法名中的(怎么定义怎么用,例如刚才有参数的方法,定义为 -(void)BBB:(NSInteger)i CCC:(NSInteger)j;),这点和 .net 有很大的不同。
· 实例化一个对象,由于要考虑到内存管理问题,OC 中基本都是用 alloc + initXXX 方法(也有个别使用其他工厂模式方法,在此不包括),alloc 表示分配内存,initXXX 表示初始化。基本使用方法:[[NSString alloc] initWithFormat:@"this is a string."]。这里的意思是实例化一个 NSString 对象,指向 "this is a string." 的内存块(initWithFormat:() 是 NSString 自有方法,其他类型也有类似的 initXXX() 的方法,也有的只有 init() 方法)。当然 OC 也有 new() 方法,但是不建议使用。还是 .net 的 new() 好用!!OC 中没有重载,只能用方法名来区分。例如 NSString 中的 initWithFormat() 方法和 initWithCoder()。还是 .net 的 new() 好用!!
· get/set 方法,.net 中我们是使用 { get; set; } 让编译器自动为我们添加,OC 也有类似的:
// 使用 property 关键字声明 // 括号内指定该属性的形式: // nonatomic 表示线程不安全的,在单线程不考虑线程安全的情况,使用 nonatomic 就可以了,对应的 atomic // 一般基础类型用 assign,NSString 用 copy,其他用 strong(retain),当然,凡事也有例外,具体自行谷哥吧 // 此外还有 readonly、getter 等等,自行谷哥吧 @property (nonatomic, assign) BOOL balabalabala; |
只需要这句话,编译器会自动生成私有的成员变量和对应的 get/set 方法,当然也可以自己重写 get/set 方法,在 .m 文件中实现即可。
· 方法前面,"-" 表示是实例方法,即由实例调用;"+" 表示是类方法,即 .net 中类的静态方法。
· OC 中的 id 类型,弱类型,类似 js 里的 var,硬要跟 .net 挂钩的话,很像 dynamic,但貌似又跟 dynamic 有所不同,请原谅我蹩脚的表达能力。。。
· self 关键字,即 .net 中的 this 关键字;super 关键字,即 .net 中的 base 关键字。
· block,也就是 .net 中的匿名函数,定义顺序是:返回类型、"^"关键字、方法名(若声明位置是方法参数,可省略)、参数列表,例如:
// 返回类型为空 // 方法名为 TEST1 // 参数列表为空 void (^TEST1)(void) = ^ { NSLog(@"TEST1"); }; TEST1(); // 返回类型为 NSString * // 方法名为 TEST2 // 需传入一个 NSInteger 类型的参数 NSString *(^TEST2)(NSInteger) = ^(NSInteger i) { NSLog(@"TEST2 parameter: %i", i); return @"test2"; }; NSString *result = TEST2(10); NSLog(@"%@", result); // TEST3 方法定义了一个 block 参数,该 block 无返回值,需传入一个 NSString 类型的参数 - (void)TEST3:(void (^)(NSString * parameter))block { NSLog(@"TEST3"); // 调用 block,并传入参数 block(@"TEST3 parameter"); } // 调用 TEST3,传入 block [self TEST3:^(NSString *parameter) { NSLog(@"parameter: %@", parameter); }]; |
用法同 .net 的无异,只是语法不同。
· category,扩展,即 .net 中的类扩展,例如 public static bool IsValidEmailAddress(this string str);。OC 中,只允许扩展方法,写法:
/** NSString+Category.h 文件命名方法,OC 建议是原类名+扩展名 **/ // 声明时,原类名后加一个括号,括号里为扩展名,表示这是一个扩展 @interface NSString (Category) // 扩展一个方法,判断该字符串是否为 Email 地址 - (BOOL)isValidEmailAddress; @end /** NSString+Category.m **/ @implementation NSString (Category) // 实现方法 // 请忽略具体方法,这里只做演示 - (BOOL)isValidEmailAddress { return [self length] % 2 == 0; } @end // 使用时,引入 .h 文件,即可像普通类方法一样使用 NSString *str1 = @"1"; NSString *str2 = @"11"; NSLog(@"%i", [str1 isValidEmailAddress]); NSLog(@"%i", [str2 isValidEmailAddress]); |
· delegate,协议,注意,这里并不同 .net 中的 delegate,.net 中的 delegate 应该更类似 OC 中的 block。这里的 delegate 更像是 .net 中的接口。用我自己的理解来说,就是我有时候会触发某个事件,而你如果需要知道我何时触发了事件,那么我们之间就必须有一种 "约定协议",即当我触发事件时,通知你。而你收到我的通知后,要干嘛,那是你的自由了。
这里所说的 "约定协议",即 OC 中的 delegate。我要通知你,即使用 delegate 中的方法。你只需要实现这个方法,我就可以通知到你。这个在下一篇再进行详细说明。
OC 的一些基础的东西就差不多了,接下来就是结合到界面的开发,形式很像 .net 的 winform 开发,iOS 中使用 MVC 框架,所以如果你 .net 会这两样的话,还是比较容易看得懂的。
做一款像样的 App,需要各种岗位的同事帮忙,从需求,到设计、编码、测试、上线,确实考验一个团队的合作能力。
1 脏读: 一个事务可以读取另一个事务未提交的数据
2 不可重复读: 一个事务连续两次执行某个读操作,返回的结果不一致,被修改了
3 虚读 :一个事务连续两次执行某个读操作,返回的结果不一致,返回的纪录数目有改变
不可重复读 与 虚读 的区别:
不可重复读,强调的是,第二次返回的结果中,某个条目被修改过,比如某些字段被修改
虚读,强调的是,第二次返回结果中,属于第一次返回结果的条目没有任何变化,但是返回条目的数目会变化
数据库事务隔离级别
1 read uncommitted 脏读的那个级别
2 read committed 解决脏读问题,提交了才能被其它事务读到 (大多数数据库的默认事务等级)
3 repeatable read 可重复读,如何实现:规定一个事务不能修改被其它事务读但是未提交的事务
4 serializable 最高的事务隔离级别 如何实现:所有的事务都串行执行
repeatable read是如何实现?
规定一个事务不能修改其它事务读但是未提交的事务
这样就保证了一个事务中第二次读到的数据都是没有被修改过得
为什么repeatable read隔离级别,虚读问题没有被解决掉?
只对修改作了限制,如果两次读之间进行了插入或者删除操作,满足没有修改其它事务读但未提交的事务,结果,两次读的结果还是会不一致,所以虚读问题没有被解决
虚读问题解决,serializable隔离级别?
串行化执行,很容易理解,我在一个事务中,执行两次读操作,在此期间,没有其它事务在执行,必须等我执行完以后才会被执行,因为是串行,大家排队,所以结果不可能不一致。