zoukankan      html  css  js  c++  java
  • iOS应用开发之Persistence持久化[转]

    持久化(Persistence)

    持久化(Persistence)意思就是当你退出app的时候它还会存在。NSUserDefaults就是一个非常简单的持久化方案,不过这有限制,它只能是很小的东西,通常是些用户选项。

    如何把那些大数据的东西持久化?

    第一个方法,把东西持久化的第一个简单的方式有点像用NSUserDefaults里的property list来实现的进化版,property list是我们自定义的一个概念,是NSArray、NSDictionary、NSNumber、NSString、 NSDate和NSData的组合。所有以上这些都有API可以用来保存,NSUserDefaults也有些API可以。

    NSData、NSArray和NSDictionary里还有很重要的方法writeToURL:atomically:,这个方法就是把一个array写到一个文件系统中URL表示的地方,atomically就是如果文件已经存在,它会把文件移到一边,然后写到一个新的文件,关掉这个新的文件。实际上就是写到一个临时命名的文件,然后关掉,把另一个移走,有点原子化操作的感觉,不能写到一半就停下来。writeToURL之后,为了把东西取回来,可以用initWithContentsOfURL或者dataWithContentsOfURL。这些读写方法,不管是发送到NSData或者NSArray或者NSDictionary,就是想让一个相同类型的对象去读回来,它的内部可以就是些property list的东西。

    还有另一个类叫做NSPropertyListSerialization,它所做的事情就是把property list转化成NSData,反之亦然。这样它可以将property list转化成一堆二进制数,然后就可以写到磁盘上,或储存到网络。也可以通过网络读取一堆二进制数再通过NSPropertyListSerialization把它们转化回property list。

    关于存储的另外一个方法就是通过对象的映像图(arbitrary graphs)来进行归档对象(Archiving Objects)的保存。

    把东西存储到文件系统当中。

    然后是SQLite

    关于持久化存储的重头部分就是Core Data,这是在SQL上层的一个面向对象的数据库机制。

    Archiving

    在做对象图归档的时候有很多陷阱,归档很适合做循环图。Archiving能发现指针实际所指的对象或者指针相互指着,然后当unarchives的时候又恢复那个指针。但你要考虑清楚来使你构建的对象图有意义,最好的例子可能是在xcode里面建立的view层级。

    当从对象库中拖一些东西到屏幕上,就是在实例化UIView,实例化UIViewController,它们都是generic的,所以才需要去inspector面板来改变它们的类。

    基本上就是在这个图里面的对象都必须实现NSCoding这个protocol,然后这个protocol里面有两个重要方法:

    - (void)encodeWithCoder:(NSCoder *)coder; 
    - initWithCoder:(NSCoder *)coder;

    第一个是把自己放进archive,第二是把你从archive中取出来用的。这就是viewController出现在storyboard时不用调用它们的指定初始化的原因,因为它们用了这套初始化。这套归档系统在对它们做alloc initWithCoder,因为它们之前用encodeWithCoder把自己保存起来了。在UIView里没有调用initWithFrame,而是用frame对应的encodeWithCoder和initWithCoder来代替。

    - (void)encodeWithCoder:(NSCoder *)coder { 
         [super encodeWithCoder:coder]; 
         [coder encodeFloat:scale forKey:@“scale”]; 
         [coder encodeCGPoint:origin forKey:@“origin”]; 
         [coder encodeObject:expression forKey:@“expression”];
    }

    必须保证initWithCoder和encodeWithCoder是相对应的。

    - initWithCoder:(NSCoder *)coder { 
         self = [super initWithCoder:coder]; 
         scale = [coder decodeFloatForKey:@“scale”]; 
         expression = [coder decodeObjectForKey:@“expression”]; 
         origin=[coderdecodeCGPointForKey:@“origin”]; //notethatorderdoesnotmatter
    }

    怎么来实现这些呢?NSKeyedArchiver中的类方法:

    + (NSData *)archivedDataWithRootObject:(id <NSCoder>)rootObject;

    可以传递一个根对象,比如在storyboard里面的根对象就可能是顶层的view controller。你要做的就是确保里面所有的对象都实现NSCoder协议。

    NSKeyedUnarchiver中的类方法:

    + (id <NSCoder>)unarchiveObjectWithData:(NSData *)data;

    这正好相反,你提供一个已经归档的NSData,然后返回被encode的根对象。

    id <NSCoder> object = ...; 
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object]; 
    id <NSCoder> dup = [NSKeyedArchiver unarchiveObjectWithData:data];

    如果有一个对象,通过第二行将得到关于这个对象的NSData。

    如果encode一个view使它的super view被归档,你会被拒绝,除非它处于正确的层级,如果它在顶部它会被拒绝,但如果在内部,就会执行。

    File System

    ios是基于Unix的,底层都是Unix的文件系统,这里有文件系统保护,不可能看到所有的东西,而且也不能随意的写入。

    只能在sandbox中做写入,为什么?为了安全。当在设备上删除app时,也会删除所有相关的数据。通过在sandbox中进行写操作,所有应用程序的东西不管是用户创建的还是应用程序自己在sandbox里面创建的一旦移除,所有的都会被移除。

    sandbox到底是什么?这里面有应用程序的bundle目录,应用程序不是单独的大的二进制文件,它实际上是一个目录,里面有可执行程序、二进制文件、storyboard及拖进来的图片,所有东西都在里面,这就是应用程序的bundle。sandbox里的目录本身是不可写的,不能在目录里写入东西。这基本上就是一个用xcode创建的app的只读副本。documents目录是sandbox中的一个重要目录,这些地方是用来存储那些被用户视为是自己的文档的。还有一个缓存目录,这里都是一些我们写出来的东西,用户一般不会认为这些是文档,而且这些文档的存在都很短暂,没有了也不会影响到用户。documentation里的关键东西是NSSearchPathDirectory。

    如何才能得到这些目录,如何得到这些目录的URL?如果想在application目录中写入,但又不能写入,那么要做的就是将application包里面的东西拷贝到sandbox中任意一个可写的地方然后在那里写入。如果你想将一个数据库连接到你的app,或写入那些数据库,得将它们拷贝出来,无论是拷到文档目录,或是缓存目录,总之是可以写入了。

    如何搞到这些目录的路径呢?使用这个方法:

    - (NSArray *)URLsForDirectory:(NSSearchPathDirectory)directory    
                        inDomains:(NSSearchPathDomainMask)domainMask; //NSUserDomainMask

    NSURL中有个API可以获得一个URL的清单,但得传入想要的目录类型,譬如文档目录、缓存目录。以上方法和NSURL的方法,它们返回一个路径的array,所以当我请求缓存目录时,将得到一个URL的array,或是路径字符串的数组。

    NSFileManager为文件系统提供实用操作,这不是用来读写它们自身文件的类。你可以用它来找出文件到底有多大,删除那些陈旧的需要被踢出的缓存文件,也可以用它来找出譬如当应用程序启动时,缓存里面都有哪些文件。它是线程安全的,只要不在两个不同的线程中使用同一个实例。

    NSString也有一些文件系统有关的东西,特别是制作路径,一般用NSURL来指定一个链接,有时会用字符串来构建URL。

    - (NSString *)stringByAppendingPathComponent:(NSString *)component;

    这可以在一个路径中添加内容。还可以把字符串的内容写到文件里去,必须要指定用哪种编码,譬如ASCII、ISOLatin1,

    - (BOOL)writeToFile:(NSString *)path
             atomically:(BOOL)flag 
               encoding:(NSStringEncoding)encoding //e.g.ASCII,ISOLatin1,etc.
                  error:(NSError **)error;

    也可以从文件读取string:

    - (NSString *)stringWithContentsOfFile:(NSString *)path
                              usedEncoding:(NSStringEncoding *)encoding 
    error:(NSError **)error;

    SQLite

    SQLite是指SQL文件保存在单一一个文件里,存储在一个单一的文件中,它速度很快,只占用很小的内存。它基于事务处理,是真正的SQL。这不是基于服务器的SQL,而是基于文件的,所以它是并发性的。如果app中有两个线程,都要写入到SQL数据库中,这可以正常运行。但它只是做简单的锁线程和并发。

    以下就是它的API:

    复制代码
    intsqlite3_open(constchar*filename,sqlite3**db); //get a database into db 
    int sqlite3_exec(sqlite3 *db,    // execute SQL statements
                     const char *sql, 
                     int (*callback)(void *, int, char **, char **), 
                     void *context, 
                     char **error);
    int mycallback(void *context,int count,char **values,char **cols); //data returned 
    int sqlite3_close(sqlite3 *db);    // close the database
    复制代码

    当你打开SQL文件时,你会得到一个SQL数据库指针,然后要执行SQL语句。向数据库递交你的SQL语句,一些SQL语句就会回调然后在一张表中将你请求的数据返回给你,返回的也可能是error。回调函数通常是这样的格式:

    int mycallback(void *context,int count,char **values,char **cols);

    然后就可以关闭了。你要做的就是第三行const char *sql,这是执行SQL语句的地方。

    from:http://www.cnblogs.com/geory/archive/2013/03/11/2953294.html

  • 相关阅读:
    python-函数(下):递归、高阶函数
    Spring Hystrix 原理与使用详解
    Jmeter 添加kafka支持
    dnspod-sr 高性能DNS 服务器软件
    Jupyter精选资源合集
    spring 跨域CORS Filter
    TensorFlow 基础准备指导
    TortoiseGit SSH-key 免用户名密码验证
    node.js、npm 升级操作详解
    Maven -DskipTests和-Dmaven.test.skip=true的区别
  • 原文地址:https://www.cnblogs.com/sunshine-anycall/p/3446606.html
Copyright © 2011-2022 走看看