2017-04-26 16:47:48 u014084081 阅读数 336

在iOS中保存数据有许多中方式,这里并不会介绍Core Data或者Realm

Saving Data in iOS

在Playground中,可把图片直接拖动到代码的编辑处,其类型就是UIImage,而把某一个文件拖动到代码的编辑处,其类型则是URL

FileManager

Swift中的FileManager的用法与OC中的用法基本一致。
例如,如下获取Document目录,使用的方法是 open func url(for directory: FileManager.SearchPathDirectory, in domain: FileManager.SearchPathDomainMask, appropriateFor url: URL?, create shouldCreate: Bool) throws -> URL

let documentDirectoryURL = try FileManager.default.url(
    for: .documentDirectory,
    in: .userDomainMask,
    appropriateFor: nil,
    create: false
)

FileManager.SearchPathDirectory而言,如下的几个会保存到iCloud

  • .documentDirectory
  • .libraryDirectory
  • .applicationSupportDirectory

.cachesDirectory不会保存到iCloud

一些基本的用法
1.copy到某一个地方

for resourceURL in resourcesURLs {
    let documentURL = documentDirectoryURL.appendingPathComponent(resourceURL.lastPathComponent)
    if !FileManager.default.fileExists(atPath: documentURL.path) {
        try FileManager.default.copyItem(at: resourceURL,to: documentURL
        )
    }
}

2.移动到某一个地方

try FileManager.default.moveItem(at: aliasURL,to:directoryURL.appendingPathComponent(aliasURL.lastPathComponent)

FileManagerDelegate

FileManagerDelegate协议定义了用于管理涉及复制,移动,链接或删除文件和目录的操作的可选方法。如果要使用FileManagerDelegate,就必须自己创建一个FileManager类的实例,而不能使用共享的实例。

如下使用FileManagerDelegate,指定删除时只可删除lastPathComponent中包含scene的文件

//代理
final class SceneDeletionDelegate: NSObject, FileManagerDelegate {
  func fileManager(_: FileManager,shouldRemoveItemAt url: URL) -> Bool {
    return url.lastPathComponent.contains("scene")
  }
}
......
//删除文件
let fileManager = FileManager()
let sceneDeletionDelegate = SceneDeletionDelegate()
fileManager.delegate = sceneDeletionDelegate
try fileManager.removeItem(at: url)

Data(NSData)

在Swift中使用UInt8表示一个8位的字节
Swift 3之前是使用NSData来代替Data
DataNSData的区别:Data是值类型,NSData是引用类型

如下先定义一个字符串let happyCats = "xxxx"

String

字符串的写入与读取

let stringURL = documentDirectoryURL.appendingPathComponent("String").appendingPathExtension("txt")
try happyCats.write(to: stringURL,atomically: true,encoding: .utf8)

try String(contentsOf: stringURL)

Data

data的读取与写入

//string to data
let stringData = happyCats.data(using: .utf8)!
//写入的url
let stringDataURL = URL(fileURLWithPath: "String Data",relativeTo: documentDirectoryURL).appendingPathExtension("txt")
try stringData.write(to: stringDataURL,options: [.atomic])
//转为字符串
try String(contentsOf: stringDataURL)

let savedStringData = try Data(contentsOf: stringDataURL)

String(bytes: savedStringData,encoding: .utf8)

UIImage

UIImage转为data,data转UIImage

let imageData = UIImagePNGRepresentation(#imageLiteral(resourceName: "Happy Cat Food.png"))!
let imageURL = documentDirectoryURL.appendingPathComponent("Happy Cat Food").appendingPathExtension("png")
try imageData.write(to: imageURL)

UIImage(contentsOfFile: imageURL.path)
let savedImageData = try Data(contentsOf: imageURL)
UIImage(data: savedImageData)

NSCoding

Swift中使用NSCoding的类,必须从NSObject派生
使用方式与OC基本一致,可参考NSCoding / NSKeyed​Archiver

如下的例子,实现NSCoding协议,实现对应的方法

//MARK: NSCoding
extension Sticker: NSCoding {
    public convenience init?(coder: NSCoder) {
        guard let name = coder.decodeObject(forKey: Key.name) as? String,
            let image = (coder.decodeObject(forKey: Key.image) as? Data).flatMap(UIImage.init)
            else {return nil}

        self.init(
            name: name,
            birthday: coder.decodeObject(forKey: Key.birthday) as? Date,
            normalizedPosition: coder.decodeCGPoint(forKey: Key.normalizedPosition),
            image: image
        )
    }

    public func encode(with coder: NSCoder) {
        coder.encode(name, forKey: Key.name)
        coder.encode(birthday, forKey: Key.birthday)
        coder.encode(normalizedPosition, forKey: Key.normalizedPosition)
        coder.encode(UIImagePNGRepresentation(image), forKey: Key.image)
    }
}

archive对象常用方法

class func archiveRootObject(_: Any, toFile: String) -> Bool
class func archivedData(withRootObject: Any) -> Data

如下,archiveSticker对象

let stickerURL = URL(fileURLWithPath: "Sticker",relativeTo: documentDirectoryURL).appendingPathExtension("plist")

guard NSKeyedArchiver.archiveRootObject(birtSticker,toFile: stickerURL.path) else {fatalError()}

在沙盒中可以看到归档的plist文件如下:

归档的plist文件

unarchive对象常用方法

class func unarchiveObject(withFile: String) -> Any?
class func unarchiveObject(with: Data) -> Any?

如下,解档上面的Sticker对象

let unarchivedSticker = NSKeyedUnarchiver.unarchiveObject(withFile: stickerURL.path) as! Sticker

Property List

属性文件支持的数据的格式

  • Bool
  • integer and floating-point numbers
  • String
  • Date
  • Data
  • Array
  • Dictionary<String, Any>

Property List有三种格式

  • OpenStep ASCII是只读的,就意味着你不能用它来保存数据、
  • XML没有OpenStep ASCII的限制,而且可编辑
  • Binary有更小的size

PropertyListSerialization提供了一些把property list转为其它格式或者把其它格式转为property list的方法

序列化
如下,把一个dictionary序列化为property list

let propertyListData = try PropertyListSerialization.data(fromPropertyList: sceneDictionary,format: .binary,options: .init())

let url = URL(fileURLWithPath: "Scene",relativeTo: documentDirectoryURL).appendingPathExtension("plist")

try propertyListData.write(to: url)

反序列化
可创建一个PropertyListSerialization的extension,返回的是一个元祖

public extension PropertyListSerialization {
  static func deserialize(_ data: Data) throws -> (propertyList: Any,format: PropertyListFormat) {
    var format: PropertyListFormat = .openStep

    return (propertyList: try propertyList(from: data,format: &format),format: format)
  }
}

如下,反序列化

let deserialized = try PropertyListSerialization.deserialize(try Data(contentsOf: url))

JSON

使用JSONSerialization来解析JSON数据

dictionary转为json data

  let url = URL(fileURLWithPath: "Scene",relativeTo: documentDirectoryURL).appendingPathExtension("json")

  let jsonData = try JSONSerialization.data(withJSONObject: sceneDictionary)

  try jsonData.write(to: url)

json datadictionary

  let jsonDictionary = try JSONSerialization.jsonObject(with: Data(contentsOf: url)) as! [String: Any]

需要注意一点的是如果字典key对应的value是UImage或者Date,如果要转为json,UImage或者Date要转换为字符串或者其他类型。
UIImagePNGRepresentation(image)!.base64EncodedString()date!.timeIntervalSinceReferenceDate

User Defaults

用法与OC基本一致

UserDefaults.standard.set(view.bounds.width, forKey: Key.width)
UserDefaults.standard.data(forKey: Key.background)
2015-05-05 15:59:03 GrowingGiant 阅读数 834

    应用又被拒绝了,原因是IOS Data Storage  

为了区分清楚sandbox里边各个目录的作用,我去看了下apple文档,sandbox目录介绍

总结下:

Documents:存放用户产生的数据,比如用户下载的视频图书,浏览记录等。但是对于位于Documents中可在生成或可重新下载的资源,必须标记为不能通过iTunes恢复的类型(NSURLIsExcludedFromBackupKey)(意思是说文件太大了,又是个动态的,不需要备份)。翻译的比较挫,看下官方的原话,自己体会下:

Remember that files in Documents/ and Application Support/ are backed up by default. You can exclude files from the backup by calling -[NSURL setResourceValue:forKey:error:] using the NSURLIsExcludedFromBackupKey key. Any file that can be re-created or downloaded must be excluded from the backup. This is particularly important for large media files. If your application downloads video or audio files, make sure they are not included in the backup.

具体方法:

//标记文件夹不可从iTunes恢复

            [[NSURL URLWithString:resource] setResourceValue:@(YES)

                                                                                    forKey:NSURLIsExcludedFromBackupKey

                                                         error:nil];



Library/Preferences:我们不需要自己创建文件进去,这个目录一般是程序使用NSUserDefaults存储数据的地方。支持iTunes恢复。


Library/Caches: 这个目录可以用来存储任何文件,包括用户的数据。IOS2.2之后caches就不在支持iTunes恢复,并且会在iTunes恢复时被清空,IOS5之后系统会在硬盘紧张时清空该目录。所以用来存储用户数据需要谨慎,最好是放到Documents。  


Tmp: 在程序运行的时候可用来存放临时文件,文件不再需要时需要自己手动移除,当然系统在程序没有运行时也会定期清空,不支持iTunes恢复。



PS:用到的同学请点个赞吧



2016-01-15 01:12:10 yang198907 阅读数 863


Code Data
  • Core Data 是iOS SDK 里的一个很强大的框架,允许程序员以面向对象的方式储存和管理数据。使用Core Data框架,程序员可以很轻松有效地通过面向对象的接口管理数据
  • Core Data是一种持久化技术,能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data 不仅是一个加载、保存数据的框架,它还能和内存中的数据很好的共事
        在数据操作过程中,无需编写任何 SQL 语句 Core Data 使用包括实体实体间关系,以及查找符合某些条件实体的请求等内容
       开发者可以在纯对象层上查找与管理这些数据,而不必担心存储和查找的实现细节


     有人说,Code Data性能不好,特别是跟FMDB比较起来!
     笔者亲自测试,插入10000数据,
          FMDB在笔者的模拟器上运行是0.193092682997958秒左右,
       Code Data 运行时间是: 0.171803秒左右
   存储数据的速度甚至比FMDB还要快! 

Core Data Stack 示意图
     Core Data stack 是 Core Data的核心,由一组 Core Data 核心对象组成
  • NSManagedObjectModel 被管理对象模型
  • NSPersistentStoreCoordinator 负责将数据保存到磁盘
  • NSManagedObjectContext 负责管理模型对象的集合



Code Data 的快速体验;
          使用Xcode自带的生成工具体验Code Data

1、新建项目:勾选 Use Core Data
2、创建实体:
  • 打开系统默认创建的 XXX.xcdatamodeld
  • 创建实体 - Entity类似与 SQLite 中的表,不过更像是 OC 中的类
  • 选择 Add Entity,建立如下图所示的实体




3、Create NSManagedObject SubClasses
  • 选择菜单项 Editor - Create NSManagedObject SubClasses,勾选所有看到的选项,两次 Next
  • 调整目录结构,调整完成之后的目录结构示意图如下图所示


4、查看AppDelegate.h的变化

数据操作


#import "Person.h"
#import "AppDelegate.h"
#import "Person+CoreDataProperties.h"
@interface ViewController ()

@property (nonatomic, readonly) AppDelegate *appDelegate;
@property (nonatomic, readonly) NSManagedObjectContext *moc;
@end

@implementation ViewController
- (UIApplication *)appDelegate {
    return [UIApplication sharedApplication].delegate;
}
-(NSManagedObjectContext *)moc {
    return self.appDelegate.managedObjectContext;
}

插入数据并记录currentPerson 方便后面使用
  • 添加 insertPerson 方法
#pragma 插入一条数据
- (void)insertData {
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.moc];
    person.name = @"ysc";
    person.age = @100;
    person.height = @180;

    [self.appDelegate saveContext];
    self.currentPerson = person;
}



修改数据
  • 在私有扩展中增加属性
  • @property (nonatomic) Person *currentPerson;
     添加 updatePerson 方法,并在 touch 方法中调用

#pragma 更新数据
- (void)upDateData {
    self.currentPerson.name = @"CSY";
    self.currentPerson.age = @27;
    [self.appDelegate saveContext];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
     [self upDateData];
}

删除数据(也在touchesBegan中进行测试

#pragma 删除
- (void)deleteData {
    [self.moc deleteObject:self.currentPerson];
    [self.appDelegate saveContext];
}

性能测试:
插入10000条数据测试:(在touchesBegan中进行测试) 
#pragma instertManyPerson
- (void)instertManyPerson{
    NSTimeInterval start = CACurrentMediaTime();
    for (int i = 0 ; i < 10000; i++) {
        Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.moc];
        person.name = [@"zhangsan--" stringByAppendingFormat:@"%d",i] ;
        person.age = @19;
        person.height = @180;
    }
    [self.appDelegate saveContext];
    NSTimeInterval margin = CACurrentMediaTime() - start;
    NSLog(@"margin:%f",margin);
}

通过以上代码运行结果可以通过Navicat中查看数据,
可以看出,Code Data对数据的操作纯面向对象方式开发,使用起来还是非常的方便的!

当然,我们还会发现,在AppDelegate.h中多了许多代码,使得AppDelegate看起来非常的乱,在实际的开发中,我们就需要对其进行封装,使得操作变得更加简单明了!

在下一篇中,再来总结一下,Code Data单例的封装!




2017-05-07 18:54:37 m0_38076563 阅读数 4494


let d = "deed de"

很多类型都能转成Data或NSData。这里再介绍一个 image转data



然后就可以 1. 直接打印出data类型的Byte数了:


let a = d.data(using: .utf8)


print(a!.count)


2. NSData的打印方法(data与NSData桥接,打印哪个都行):


print(NSData(data:a!).length)


打印结果都是  7

一个count 一个length




2015-09-09 10:07:41 qq_25122103 阅读数 356
                     iOS  Encoding URL Data 

     我们知道为了服务端能正确识别我们APP传递过来的URL,我们需要编码URL。尽管NSString提供了内建的"adding percent escapes"方法。
   
- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding

这样做有时是可以的,但如果你的URL传递的参数中包含特殊字符比如&,那么服务端就没办法去正确解析了。因为这些字符与URL保留字符相冲突。


   苹果提供了我们另外的方法编码URL,编码URL中单个字符串,比如包含&的参数。使用如下:

 NSString *encodeStr = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,  CFSTR("1&2?3"), NULL, CFSTR(":/?#[]$&="), kCFStringEncodingUTF8));
    
 NSString *url1 = [NSString stringWithFormat:@"http://192.168.0.7:8080/VMMap/TestServlet?a=%@",encodeStr];

  当然对URL编码后,服务端接受的时候要进行相应的解码,才能正确得到APP传递过来的参数。java代码如下:
String decodeA = java.net.URLDecoder.decode(request.getParameter("a"),
				"UTF-8");





iOS: Data Persistence

阅读数 1109

没有更多推荐了,返回首页