zoukankan      html  css  js  c++  java
  • 应用程序首选项(application preference)及数据存储

    应用程序首选项(application preference)用来存储用户设置,考虑以下案例:

    a. 假设有一款MP3播放器程序,当用户调节了音量,当下次运行该程序时,可能希望保持上一次调节的音量值。

    b. 一款游戏的难易度设置。

    c. Twitter等社交程序的用户名和密码设置。

    iOS应用程序存储信息的方式很多,但主要有如下3种:

    1. 单例类NSUserDefaults:NSUserDefaults类的工作原理类似于NSDirectionary,所有首选项都以键/值对的方式存储在NSUserDefaults单例中。

    2. 设置束(settings bundle):提供了一个通过iOS应用程序Settings对应用程序进行配置的接口。

    3. 直接访问文件系统:能够读取属于当前应用程序的iOS文件系统部分的文件。

    单例类NSUserDefaults

    先获取NSUserDefaults单例的引用:

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    然后便可以读写默认设置数据库了,写入值必须使用6个函数之一:setBool:forKey、setFloat:forKey、setInteger:forKey、setObject:forKey、setDouble:forKey、setURL:forKey。函数setObject:forKey可用于存储NSString、NSDate、NSArray以及其他常见的对象类型。例如:

    [userDefaults setInteger:18 forKey:@"age"];
    [userDefaults setObject:@"Tom" forKey:@"name"];

    将数据写入NSUserDefaults时,并不一定会立即保存这些数据。为确保所有数据都写入了NSUserDefaults,可使用方法synchronize:

    [userDefaults synchronize];

    可以根据键读取值:

    NSString *name = [userDefaults stringForKey:@"name"];
    NSInteger *age = [userDefaults integerForKey:@"age"];

    不同于写入值只提供了6个特有的函数,读取值提供了许多用于特定类型的方法,可以轻松地将存储的对象赋给特定类型的变量。根据要读取的数据类型,选择arrayForKey、boolForKey、dataforKey、dictionaryForKey、floatForKey、integerForKey、objectForKey、stringArrayForKey、doubleForKey或URLForKey。

    操作NSUserDefaults实际上是对一个plist文件进行了编辑,在设备上运行程序时,plist将存储在设备中;但如果在模拟器中运行程序,模拟器将使用计算机硬盘来存储plist文件,存储路径为:/Users/<your username>/Library/Application Support/iPhone Simulator/<Device OS Version>/Applications/<your project folder>/Library/Preferences/com.yourcompany.projectname.plist。

    P.s.1 在Xcode中测试时,如果需要在模拟器中利用iOS任务管理器结束正在开发的应用程序,需要先把Xcode调试停止掉(Stop),否则Xcode会抛异常。

    P.s.2 在Lion以上的OS要访问/Users/<your username>/Library,可以在"前往"菜单按住"Option键",就会多出来一个"资源库(Library)"的选项。

    设置束(settings bundle)

    设置束也是对一个plist文件进行编辑,它的优点在于,可以通过Xcode plist编辑器来操作,无需额外编写代码,只需要在编辑器里定义要存储的数据及其键即可。

    默认情况下应用程序没有设置束,可选择菜单File->New File->Resource->Settings Bundle来添加。这里有两个细节需要注意:(1) 最好是将文件建立在Supporting Files文件夹下,这样比较规范。(2)虽然新建的时候可以修改Bundle的名称,但强烈建议使用默认名称"Settings",因为在我多次测试过程中,发现使用了非默认名称后,设置束无法生效(或是无法显示)的情况。

    新建成功后,可以看到一个Settings.bundle的目录,它包含一个en.lproj文件夹和一个Root.plist文件;其中en.lproj文件夹又包含有一个Root.strings的文件,这个主要是用于多语言化的。最主要的是这个Root.plist,它包含了一个设置列表,我们主要针对这个文件进行修改与自定义。

    P.s 新建一个plist的方法比较特殊,无法直接在Xcode里完成,一个替代的方法是:先打开Settings.bundle所在目录,然后右键"显示包内容",然后在Finder里进行复制粘贴。

    设置束中的文件Root.plist决定了应用程序首选项如何出现在应用程序Settings中。有7种类型的首选项,分别为:

    Group -- 编组。键为PSGroupSpecifier,首选项逻辑编组的标题。
    Text Field -- 文本框。键为PSTextFieldSpecifier,可编辑的文本字符串。
    Title -- 标题。键为PSTitleValueSpecifier,只读文本字符串。
    Toggle Switch -- 开关。键为PSToggleSwitchSpecifier,开关按钮。
    Slide -- 滑块。键为PSSliderSpecifier,取值位于特定范围内的滑块。
    Multivalue -- 多值。键为PSMultiValueSpecifier,下拉式列表。
    Child Pane -- 子窗格。键为PSChildPaneSpecifier,子首选项页。

    一些类型的特定属性说明:

    Text Field

    Text Field is Secure -- 是否为安全文本。如果设置为YES,则内容以圆点符号出现。

    Autocapitalization Style -- 自动大写。有四个值: None(无)、Sentences(句子首字母大写)、Words(单词首字母大写)、All Characters(所有字母大写)。

    Autocorrection Style -- 自动纠正拼写,如果开启,你输入一个不存在的单词,系统会划红线提示。有三个值:Default(默认)、No Autocorrection(不自动纠正)、Autocorrection(自动纠正)。

    Keyboard Type -- 键盘样式。有五个值:Alphabet(字母表,默认)、Numbers and Punctuation(数字和标点符号)、Number Pad(数字面板)、URL(比Alphabet多出了.com等域名后缀)、Email Address(比Alphabet多出了@符合)。

    Toggle Switch

    Value for ON -- 当开关置为ON时,取得的字符串值。

    Value for OFF -- 当开关置为OFF时,取得的字符串值。

    Slider

    Minimum Value -- 最小值,Number类型。

    Maximum Value -- 最大值,Number类型。

    Min Value Image Filename -- 最小值那一端的图片。

    Max Value Image Filename -- 最大值那一端的图片。

    P.s.图片大小必须为21*21,并且要放在Settings.bundle包内(在Finder里显示包内容,然后粘贴)。

    Multivalue

    Values -- 值的集合。

    Titles -- 标题的集合,与值一一对应。

    Child Pane

    Filename -- 子plist的文件名。

    设置束只是以一种可视化的方式来操作NSUserDefaults,所以取值的方式也完全相同,需要特别注意的是:当用户运行程序前,假如根本就没有通过设置束修改过任何值,这样即使设置了项的默认值,也会返回null,解决方法是在初始化代码里调用registerDefaults进行注册。

    - (void)viewDidLoad
    {
        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"无名氏",@"nickname",@"beijing",@"city",nil];
        [userDefaults registerDefaults:dict];
        [userDefaults synchronize];
        
        NSString *nickName = [userDefaults stringForKey:@"nickname"];
        NSString *city = [userDefaults stringForKey:@"city"];
        self.lblNickName.text = [NSString stringWithFormat:@"nickname is %@", nickName];
        self.lblCity.text = [NSString stringWithFormat:@"city is %@", city];
    
        [super viewDidLoad];
    }

    还有另外一种更智能的方法,就是在AppDelegate.m的didFinishLaunchingWithOptions方法里读取项的DefaultValue,然后根据这个来赋值;但是如果没有设置DefaultValue,则会产生一个插入nil值的异常。

    - (void)registerDefaultsFromSettingsBundle 
    {  
        NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];  
        if(!settingsBundle) {  
            NSLog(@"Could not find Settings.bundle");  
            return;  
        }  
        
        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];  
        NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];  
        
        NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];  
        for(NSDictionary *prefSpecification in preferences) {  
            NSString *key = [prefSpecification objectForKey:@"Key"];  
            if(key) {  
                [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];  
            }  
        }  
        [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
    }
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [self registerDefaultsFromSettingsBundle];
        return YES;
    }

    直接访问文件系统

    存储基本步骤是:获取Documents路径-->在路径下创建一个可读写文件-->利用NSFileHandle编辑文件内容

    - (void)storeData
    {
        NSString *strSave = @"Here is my private data";
        
        NSString *strDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        
        NSString *strFile = [strDir stringByAppendingPathComponent:@"mydata.csv"];
        
        if(![[NSFileManager defaultManager] fileExistsAtPath:strFile])
        {
            [[NSFileManager defaultManager] createFileAtPath:strFile contents:nil attributes:nil];
        }
        
        NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:strFile];
        [fileHandle seekToEndOfFile];
        //[fileHandle truncateFileAtOffset:0]; //清空内容
        [fileHandle writeData:[strSave dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
    }

    读取基本步骤是:获取Documents路径-->访问可读写文件-->利用NSFileHandle读取文件内容获取Documents路径

    - (void)readData
    {
        NSString *strDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        
        NSString *strFile = [strDir stringByAppendingPathComponent:@"mydata.csv"];
        
        if([[NSFileManager defaultManager] fileExistsAtPath:strFile])
        {
            NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:strFile];
            NSString *strData = [[NSString alloc] initWithData:[fileHandle availableData] encoding:NSUTF8StringEncoding];
            [fileHandle closeFile];
            NSLog(@"%@",strData);
        }
    }

    一些概念性的东西:

    iPhone会为每一个应用程序生成一个私有目录,这个目录位于/Users/*your user name*/Library/Application Support/iPhone Simulator/5.0/Applications,并随机生成一个数字+字母的目录名,在每一次应用程序启动时,这个目录名都会随机变化。

    在程序的目录下有三个常用的文件夹:Documents、Caches和tmp。

    • Documents:应用中用户数据可以放在这里,iTunes备份和恢复的时候会包括此目录
    • tmp:存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除
    • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除

    Foundation框架中的NSSearchPathForDirectoriesInDomains函数用于取得几个应用程序相关目录的全路径。在iOS上使用这个函数时,第一个参数NSSearchPathDirectory用于指定搜索路径常量,第二个参数NSSearchPathDomainMask使用NSUserDomainMask常量,第三个参数BOOL expandTilde表示是否展开成完整路径。

    常用的搜索路径常量:

    NSDocumentDirectory -- <Application_Home>/Documents

    NSCachesDirectory -- <Application_Home>/Library/Caches

    NSApplicationSupportDirectory -- <Application_Home>/Library/Application Support

    所有的NSUserDomainMask常量:

    NSUserDomainMask = 1, //用户主目录中

    NSLocalDomainMask = 2, //当前机器中

    NSNetworkDomainMask = 4, //网络中可见的主机

    NSSystemDomainMask = 8, //系统目录,不可修改(/System)

    NSAllDomainsMask = 0x0ffff //全部

    P.S. 可以在Xcode点击NSSearchPathDirectoryNSSearchPathDomainMask链接到定义类,查看所有的枚举值及部分注释。

    通常使用Documents目录进行持久化数据的保存,而这个Documents目录可以通过NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserdomainMask,YES) 得到,这个方法返回的是一个数组,因为NSSearchPathForDirectoriesInDomains函数最初是为Mac OS X设计的,而Mac OS X上可能存在多个这样的目录,所以它的返回值是一个路径数组,而不是单一的路径。在iOS上,结果数组应该只包含一个给定目录的路径,所以可以通过取索引0来直接获取:

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSLog(@"%@",documentsDirectory);
    //输出:/Users/wayne/Library/Application Support/iPhone Simulator/5.0/Applications/96CFD458-361A-4D7E-82A7-DA6699DC0F96/Documents

    也可以使用NSHomeDirectory函数返回顶级Home目录的路径--也就是包含应用程序、Documents、Library和tmp目录的路径:

    NSString *homePath = NSHomeDirectory();
    NSLog(@"%@",homePath);
    //输出:/Users/wayne/Library/Application Support/iPhone Simulator/5.0/Applications/96CFD458-361A-4D7E-82A7-DA6699DC0F96

    文件管理类NSFileManager常用操作:

    创建一个文件管理器

    NSFileManager *fm = [NSFileManager defaultManager];

    浅度遍历目录

    - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error

    深度遍历目录

    - (NSArray *)subpathsOfDirectoryAtPath:(NSString *)path error:(NSError **)error

    获取当前目录

    - (NSString *)currentDirectoryPath

    更改当前目录

    - (BOOL)changeCurrentDirectoryPath:(NSString *)path

    枚举目录内容

    - (NSDirectoryEnumerator *)enumeratorAtPath:(NSString *)path

    创建目录

    - (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error

    创建文件

    - (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)contents attributes:(NSDictionary *)attributes

    复制文件

    - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error

    删除文件

    - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error

    目录/文件拷贝

    - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error

    移动/重命名文件或者目录

    - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error

    测试文件是否存在

    - (BOOL)fileExistsAtPath:(NSString *)path

    获取文件信息(属性和权限)

    - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error

    从文件中读取数据

    - (NSData *)contentsAtPath:(NSString *)path

    比较两个文件的内容

    - (BOOL)contentsEqualAtPath:(NSString *)path1 andPath:(NSString *)path2

    测试文件是否存在,且是否能执行读操作

    - (BOOL)isReadableFileAtPath:(NSString *)path

    测试文件是否存在,且是否能执行写操作

    - (BOOL)isWritableFileAtPath:(NSString *)path

    文件操作类NSFileHandle常用操作:

    只读方式打开文件

    + (id)fileHandleForReadingAtPath:(NSString *)path

    只写方式打开文件

    + (id)fileHandleForWritingAtPath:(NSString *)path

    读写方式打开文件

    + (id)fileHandleForUpdatingAtPath:(NSString *)path

    从文件当前位置读到结尾

    - (NSData *)readDataToEndOfFile

    从文件当前位置读固定字节数的内容

    - (NSData *)readDataOfLength:(NSUInteger)length

    返回所有可用的数据

    - (NSData *)availableData

    写文件

    - (void)writeData:(NSData *)data

    定位到文件尾部

    - (unsigned long long)seekToEndOfFile

    定位到文件指定位置

    - (void)seekToFileOffset:(unsigned long long)offset

    获取当前文件的偏移量

    - (unsigned long long)offsetInFile

    将文件的长度设置为offset字节

    - (void)truncateFileAtOffset:(unsigned long long)offset

    关闭文件

    - (void)closeFile

    P.S. (网络socket中)通过initWithFileDescriptor初始化的对象,需要显式调用此方法;其它方法创建的对象会自动打开文件,该对象被销毁时会自动关闭该方法,不需显式调用此方法。

    =======================================================================

    以下摘自网络,待整理:

    下面举个例子介绍下NSFileHandle的具体用法
        NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:PATH];//以只读方式打开文件生成文件句柄
        NSData *data = [fh readDataOfLength:3]; //从文件读取指定字节的内容
        data = [fh readDataOfLength:5];//从上次读取的位置往后再读5个字节
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    如果文件内容不长的话,可以一次性读到结尾。
      
        NSData *data = [fh readDataToEndOfFile];//一次性的把文件中的内容全读出来
    
    以上介绍的是如何读取文件下面来介绍如何写入文件
    
     NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:PATH];//以只写方式打开文件生成文件句柄
    
    [fh writeData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];//这将替换PATH目录下文件的前五个字节剩下的内容不变
     如果我们想把文件清空再重新写入该怎么办呢?
    
    [fh truncateFileAtOffset:0];//将文件内容截断至0字节 这样就会把内容清空
    
    
    那么我们能不能保持原文件内容不变往后面追加内容呢,也是可以的
    [fh seekToEndOfFile]; //我们先把读写指针设在文件的尾端
    [fh writeData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
    writeToFile 也用于文件的写入它和NSFileHandle有什么区别呢
    
    [@"" writeToFile:PATH atomically:YES encoding:NSUTF8StringEncoding error:nil];
    writeToFile往文件里面写数据,都是覆盖式写入的 atomically的YES 或 NO YES 表示保证文件的写入原子性,就是说会先创建一个临时文件,直到文件内容写入成功再导入到目标文件里。 NO 则直接写入目标文件里。
    如果要采用追回式的文件写入,也就是不覆盖原文件的内容可以采用NSFileHandle
    Part1
    常用路径工具函数
    NSString * NSUserName(); 返回当前用户的登录名 
    NSString * NSFullUserName(); 返回当前用户的完整用户名 
    NSString * NSHomeDirectory(); 返回当前用户主目录的路径 
    NSString * NSHomeDirectoryForUser(); 返回用户user的主目录 
    NSString * NSTemporaryDirectory(); 返回可用于创建临时文件的路径目录 
     
    常用路径工具方法
    -(NSString *) pathWithComponents:components                         根据components中元素构造有效路径 
    -(NSArray *)pathComponents                                          析构路径,获取路径的各个部分 
    -(NSString *)lastPathComponent                                       提取路径的最后一个组成部分 
    -(NSString *)pathExtension                                           路径扩展名 
    -(NSString *)stringByAppendingPathComponent:path                    将path添加到现有路径末尾 
    -(NSString *)stringByAppendingPathExtension:ext                     将拓展名添加的路径最后一个组成部分 
    -(NSString *)stringByDeletingPathComponent                           删除路径的最后一个部分 
    -(NSString *)stringByDeletingPathExtension                           删除路径的最后一个部分 的扩展名 
    -(NSString *)stringByExpandingTildeInPath                            将路径中的代字符扩展成用户主目录(~)或指定用户主目录(~user) 
    -(NSString *)stringByResolvingSymlinksInPath                         尝试解析路径中的符号链接 
    -(NSString *)stringByStandardizingPath                               通过尝试解析~、..、.、和符号链接来标准化路径 
    - 
    使用路径NSPathUtilities.h 
    tempdir = NSTemporaryDirectory(); 临时文件的目录名 
    path = [fm currentDirectoryPath];
    [path lastPathComponent]; 从路径中提取最后一个文件名 
    fullpath = [path stringByAppendingPathComponent:fname];将文件名附加到路劲的末尾 
    extenson = [fullpath pathExtension]; 路径名的文件扩展名 
    homedir = NSHomeDirectory();用户的主目录 
    component = [homedir pathComponents];  路径的每个部分 
     
    NSProcessInfo类:允许你设置或检索正在运行的应用程序的各种类型信息
    (NSProcessInfo *)processInfo                                  返回当前进程的信息
    -(NSArray*)arguments                                           以NSString对象数字的形式返回当前进程的参数
    -(NSDictionary *)environment                                   返回变量/值对词典。描述当前的环境变量
    -(int)processIdentity                                          返回进程标识
    -(NSString *)processName                                       返回进程名称
    -(NSString *)globallyUniqueString                              每次调用该方法都会返回不同的单值字符串,可以用这个字符串生成单值临时文件名   
    -(NSString *)hostname                                          返回主机系统的名称 
    -(unsigned int)operatingSystem                                 返回表示操作系统的数字 
    -(NSString *)operatingSystemName                                     返回操作系统名称 
    -(NSString *)operatingSystemVersionString                                     返回操作系统当前版本
    -(void)setProcessName:(NSString *)name                                将当前进程名称设置为name 
    过滤数组中的文件类型  : [fileList pathsMatchingExtensions:[NSArrayarrayWithObject:@"jpg"]];
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    基本文件操作NSFileHandle
    常用NSFileHandle方法
    (NSFileHandle *)fileHandleForReadingAtPath:path                        打开一个文件准备读取
    (NSFileHandle *)fileHandleForWritingAtPath:path                        打开一个文件准备写入
    (NSFileHandle *)fileHandleForUpdatingAtPath:path                        打开一个文件准备更新(读取和写入)
    -(NSData *)availableData                                                  从设备或通道返回可用数据
    -(NSData *)readDataToEndOfFile                                            读取其余的数据直到文件末尾(最多UINT_MAX字节)
    -(NSData *)readDataOfLength:(unsigned int)bytes 从文件读取指定数目bytes的内容
    -(void)writeData:data                  将data写入文件
    -(unsigned long long) offsetInFile      获取当前文件的偏移量
    -(void)seekToFileOffset:offset         设置当前文件的偏移量 
    -(unsigned long long) seekToEndOfFile      将当前文件的偏移量定位的文件末尾
    -(void)truncateFileAtOffset:offset        将文件的长度设置为offset字节
    -(void)closeFile                           关闭文件
     
    获取文件大小 
     
    Using the C FILE type:
    int getFileSizeFromPath(char * path)
{
     FILE * file;
     int fileSizeBytes = 0;
     file = fopen(path,"r");
     if(file>0){
         fseek(file, 0, SEEK_END);
         fileSizeBytes = ftell(file);
         fseek(file, 0, SEEK_SET);
         fclose(file);
     }
     return fileSizeBytes;
}
    or in XCode use the NSFileManager:
    NSFileManager * filemanager = [[NSFileManager alloc]init];
if([filemanager fileExistsAtPath:[self getCompletePath] isDirectory:&isDirectory]){
         NSDictionary * attributes = [filemanager attributesOfItemAtPath:[self getCompletePath] error:nil];
    // file size
     NSNumber *theFileSize;
     if (theFileSize = [attributes objectForKey:NSFileSize])
           _fileSize= [theFileSize intValue];
}
    Part2
  • 相关阅读:
    用Visual C#创建Windows服务程序(转)
    输入字符串的格式不正确(异常详细信息: System.FormatException: 输入字符串的格式不正确。)
    pv操作二
    双进程struct sigaction中mask阻塞信号
    pv操作一
    sigprocmask
    共享内存二
    面向接口编程
    类之间的几种关系
    sigaction函数一
  • 原文地址:https://www.cnblogs.com/CoderWayne/p/3441898.html
Copyright © 2011-2022 走看看