gisergising
来源:互联网 发布:邪恶漫画软件大全下载 编辑:程序博客网 时间:2024/05/29 17:49
1 空间数据库的准确性研究
2 空间关系语言研究
3 空间数据的多种表达方式研究
4 地理信息的使用和价值研究
5 海量空间数据库的结构体系研究
6 空间决策支持系统
7 空间信息的可视化研究
8 地图制图的规范化研究
9 地理信息数据共享的研究
10 GIS中时空关系的研究
11 遥感和 GIS的集成研究
12 GIS的用户接口研究
13 GIS和空间分析研究
14 GIS在全球变化中的作用研究
15 法律、信息政策和空间数据库关系研究
16 通过协作形成空间决策系统的研究
17 在社会背景中 ,如何在 GIS中表达人、空间与环境的研究
18 地理信息系统的互操作研究
19 地理世界的规范化模式研究
最近学习php,以前用过sqlite一段时间,所以想用sqlite做数据库。不熟悉php。
//使用pdo建立数据库或连接
//下面的语句如果在路径中存在数据库则连接,否则建立数据库
$db = new PDO("sqlite:e:/phpeclipse/bbs/db1.db");
//下面的是处理开始标记,php文档上说一旦下了这个语句,下面出现commit()或者rollback()才执行,我们让它直接执行
//$db->beginTransaction();
//查询是否存在表test,不存在建立test表,否则直接进行对数据库操作
$query = $db->query("select name from sqlite_master where type = 'table' and name = 'test'");
if($query->fetch() === false)
{
$db->exec("CREATE TABLE test(
id INT USIGNED NOT NULL,
name CHAR(11),
grade int,
study CHAR(20),
PRIMARY KEY(id));");
//$db->commit();
}else{
//查询操作
$res = $db->query("select * from tg_user where tg_username = 'qyl'");
$rows = $res->fetchAll();
$count = count($rows);
echo $count;
//插入
$db->exec("INSERT INTO test(id,name,grade,study) VALUES (2111,'11','12312','234234')");
}
按上面的操作不会出现问题
如果我们已经有了一个数据库,只是连接上的话,用上面的方法就会出现问题。
我把这样建立好的数据库复制到另外一个路径下,然后进行同样的操作,就一直出错,连接不到数据库,或者说只能查询不能插入。
所以使用这种方法时,一定要使用$db = new PDO("sqlite:e:/phpeclipse/bbs/db1.db");新建个数据库,然后导入你现有的数据。
至于是什么原因,还望各位大神指导啊?
什么是PHP开发框架?什么时候应该使用PHP开发框架?为什么要使用PHP开发框架?选择哪个PHP开发框架? 本文为你细细道来。
原文链接:http://www.noupe.com/php/discussing-php-frameworks.html
什么是PHP开发框架?
有太多的原因使得PHP成为世界上最受欢迎的脚本语言——高度灵活、简单易用——不过写PHP(或者任何这种语言)代码时,人们常常陷入单调重复而令人生厌的体力活动的窘境。而这正是PHP开发框架大显身手的地方。
通过提供一个开发Web程序的基本架构,PHP开发框架把PHPWeb程序开发摆到了流水线上。换句话说,PHP开发框架有助于促进快速软件开发(RAD),这节约了你的时间,有助于创建更为稳定的程序,并减少开发者的重复编写代码的劳动。这些框架还通过确保正确的数据库操作以及只在表现层编程的方式帮助初学者创建稳定的程序。PHP开发框架使得你可以花更多的时间去创造真正的Web程序,而不是编写重复性的代码。
PHP开发框架背后的思想被称为“模型—视图—控制器”(MVC)。MVC是这样一种架构模式,它隔离了业务逻辑与UI,允许其一改变而另一者不受影响。(也可以说是关注点的隔离)在MVC中,模型负责数据,视图负责表现,控制器则是程序主体或者说是负责业务逻辑。从本质上说,MVC拆分了一个程序的开发过程,这样你就可以修改独立的每一部分,而其他部分不受影响,这是十分重要的,它使得编写PHP代码更为快捷简单。
为什么要使用PHP开发框架?
开发者可能出于不同的考虑而使用PHP开发框架,不过首当其冲的原因是为了加速开发过程。相似工程之间的代码重用能够节省开发者大量的时间和精力。PHP开发框架内置了预建的模块,免去了冗长又令人厌烦的编程工作。这样开发者就能够把时间活在开发实际程序上,而不是每一次都要为每一个项目重建基础模块。
稳定性是开发者使用框架的另一个重要原因。尽管简单是PHP最大的资本,也是许多人喜爱这个脚本语言的原因,它也是PHP的“潘多拉之盒”,尤其是那些初学者而言,PHP是如此的简单以至于他们会完全没有意识地写出低质量的代码。这样的PHP程序可能在大多数时间内仍正常工作,但你可能已在代码中留下了巨大的安全漏洞,使其易受攻击。要时刻牢记PHP是一门很宽松的语言十分重要,因此确保不在代码中遗留任何安全漏洞是重中之重——即使程序看起来工作正常。
最后一点,PHP开发框架是可扩展的,并且有许多框架可供选择。你也可以创造你自己的,不过许多开发者决定从那些流行的知名的开发框架中做选择,因为它们往往有着庞大的支持团队,以及相关的论坛/社区方便你与其他使用同一个框架的开发者相互交流。注意,你应当事先检验你的项目是否需要使用框架,这里提供一份简单的列表以供参考:使用框架能否节省你(和其他任何会使用它的人)的时间和精力?是否能够让程序得到更好的表现?能否提高稳定性?如果你对上面任何一个问题的回答是肯定的,那么使用PHP开发框架对于这个项目就可能是正确的选择。
何时使用PHP开发框架?
这是开发者,无论初学者还是经验丰富的专家,常常询问的问题,但又真的没有直截了当的答案。对于许多初学者而言,框架提供了良好的稳定性和简洁性,所以他们认为只要能用就应该用框架。它能够减少低质量代码的产生,并加快项目进度。
另一方面,许多经验丰富的PHP程序员把框架视为那些不懂得如何写出高质量的清晰的代码的“初级”程序员而设计的工具。这句话对错与否有待商榷,但事实上PHP开发框架的确是用来节省时间和提高编程效率的工具。
在忙于接近最后期限的工程时,使用PHP开发框架将带来巨大的效益,因为它能够极大地加快编程进度。所以如果你时间紧迫,使用PHP开发框架是极其正确的。应当把PHP开发框架列入考虑范围的另一个例子是当你忙于一个有大量重复代码的项目时,因为它有助于减少重复工作量。
在PHP开发框架中应当关注什么?
去搜索PHP开发框架的人将会看到各式各样的选择,你甚至可以创建你自己的,尽管只推荐PHP专家们这么做。在寻找最适合你需求的PHP开发框架时,你要时刻想着谁会使用和/或彻头彻尾地修改它,这点很重要。如果会有许多人使用这个程序,最好用一个大多数开发者都熟悉的框架。另一方面,如果你想要一个自己使用的Web程序,你最好选择一个你觉得最舒服的PHP开发框架——管它是流行还是不受欢迎呢。
在寻找PHP开发框架时应该考虑多种因素:简单易用,敏捷开发/性能,在其它开发者中间的流行程度,强大的特性,以及支持/论坛。我推荐你先尝试几款不同的PHP开发框架,从中找出一个最适合你需求的,各种框架之间有些微的差异,有着不同的优势和劣势。就拿Zend Framework来说,V3版本之后就带有了丰富的功能,再加上一个可扩展的支持系统,这一切都是因为它存在了足够久。作为对照,CakePHP是另一个比Zend Framework年轻的PHP开发框架,它的支持系统的规模就略微小些(尽管对这个框架的支持正在飞速增长),但CakePHP更加用户友好且简单易用。
如你所见,每种PHP开发框架都有它自己的有点,所以最好先做一些尝试,捕捉一些错误以便找出哪个框架能最好地服务于你的需求。另一种很好的选择框架的方式是咨询你那些身处开发者社区的同事,问问他们喜欢哪个。那些真正用过某个框架的人会告诉你它是否易用,特性,可用的支持,框架周边的社区所涉及的领域,他们还可能告诉你它的缺点。
使用PHP开发框架时最常见的错误
任何一种类型的编程都会出现错误,不过PHP开发框架能够极大地限制这些错误的产生,因为它从开发过程的一开始就提供了经过验证的正确代码。而且看来重复的代码编写会增加错误出现的概率,而框架彻底解决或至少减少了那个问题。
这就是说,使用PHP开发框架仍然又需要注意的问题。例如,除非你是个PHP专家,否则你应该选择一个流行些的框架,它们有着丰富的支持和一定的活跃用户基数(下面会列举一些流行的PHP开发框架)。有许多框架只有少得可怜的支持甚至没有支持,并且/或者是由对PHP了解有限的人创建的。这些框架会导致你的程序不能正常工作,更坏的情况下会导致你的网站出现灾难性的安全问题。
另一种常见的错误是没有确保数据库和网站服务器兼容特定的框架。例如Seagull PHP Framewor推荐如下配置:
PHP: PHP 4.3.0 是最低配置,之后的版本也支持,PHP 5.1.1 及更高版本也可以。请避免任何 5.0.x 系列的东西。
MySQL: MySQL 4.0.x, 4.1.x 和 5.0.x 都支持,你也可以使用 3.23.x。
Apache: Seagull 在 1.3.x and 2.x 系列的 Apache 中工作得很好。
如果你不能满足这些要求,你就不能得到所选择框架可能的最佳性能。就算你是PHP专家,也应该在尝试框架之前阅读其文档以确保兼容性。
和上面这个错误比较类似的,不按照推荐安装步骤去安装你的PHP开发框架也会给你带来令人头痛的烦恼。仍然用Seagull作为例子——Seagull的Wiki上有一份详细的框架安装流程,其中有几个关键步骤有时候常常被粗心或者没有戒心的开发者轻易地忽略了。关键是你一次就把框架装好会让你在接下来的实际开发中节约不必要的时间开销。
现在最好用的PHP开发框架有哪些?
近几年来,随着PHP作为一门被许多开发者选作他们的脚本语言而不断演化,PHP开发框架如雨后春笋般走上荧屏。人们总是在争吵到底哪个PHP开发框架最好,但不是每个框架都是为所有人而产生的,这本来是很简单的事实。这里列举五个最好的最受欢迎的PHP开发框架:
The Zend Framework
The Zend Framework 在开发社区中有大量的追随者,它致力于Web 2.0风格的程序。因为它众多的追随者组成的活跃用户开发的扩展支持,Zend被称为“The PHP Company”。Zend是一个,也可能不是,今天可用的最受欢迎的PHP开发框架。它有健壮的特征能够支持协作开发,它需要一些PHP的额外知识。
CakePHP
CakePHP 无论对于初学者还是职业PHP开发者都是很好的选择。它基于与Ruby on Rails同样的原则而设计,它十分注重快速开发——这使得它成为一个非常好的用于RAD的开发框架。它快速增长的支持系统,简洁性和可测量性使得CakePHP成为今天可用的最受欢迎的PHP开发框架之一。
Symfony
Symfony 是为那些更加职业的主要开发企业级程序的开发者而准备的——特别是 Askeet 和 Yahoo! Bookmarks。这款开源PHP开发框架有着丰富的特性,做起这些来绰绰有余,它主要的缺点是比其他的框架略微慢一些。
Codelgniter
Codelgniter 以它的简单易用,性能和速度而闻名。与 Symphony 不同,这款PHP开发框架对于共享主机的用户而言很理想,如果你想有一个小型覆盖面的框架。它提供了简单的解决方案,还有扩展的知识库,通过视频教程,用户指南和wiki来提供支持。
初学者应当考虑CodeIgniter。
Seagull
Seagull 是一个良好的PHP开发框架,可以用来开发Web,命令行和GUI程序。这是一款无论对初学者还是职业程序员而言都及其简单的开发框架。对于初学者,Seagull提供一套包含样例程序的库,经过简单修改就能符合你的要求,而对于专家级程序员,Seagull提供了一整套设置选项——包括最佳练习,标准的和模块化的代码库——来快速简单地构建Web程序。Seagull有一个活跃的用户社区以及丰富的支持文档。
总结
PHP开发框架对于各种能力层级的开发者都是一种减少重复编码需要的良好方式,同时加快开发过程,确保创建Web程序时写了正确的代码。这不仅加速了复杂程序的开发,同时也减少了你代码中的安全漏洞从而加强了PHP的安全性。
尽管一些专家级PHP程序员感觉没必要使用开发框架开发Web程序,它们在快速软件开发中仍然有巨大优势,比如在紧迫的期限下。而对于初学者或中等开发者,开发框架能够提高PHP学习效率同时促进练习编写高质量的代码且减少低质量代码,后者因为PHP自身的“宽松”特性而常常出现。
今天有许多可以使用的PHP开发框架,因此开发者肯定能找到一款满足他们在特性,支持,速度,可测度性等等方面的需求的框架。如今被开发者使用的顶级PHP开发框架包括:The Zend Framework,CakePHP,Symfony,Codelgniter和Seagull。
[翻译:AquarHEAD,文章来源]
为何?
http://www.cnblogs.com/lovemo1314/archive/2010/11/08/1871781.html
收集一篇python中shell操作解释地比较全面的文章,我主要需要用到shel中的cp命令和rm命令
===========华丽丽分割线,转载内容如下============
os.chkdir(path) 转换到目录path 下。
os.system('md a') 可以直接创建目录。
os.name字符串指示你正在使用的平台。比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'。
● os.getcwd()函数得到当前工作目录,即当前Python脚本工作的目录路径。
● os.getenv()和os.putenv()函数分别用来读取和设置环境变量。
● os.listdir()返回指定目录下的所有文件和目录名。
● os.remove()函数用来删除一个文件。
● os.system()函数用来运行shell命令。
● os.linesep字符串给出当前平台使用的行终止符。例如,Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'。
● os.path.split()函数返回一个路径的目录名和文件名。
>>> os.path.split('/home/swaroop/byte/code/poem.txt')
('/home/swaroop/byte/code', 'poem.txt')
● os.path.isfile()和os.path.isdir()函数分别检验给出的路径是一个文件还是目录。类似地,os.path.exists()函数用来检验给出的路径是否真地存在。
文件重定向
已有PY文件new1.py ,在命令行下输入:new1>new.txt 可以将new1运行的结果输出到文件new.txt,这称为流重定向。
python可以做shell脚本吗? 首先介绍一个函数:
os.system(command)
这个函数可以调用shell运行命令行command并且返回它的返回值。试一下在python的解释器里输入os.system(”ls -l”),就可以看到”ls”列出了当前目录下的文件。可以说,通过这个函数,python就拥有了shell的所有能力。呵呵。。不过,通常这条命令不 需要用到。因为shell常用的那些命令在python中通常有对应而且同样简洁的写法。
shell中最常用的是ls命令,python对应的写法是:os.listdir(dirname),这个函数返回字符串列表,里面是所有的文件名,不过不包含”.”和”..”。如果要遍历整个目录的话就会比较复杂一点。我们等下再说吧。先在解释器里试一下:
>>> os.listdir(”/”)
[’tmp’, ‘misc’, ‘opt’, ‘root’, ‘.autorelabel’, ’sbin’, ’srv’, ‘.autofsck’, ‘mnt’, ‘usr’, ‘var’, ‘etc’, ’selinux’, ‘lib’, ‘net’, ‘lost+found’, ’sys’, ‘media’, ‘dev’, ‘proc’, ‘boot’, ‘home’, ‘bin’]
就像这样,接下去所有命令都可以在python的解释器里直接运行观看结果。
对应于cp命令的是:shutil.copy(src,dest),这个函数有两个参数,参数src是指源文件的名字,参数dest则是目标文件或 者目标目录的名字。 如果dest是一个目录名,就会在那个目录下创建一个相同名字的文件。与shutil.copy函数相类似的是 shutil.copy2(src,dest),不过copy2还会复制最后存取时间和最后更新时间。
不过,shell的cp命令还可以复制目录,python的shutil.copy却不行,第一个参数只能是一个文件。这怎么办?其 实,python还有个shutil.copytree(src,dst[,symlinks]) 。参数多了一个symlinks,它是一个布尔值,如果是True的话就创建符号链接。
移动或者重命名文件和目录呢?估计被聪明的朋友猜到了,shutil.move(src,dst),呵呵。。与mv命令类似,如果src和dst在 同一个文件系统上,shutil.move只是简单改一下名字,如果src和dst在不同的文件系统上,shutil.move会先把src复制到 dst,然后删除src文件。看到现在,大多数朋友应该已经对python的能力有点眉目了,接下来我就列个表,介绍一下其它的函数:
os.chdir(dirname)
把当前工作目录切换到dirname下
os.getcwd()
返回当前的工作目录路径
os.chroot(dirname)
把dirname作为进程的根目录。和*nix下的chroot命令类似
os.chmod(path,mode)
更改path的权限位。mode可以是以下值(使用or)的组合:
os.S_ISUID
os.S_ISGID
os.S_ENFMT
os.S_ISVTX
os.S_IREAD
os.S_IWRITE
os.S_IEXEC
os.S_IRWXU
os.S_IRUSR
os.S_IWUSR
os.S_IXUSR
os.S_IRWXG
os.S_IRGRP
os.S_IWGRP
os.S_IXGRP
os.S_IRWXO
os.S_IROTH
os.S_IWOTH
os.S_IXOTH
具体它们是什么含义,就不仔细说了,基本上就是R代表读,W代表写,X代表执行权限。USR代表用户,GRP代表组,OTH代表其它。
os.chown(path,uid,gid)
改变文件的属主。uid和gid为-1的时候不改变原来的属主。
os.link(src,dst)
创建硬连接
os.mkdir(path,[mode])
创建目录。mode的意义参见os.chmod(),默认是0777
os.makedirs(path,[mode])
和os.mkdir()类似,不过会先创建不存在的父目录。
os.readlink(path)
返回path这个符号链接所指向的路径
os.remove(path)
删除文件,不能用于删除目录
os.rmdir(path)
删除文件夹,不能用于删除文件
removedirs(path)
递归移除目录。类似于rmdir()的工作,除了如果子目录被成功地删除,removedirs()尝试接连地删除每个在path中提及的父目录,直到一个错误被挂起(这个错误被忽略,因为它通常意味父目录不为空。)例如,"os.removedirs(‘foo/bar/baz')"将首先删除"'foo/bar/baz'"目录,然后如果 "'foo/bar'"和"'foo'"为空删除它们。如果子目录不能被成功地删除挂起OSError。1.5.2版本中的新方法。
rename(src, dst)
重命名文件或目录src为dst。如果dst是一个目录,OSError将被挂起。在Unix上,如果dst存在并且是一个文件,如果用户有权限它将被默默地删除。在一些Unix风格的系统上如果src和dst是不同的文件系统,这个操作可能失败。如果成功,重命名将是一个基本的操作(这是一个POSIX要求)。在Windows上,如果dst已经存在,甚至如果它是一个文件,OSError将被挂起;当dst命名一个已存在的文件时没有办法执行基本的重命名。
renames(old, new)
递归重命名目录或文件函数。类似于rename()的工作,除了所有中间层目录的创建第一次试图需要有效的新的路径名。重命名后,目录符合老的名称最右边路径部分将被用removedirs()删除。1.5.2版本中的新方法。
注意:如果你缺乏需要删除子目录或文件的权限,使用新的目录结构重命名时这个函数会失败。
readlink(path)
返回一个代表符号连接点指向的路径的字符串。结果可以是绝对或相对路径名的其中之一;如果是相对,它可以用os.path.join(os.path.dirname(path), result)转换成一个绝对路径。可用:Macintosh, Unix。
stat(path)
在给定的路径上执行stat()系统调用。返回值是一个对象,它的属性对应stat的结构数,即:st_mode(保护块),st_ino(索引节点数), st_dev(设备),st_nlink(硬连接数),st_uid(所有者的用户ID),st_gid(所有者的组ID),st_size(文件大小,用字节),st_atime(当前访问的时间),st_mtime(当前内容修改的时间),st_ctime(平台依赖,在Unix上当前元数据改变的时间,在Windows上创建的时间):
>>> import os
>>> statinfo = os.stat('somefile.txt')
>>> statinfo
(33188, 422511L, 769L, 1, 1032, 100, 926L, 1105022698,1105022732, 1105022732)
>>> statinfo.st_size
926L
os.symlink(src,dst)
创建符号链接
tempnam([dir[, prefix]])
为创建一个临时文件合理的返回一个唯一的路径名。这将是一个绝对路径路径,以dir目录中可能的目录项命名,或是一个通常的临时文件的位置,如果dir被忽略或为None。如果给定和不为None,prefix被用来给文件名提供一个简短的前缀。应用负责使用由tempnam()返回的路径恰当地创建和管理文件;不提供自动清除。在Unix上,环境变量TMPDIR覆盖dir,在Windows上TMP被使用。这个函数的指定行为依赖于C库的执行;some aspects are underspecified in system documentation.注意:tempnam()的用法是危险的对于符号连接攻击;考虑用tmpfile()(14.1.2节)替代。可用: Macintosh, Unix, Windows。
tmpnam()
为创建一个临时文件合理的返回一个唯一的路径名。这将是一个绝对路径路径,以一个通常的临时文件的位置中可能的目录项命名,应用负责使用由tempnam ()返回的路径恰当地创建和管理文件;不提供自动清除。注意:tempnam()的用法是危险的对于符号连接攻击;考虑用tmpfile() (14.1.2节)替代。可用:Macintosh, Unix, Windows。这个函数大概在Windows上不被使用,不过:微软tmpnam()的实现一直创建在当前驱动的根目录中创建一个名字,它通常是一个临时文件粗略的位置(依赖于特权,你甚至用这个名称不能打开一个文件)。
TMP_MAX
再使用这些名称之前tmpnam()将生成的唯一的名称的最大数目。
unlink(path)
删除文件path。同remove()相同;unlink()名称是它的传统的Unix名称。可用:Macintosh, Unix, Windows。
utime(path, times)
设置由path指定的文件的访问和修改时间。如果times是None,那么文件的访问和修改时间被设置为当前的时间。否则,times必须是一个二元组数,它的被用来设置访问和修改时间的格式分别是(atime, mtime)。目录能否由path指定依赖于操作系统是否作为一个文件执行目录(如,Windows就不是)。注意你这儿设置的精确的时间不能通过后来的 stat()调用返回,依赖于正式的你的操作系统纪录的访问和修改时间;参见stat()。2.0版本中的改变:增加times为None的支持。可用: Macintosh, Unix, Windows。
walk(top[, topdown=True [, onerror=None]]),它os.listdir()明显的区别就是,它能进行纵深遍历,os.listdir()只能遍历当前目录里的所有子目录和文件。
在目录树中walk()生成文件名,通过由上而下或右下而上遍历树。这个树中每个目录的根在目录top(包含top自身),它给出一个三元组(dirpath, dirnames, filenames)。
dirpath 是一个目录的路径字符串。dirnames是一个dirpath的子目录的名称的列表(排除'.'和'..')。filenames是一个dirpath 中没有目录的文件的名称的列表。注意列表中的名称包含无路径的组件。获取dirpath中文件和目录的完整路径(以top开始),用 os.path.join(dirpath, name)。
如果可选参数topdown为true或没有指定,它的子目录三倍生成之前三倍的目录被生成(目录的生成从上而下)。如果topdown为false,三倍的目录被生成在它的子目录三倍生成之后(目录生成由下而上)。
当topdown 为true,调用者能修改dirnames列表的原状(可能用del或slice任务),walk()仅将递归名称仍然在dirnames中的子目录;这能被用来删除查找,利用指定的顺序访问,或者重新walk()之前告诉walk()关于调用者创建或重命名的目录。当topdown为false时修改 dirnames是无效的,因为在自底向上的模式中dirnames中的目录在dirpath生成之前被生成。
os.listdir()调用缺省的错误被忽略。如果可选参数onerror被指定,它应该是一个函数;它将被调用用一个参数,一个OSError实例。它能用walk继续报告错误,或忽略walk挂起异常。注意文件名是可用的作为异常对象的文件名属性。
注意:如果你传递一个相对的路径名,在重新开始和walk()之间不改变当前的工作目录。walk()从不改变当前的目录,并且假定它的调用者也不改变。
注意:在支持符号连接的系统上,连接到出现在dirnames列表中的子目录,但是walk()将不访问它们(当跟随符号连接时避免无穷大的循环是困难的)。访问连接的目录,你能用os.path.islink(path)来识别它们,并且各自直接调用walk(path)。
这个例子显示开始目录下的每个目录中非目录的文件的字节数,除了不查找任意CVS子目录下的:
import os
from os.path import join, getsize
for root, dirs, files in os.walk('python/Lib/email'):
print root, "consumes",
print sum(getsize(join(root, name)) for name in files),
print "bytes in", len(files), "non-directory files"
if 'CVS' in dirs:
dirs.remove('CVS') # don't visit CVS directories
下面的例子中,从下到上遍历树等于:rmdir()目录为空之前不允许删除目录:
# Delete everything reachable from the directory named in 'top',
# assuming there are no symbolic links.
# CAUTION: This is dangerous! For example, if top == '/', it
# could delete all your disk files.
import os
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
New in version 2.3.
shutil.rmtree(path[,ignore_errors[,onerror]])
删除文件夹
介绍了这么多,其实只要查一下os和shutil两个模块的文档就有了,呵呵。。真正编写shell脚本的时候还需要注意:
1.环境变量。python的环境变量保存在os.environ这个字典里,可以用普通字典的方法修改它,使用system启动其它程序的时候会自动被继承。比如:
os.environ[”fish”]=”nothing”
不过也要注意,环境变量的值只能是字符串。和shell有些不同的是,python没有export环境变量这个概念。为什么没有呢?因为python没有必要有:-)
2.os.path这个模块里包含了很多关于路径名处理的函数。在shell里路径名处理好像不是很重要,但是在python里经常需要用到。最常用的两个是分离和合并目录名和文件名:
os.path.split(path) -> (dirname,basename)
这个函数会把一个路径分离为两部分,比如:os.path.split(”/foo/bar.dat”)会返回(”/foo”,”bar.dat”)
os.path.join(dirname,basename)
这个函数会把目录名和文件名组合成一个完整的路径名,比如:os.path.join(”/foo”,”bar.dat”)会返回”/foo /bar.dat”。这个函数和os.path.split()刚好相反。
还有这些函数:
os.path.commonprefix(list)
返回list中,所有path共有的最长的路径。
如:
>>> os.path.commonprefix(['/home/td','/home/td/ff','/home/td/fff'])
'/home/td'
os.path.lexists(path)
与os.path.exists(path)的不同是如果有损坏的链接会返回True
os.path.basename('/foo/bar.dat')
>>>bar.dat
os.path.dirname('/foo/bar.dat')
>>>/foo
os.path.realpath(path)
返回path的真实路径,去除符号链接
os.path.relpath(path[, start])
返回一个“相关路径”,当前目录或者可选的start
Return a relative filepath to path either from the current directory or from an optional start point.
如:
>>> os.path.relpath('/home/jimin','/usr/lib/')
'http://www.cnblogs.com/home/jimin'
os.path.samefile(path1, path2)
如果path1与path2是相同的文件或目录,返回真
os.path.sameopenfile(fp1, fp2)
如果fp1和fp2指向的是同一个文件,返回True
os.path.samestat(stat1, stat2)
如果 stat tuple stat1和stat2指向同一个文件,返回真。stat tuple结构是由fstat()、lstat()、stat()产生的
os.path.abspath(path)
把path转成绝对路径,相当于normpath(join(os.getcwd(), path))
os.path.expanduser(path)
把path中包含的”~”和”~user”转换成用户目录
os.path.expandvars(path)
接受环境变理的扩展,path中可以使用环境变量
如:
>>> os.path.expandvars('$PATH')
'/usr/lib64/qt-3.3/bin:/usr/kerberos/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local
/sbin:/usr/sbin:/sbin:/home/jimin/bin'
>>> os.path.expandvars('$HOME')
'/home/jimin'
os.path.expandvars(path)
根据环境变量的值替换path中包含的”$name”和”${name}”,比如环境变量FISH=nothing,那 os.path.expandvars(”$FISH/abc”)会返回”nothing/abc”
os.path.normpath(path)
去掉path中包含的”.”和”..”
os.path.splitext(path)
把path分离成基本名和扩展名。比如:os.path.splitext(”/foo/bar.tar.bz2″)返回(’/foo /bar.tar’, ‘.bz2′)。要注意它和os.path.split()的区别
3.在os模块有一个很好用的函数叫os.stat()没有介绍,因为os.path模块里包含了一组和它具有同样功能的函数,但是名字更好记一点。
os.path.exists(path)
判断文件或者目录是否存在
os.path.isfile(path)
判断path所指向的是否是一个普通文件,而不是目录
os.path.isdir(path)
判断path所指向的是否是一个目录,而不是普通文件
os.path.islink(path)
判断path所指向的是否是一个符号链接
os.path.ismount(path)
判断path所指向的是否是一个挂接点(mount point)
os.path.getatime(path)
返回path所指向的文件或者目录的最后存取时间。
os.path.getmtime(path)
返回path所指向的文件或者目录的最后修改时间
os.path.getctime(path)
返回path所指向的文件的创建时间
os.path.getsize(path)
返回path所指向的文件的大小
4.应用python编写shell脚本经常要用到os,shutil,glob(正则表达式的文件名),tempfile(临时文 件),pwd(操作/etc/passwd文件),grp(操作/etc/group文件),commands(取得一个命令的输出)。前面两个已经基本 上介绍完了,后面几个很简单,看一下文档就可以了。
5.sys.argv是一个列表,保存了python程序的命令行参数。其中sys.argv[0]是程序本身的名字。
不能光说不练,接下来我们就编写一个用于复制文件的简单脚本。前两天叫我写脚本的同事有个几万个文件的目录,他想复制这些文件到其它的目录,又不能 直接复制目录本身。他试了一下”cp src/* dest/”结果报了一个命令行太长的错误,让我帮他写一个脚本。操起python来:
import sys,os.path,shutil
for f in os.listdir(sys.argv[1]):
shutil.copy(os.path.join(sys.argv[1],f),sys.argv[2])
再试一下linuxapp版里的帖子——把一个文件夹下的所有文件重命名成10001~10999。可以这样写:
import os.path,sys
dirname=sys.argv[1]
i=10001
for f in os.listdir(dirname):
src=os.path.join(dirname,f)
if os.path.isdir(src):
continue
os.rename(src,str(i))
i+=1
pickle的序列化规则
http://blog.163.com/qiongling007@126/blog/static/214242962011110193074/
Python规范(Python-specific)提供了pickle的序列化规则。这就不必担心不同版本的Python之间序列化兼容性问题。默认情况下,pickle的序列化是基于文本的,我们可以直接用文本编辑器查看序列化的文本。我们也可以序列成二进制格式的数据,这样的结果体积会更小。更详细的内容,可以参考Python手册pickle模块。
下面就开始使用pickle吧~
pickle.dump(obj, file[, protocol])
序列化对象,并将结果数据流写入到文件对象中。参数protocol是序列化模式,默认值为0,表示以文本的形式序列化。protocol的值还可以是1或2,表示以二进制的形式序列化。
pickle.load(file)
反序列化对象。将文件中的数据解析为一个Python对象。下面通过一个简单的例子来演示上面两个方法的使用:
注意:在反序列化的时候,必须能找到对应类的定义,否则反序列化将失败。在上面的例子中,如果取消#del Person的注释,在运行时将抛AttributeError异常,提示当前模块找不到Person的定义。
pickle.dumps(obj[, protocol])
pickle.loads(string)
我们也可以直接获取序列化后的数据流,或者直接从数据流反序列化。方法dumps与loads就完成这样的功能。dumps返回序列化后的数据流,loads返回的序列化生成的对象。
python模块中还定义了两个类,分别用来序列化、反序列化对象。
class pickle.Pickler(file[, protocal]):
该类用于序列化对象。参数file是一个类文件对象(file-like object),用于保存序列化结果。可选参数表示序列化模式。它定义了两个方法:
dump(obj):
将对象序列化,并保存到类文件对象中。参数obj是要序列化的对象。
clear_memo()
清空pickler的“备忘”。使用Pickler实例在序列化对象的时候,它会“记住”已经被序列化的对象引用,所以对同一对象多次调用dump(obj),pickler不会“傻傻”的去多次序列化。下面是一个简单的例子:
class pickle.Unpickler(file):
该类用于反序列化对象。参数file是一个类文件(file-like object)对象,Unpickler从该参数中获取数据进行反序列化。
load():
反序列化对象。该方法会根据已经序列化的数据流,自动选择合适的反序列化模式。
上面介绍了pickle模块的基本使用,但和marshal一样,并不是所有的类型都可以通过pickle序列化的。例如对于一个嵌套的类型,使用pickle序列化就失败。例如:
关于pickle支持的序列化类型,可以参考Python手册。
Python手册中的pickle模块,介绍了更高级的主题,例如自定义序列化过程。有时间再和大家分享。
方法一:
max = 6
index = [0]*max
from time import clock
start=clock()
while True:
candi = range(0,max)
for i in range(1,max+1):
print candi.pop(index[-i]),
print ''
index[1] += 1
i = 1
while i < max-1 and index[i] > i:
index[i] = 0
index[i+1] += 1
i+=1
if index[-1] >= max:
break
finish=clock()
print (finish-start)
方法二:
def myprint(a,offset,needPrint):
if needPrint:
print a
if len(a) == 1:
return
for t in range(offset,len(a)):
te = a.pop(len(a)-1)
a.insert(0,te)
myprint(a,offset+1,t != len(a) -1)
from time import clock
start=clock()
a = [0,1,2,3,4,5]
myprint(a,0,True)
finish=clock()
print (finish-start)
方法一是从网上找的非递归的方法,使用的是统计学的方法,方法二是递归调用方法,如果方法二的换位置自己写的话,用时差不多,如果使用上述方法写的话,大家看看时间吧:
当你生成、画、编辑、选取和分析基于矢量地理数据时arcobject的geometry类是核心。
这些任务依赖于平面几何,像“点”和“线”这样的概念代表是平面几何要素的集合类,像“相交”和“距离”这样的概念是上述集合类的方法和属性。
几何用于Arcobject模型的其他方面,比如说空间数据库、空间引用、标志和编辑工作。熟悉这样方面有助于你理解arcobject的geometry类。
接下来的几节将介绍geometry 模型中各个类。首先介绍像几何属性和几何要素这样的常识概念,每个概念将在以后的章节中深入介绍。
一、geometry的组件类(coclass)
geometry组件类是代表笛卡尔几何中被认为是“top level”的几何要素,这些组件类作为一个特征类型来组成几何体。这些顶级类包括:
点(point):x、y平面的一个位置
多点(multipoint):点的有限集
包围体(envelope):是个举行的区域。通常用来为其他几何体提供近似的包围区域。一个给定的点要不是在envelope内要么是在envelope外。
聚合线(polyline):是一个有顺序的点的集合,使用段(segment)将一个点与下一个点连接。例如:一条线是两个点和之间的线段(line)组成。一个弧是两个点和中间的段(椭圆弧或者贝塞尔曲线)。一条聚合线的不同段的类型可以不一样。一条polyline的不同段之间可能有间隙,任意给定点要么在polyline上要么不在。
一个多边形(Polygon)是一个平面的一部分,给定的点可以在内、外或者在边上
一个几何体包(geometryBag)组件类不被认为是顶级类,他是任意支持IGeometry接口的类的集合。
尽管上述的讨论都是基于二维平面的几何体,arcobject实现了另外一些几何体包括2.5维或者3维的几何体,这些内容将在本章的后面介绍。
你可以使用openUrl打开一些程序,不只是浏览器,我们将在下面的例子中演示这些这些应用:
- 打开浏览器
- 打开google map
- 打开email
- 拨号程序
- 发短信程序
- 打开appstore
Launch Google Maps
到googlemap的URL格式是:
http://maps.google.com/maps?q=${QUERY_STRING}
你可以更改QUERY_STRING改变位置信息:
NSString* searchQuery = @"the postion I want to know";
searchQuery = [searchQuery stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", searchQuery]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString: urlString]];
打开Apple Mail
格式:
mailto://${EMAIL_ADDRESS}
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://info@iphonedevelopertips.com"]];
拨打电话(iPhone Only)
格式:
tel://${PHONE_NUMBER}
1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://8004664411"]];
发短信
格式
sms:${PHONENUMBER_OR_SHORTCODE}
1
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:55555"]];
打开app store
打开appstore的程序位置,右键点击程序图标获取url
格式如下:
http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&mt=8
12
NSURL *appStoreUrl = [NSURL URLWithString:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&mt=8"];[[UIApplication sharedApplication] openURL:appStoreUrl];
iPhone/Mac Objective-C内存管理教程和原理剖析
此文版权归作者Vince Yuan (vince.yuan#gmail.com)所有。欢迎非营利性转载,转载时必须包含原始链接http://vinceyuan.cnblogs.com/,且必须包含此版权声明的完整内容。
版本 1.1 发表于2010-03-08
前言
初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理机制的研究成果和经验,写了这么一个由浅入深的教程。希望对大家有所帮助,也欢迎大家一起探讨。
此文涉及的内存管理是针对于继承于NSObject的Class。
一 基本原理
Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。
1 Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。
ClassA *obj1 = [[ClassA alloc] init];
2 Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。
[obj1 dealloc];
这带来了一个问题。下面代码中obj2是否需要调用dealloc?
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //输出hello
[obj1 dealloc];
[obj2 hello]; //能够执行这一行和下一行吗?
[obj2 dealloc];
不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。
如何避免无效指针?请看下一条。
3 Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。
ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1
[obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁
我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 0,对象被销毁
[obj2 hello];
[obj2 release];
[obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。
4 Objective-C指针赋值时,retain count不会自动增加,需要手动retain。
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 0,对象被销毁
问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)
这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。
5 Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)
5.1 新生成的对象,只要调用autorelease就行了,无需再调用release!
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release
5.2 对于存在指针赋值的情况,代码与前面类似。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
//对于obj1,无需调用(实际上不能调用)release
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 2-1 = 1
细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。
6 autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)
6.1 autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6.2 NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中
6.3 NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。
6.4 默认只有一个autorelease pool,通常类似于下面这个例子。
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
// do something
[pool release];
return (0);
} // main
所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
}
[pool release];
return (0);
} // main
(可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。
7 Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
for (j = 0; j < 100000; j++ )
[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
[loopPool release];
}
[pool release];
return (0);
} // main
(可以参考附件中的示例程序memman-many-objs-many-pools.m,占用内存的变化极小)
二 口诀与范式
1 口诀。
1.1 谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。
1.2 除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。
1.3 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。
2 范式。
范式就是模板,就是依葫芦画瓢。由于不同人有不同的理解和习惯,我总结的范式不一定适合所有人,但我能保证照着这样做不会出问题。
2.1 创建一个对象。
ClassA *obj1 = [[ClassA alloc] init];
2.2 创建一个autorelease的对象。
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
2.3 Release一个对象后,立即把指针清空。(顺便说一句,release一个空指针是合法的,但不会发生任何事情)
[obj1 release];
obj1 = nil;
2.4 指针赋值给另一个指针。
ClassA *obj2 = obj1;
[obj2 retain];
//do something
[obj2 release];
obj2 = nil;
2.5 在一个函数中创建并返回对象,需要把这个对象设置为autorelease
ClassA *Func1()
{
ClassA *obj = [[[ClassA alloc]init]autorelease];
return obj;
}
2.6 在子类的dealloc方法中调用基类的dealloc方法
-(void) dealloc
{
…
[super dealloc];
}
2.7 在一个class中创建和使用property。
2.7.1 声明一个成员变量。
ClassB *objB;
2.7.2 声明property,加上retain参数。
@property (retain) ClassB* objB;
2.7.3 定义property。(property的默认实现请看第三章)
@synthesize objB;
2.7.4 除了dealloc方法以外,始终用.操作符的方式来调用property。
self.objB 或者objA.objB
2.7.5 在dealloc方法中release这个成员变量。
[objB release];
示例代码如下(详细代码请参考附件中的memman-property.m,你需要特别留意对象是在何时被销毁的。):
@interface ClassA : NSObject
{
ClassB* objB;
}
@property (retain) ClassB* objB;
@end
@implementation ClassA
@synthesize objB;
-(void) dealloc
{
[objB release];
[super dealloc];
}
@end
2.7.6 给这个property赋值时,有手动release和autorelease两种方式。
void funcNoAutorelease()
{
ClassB *objB1 = [[ClassB alloc]init];
ClassA *objA = [[ClassA alloc]init];
objA.objB = objB1;
[objB1 release];
[objA release];
}
void funcAutorelease()
{
ClassB *objB1 = [[[ClassB alloc]init] autorelease];
ClassA *objA = [[[ClassA alloc]init] autorelease];
objA.objB = objB1;
}
三 @property (retain)和@synthesize的默认实现
在这里解释一下@property (retain) ClassB* objB;和@synthesize objB;背后到底发生了什么(retain property的默认实现)。property实际上是getter和setter,针对有retain参数的property,背后的实现如下(请参考附件中的memman-getter-setter.m,你会发现,结果和memman-property.m一样):
@interface ClassA : NSObject
{
ClassB *objB;
}
-(ClassB *) getObjB;
-(void) setObjB:(ClassB *) value;
@end
@implementation ClassA
-(ClassB*) getObjB
{
return objB;
}
-(void) setObjB:(ClassB*) value
{
if (objB != value)
{
[objB release];
objB = [value retain];
}
}
在setObjB中,如果新设定的值和原值不同的话,必须要把原值对象release一次,这样才能保证retain count是正确的。
由于我们在class内部retain了一次(虽然是默认实现的),所以我们要在dealloc方法中release这个成员变量。
-(void) dealloc
{
[objB release];
[super dealloc];
}
四 系统自动创建新的autorelease pool
在生成新的Run Loop的时候,系统会自动创建新的autorelease pool(非常感谢网友hhyytt和neogui的提醒)。注意,此处不同于xcode在新建项目时自动生成的代码中加入的autorelease pool,xcode生成的代码可以被删除,但系统自动创建的新的autorelease pool是无法删除的(对于无Garbage Collection的环境来说)。Objective-C没有给出实现代码,官方文档也没有说明,但我们可以通过小程序来证明。
在这个小程序中,我们先生成了一个autorelease pool,然后生成一个autorelease的ClassA的实例,再在一个新的run loop中生成一个autorelease的ClassB的对象(注意,我们并没有手动在新run loop中生成autorelease pool)。精简的示例代码如下,详细代码请见附件中的memman-run-loop-with-pool.m。
int main(int argc, char**argv)
{
NSLog(@”create an autorelasePool/n”);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@”create an instance of ClassA and autorelease/n”);
ClassA *obj1 = [[[ClassA alloc] init] autorelease];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@”releasing autorelasePool/n”);
[pool release];
NSLog(@”autorelasePool is released/n”);
return 0;
}
输出如下:
create an autorelasePool
create an instance of ClassA and autorelease
create an instance of ClassB and autorelease
ClassB destroyed
releasing autorelasePool
ClassA destroyed
autorelasePool is released
注意在我们销毁autorelease pool之前,ClassB的autorelease实例就已经被销毁了。
有人可能会说,这并不能说明新的run loop自动生成了一个新的autorelease pool,说不定还只是用了老的autorelease pool,只不过后来drain了一次而已。我们可以在main函数中不生成autorelease pool。精简的示例代码如下,详细代码请见附件中的memman-run-loop-without-pool.m。
int main(int argc, char**argv)
{
NSLog(@”No autorelasePool created/n”);
NSLog(@”create an instance of ClassA/n”);
ClassA *obj1 = [[ClassA alloc] init];
NSDate *now = [[NSDate alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:now
interval:0.0
target:obj1
selector:@selector(createClassB)
userInfo:nil
repeats:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
[now release];
[runLoop run]; //在新loop中调用一函数,生成ClassB的autorelease实例
NSLog(@”Manually release the instance of ClassA/n”);
[obj1 release];
return 0;
}
输出如下:
No autorelasePool created
create an instance of ClassA
create an instance of ClassB and autorelease
ClassB destroyed
Manually release the instance of ClassA
ClassA destroyed
我们可以看出来,我们并没有创建任何autorelease pool,可是ClassB的实例依然被自动销毁了,这说明新的run loop自动创建了一个autorelease pool,这个pool在新的run loop结束的时候会销毁自己(并自动release所包含的对象)。
补充说明
在研究retain count的时候,我不建议用NSString。因为在下面的语句中,
NSString *str1 = @”constant string”;
str1的retain count是个很大的数字。Objective-C对常量字符串做了特殊处理。
当然,如果你这样创建NSString,得到的retain count依然为1
NSString *str2 = [NSString stringWithFormat:@”123”];
涉及的示例程序代码(卖个关子,回复可见呦,其实我博客上也有):
http://files.cnblogs.com/VinceYuan/objective-c-memman.zip
地图从http://thematicmapping.org/downloads/world_borders.php下载,使用vpn下载。
确保安装好python和gdal
import os
os.chdir("Pah/to/thefile/world.shp")
import osgeo.ogr
shapefile = ogr.Open("world.shp")
layer = shapefile.GetLayer(0)
for i in range(layer.GetFeatureCount())
feature = layer.GetFeature(i)
countryName = feature.GetField("NAME")
countryCode = feature.GetField("ISO3")
geometry = feature.GetGeometryRef()
minlong,maxlong,minLat,maxLat = geometry.GetEnvelope()
print countryName,countryCode,minlong,maxlong,minLat,maxLat