一、在iOS开发中对于文件的管理非常重要。下面我们一起来探究一下 NSFileHandle 类是如何对文件管理的。
二、NSFileHandle类的方法介绍和使用,并加举例。
//
// ViewController+KNSFileHandle.m
// KNSFileHandle
/**
本特辑是探究IOS 的文本管理的 NSFileHandle 类的使用和介绍。
*/
// Created by MAC on 2017/3/17.
// Copyright © 2017年 NetworkCode小贱. All rights reserved.
//
#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@implementation ViewController (KNSFileHandle)
-(void)viewDidLoad{
/**
首先创建一个文件
*/
[self createFile];
}
-(void)createFile{
if (!filePath) {
filePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
filePath =[NSString stringWithFormat:@"%@/test.txt",filePath];
}
/**
生成文件的属性设置
*/
NSDictionary * attributes = @{NSFileOwnerAccountName:@"NetWork小贱"};
/**
创建写入的数据
*/
NSData * saveData = [@"成功QQ吧" dataUsingEncoding:NSUTF8StringEncoding] ;
NSFileManager * fileManager = [NSFileManager defaultManager];
BOOL isFile = [fileManager createFileAtPath:filePath contents:saveData attributes:attributes];
if (!isFile) {
printf("创建失败");
return;
}
/**
读
*/
// [self createReadingFileHandle];
/**
写
*/
// [self createWritingFileHandle];
/**
更新数据
*/
// [self createUpdatingFileHandle];
/**
文件的通知
*/
// [self notifitionFileHandle];
/**
两个回调 read 和 write
*/
// [self fileHandleBlock];
/**
服务通知——socket
*/
[self socketFileHandle];
}
#pragma mark --- create createReadingFileHandle
-(void)createReadingFileHandle{
/**
三种创建方法
另外,下面的这个不常用:
- (instancetype)initWithFileDescriptor:(int)fd closeOnDealloc:(BOOL)closeopt NS_DESIGNATED_INITIALIZER;
*/
// 打开文件准备读取
NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
/**
按长度读取数据
*/
[self analysis:[fileHandle readDataOfLength:3]];
/**
读取文件内容
*/
[self analysis:[fileHandle readDataToEndOfFile]];
/**
可获取的数据
*/
[self analysis:[fileHandle availableData]];
/**
获取文件的操作位置
例如:
文件内有:成功QQ吧 ,然后再使用 [self analysis:[fileHandle readDataOfLength:3]] 之后,再调用 [fileHandle offsetInFile]
就可得到 3。因为,一个汉字占用3个字节,我们开始是读取3个字节,所以现在的文件操作位置是 3.
*/
NSInteger length = [fileHandle offsetInFile];
NSLog(@"%ld",length);
/**
设置当前文件的操作位置
@param
输出:
2017-03-24 13:15:36.912 KNSFileHandle[3469:749724] 11
2017-03-24 13:15:36.912 KNSFileHandle[3469:749724] 3
*/
[fileHandle seekToFileOffset:3];
NSInteger lengthSet = [fileHandle offsetInFile];
NSLog(@"%ld",lengthSet);
/**
seekToEndOfFile 是设置文件操作位置到文件的末尾
输出:
2017-03-24 13:17:45.497 KNSFileHandle[3505:759909] 3
2017-03-24 13:17:45.497 KNSFileHandle[3505:759909] 11
*/
[fileHandle seekToEndOfFile];
NSInteger lengthSetLast = [fileHandle offsetInFile];
NSLog(@"%ld",lengthSetLast);
/**
同步文件
*/
[fileHandle synchronizeFile];
/**
关闭文件
*/
[fileHandle closeFile];
/*
// 测试读取创建的是否可以写入
[fileHandle writeData:[self createData:@"特别推出"]];
// 我们在读取
[self analysis:[fileHandle readDataToEndOfFile]];
*/
/****
总结
1、读取文件创建的NSFileHandle对象,不可用于写入NSFileHandle对象
2、NSFileHandle 读取文件是从文件中取出数据,被取出的数据在文件中被删除
3、读取创建的 NSFileHandle对象 ,不可使用 truncateFileAtOffset 设置文件大小
********/
}
#pragma mark --- createWritingFileHandle
-(void)createWritingFileHandle{
/**
打开并写入
*/
NSFileHandle * fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
/**
我们测试看能否读取
[self analysis:[fileHandle availableData]];
*/
/**
设置写入文件的大小
*/
[fileHandle truncateFileAtOffset:14];
/**
写入数据
*/
[fileHandle writeData:[self createData:@"特别提供"]];
/**
将文件的操作设置到最后
*/
[fileHandle seekToEndOfFile];
NSInteger lengthSet = [fileHandle offsetInFile];
NSLog(@"%ld",lengthSet);
/***
输出:2017-03-24 13:41:03.731 KNSFileHandle[3851:859436] 22
问题:为什么输出的是 22 不是 23 ???
答: 因为写入文件前的文件操作位置是 11,我们又将操作位置设置为 10,然后,我们是从10的操作位置到12,则,最后的操作位置是 22,不是23 。
**/
/**
写入完要同步一下
*/
[fileHandle synchronizeFile];
[fileHandle closeFile];
/**
我们查看最后的结果
输出:
2017-03-24 13:55:38.997 KNSFileHandle[4187:912750]
Result:成功QQ吧
*/
NSFileHandle * fileHandleRead = [NSFileHandle fileHandleForReadingAtPath:filePath];
[self analysis:[fileHandleRead readDataToEndOfFile]];
[fileHandleRead closeFile];
/****
总结
1、写入文件创建的NSFileHandle对象,不可用与读取数据
2、由fileHandleForWritingAtPath 创建的 NSFileHandle对象 ,在写入数据是覆盖不是增加
3、注意,如果在文件写入前设置 truncateFileAtOffset ; 如果设置的数字小于要写入的文件长度,则后期再获取文件的内容为空
********/
}
#pragma mark --- createUpdatingFileHandle
-(void)createUpdatingFileHandle{
/**
打开并更新
*/
NSFileHandle * fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
/**
测试更新状态是否可以读取数据
输出:
2017-03-24 14:10:01.971 KNSFileHandle[4326:965690]
Result:成功QQ吧
*/
[self analysis:[fileHandle availableData]];
/**
跳转操作节点
*/
[fileHandle seekToEndOfFile];
/**
测试更新状态是否可以写入
*/
[fileHandle writeData:[self createData:@"特别提供"]];
[fileHandle synchronizeFile];
[fileHandle closeFile];
/**
我们读取数据
*/
NSFileHandle * fileHandleWrite = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
[self analysis:[fileHandleWrite availableData]];
/***
注意:
使用fileHandleForUpdatingAtPath 创建的对象A 打开更新文件。不能写入后,再使用A,来获取写入的内容
****/
}
#pragma mark --- notifitionFileHandle
-(void)notifitionFileHandle{
/**
文件数据读取到最后的时候发出通知
*/
NSFileHandle * fileHandleReadToEnd = [NSFileHandle fileHandleForReadingAtPath:filePath];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ReadToEndNotification:) name:NSFileHandleReadToEndOfFileCompletionNotification object:fileHandleReadToEnd];
[fileHandleReadToEnd readToEndOfFileInBackgroundAndNotify];
[self analysis:[fileHandleReadToEnd readDataToEndOfFile]];
/**
文件读取中,包括文件开始被读取,都发出通知
*/
NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readCompletionNotification:) name:NSFileHandleReadCompletionNotification object:fileHandle];
[fileHandle readInBackgroundAndNotify];
[self analysis:[fileHandle availableData]];
/**
输出:
2017-03-24 18:33:10.110 KNSFileHandle[7056:1676167] 文件被读取了
2017-03-24 18:33:10.111 KNSFileHandle[7056:1676167] 文件被读取到最后了
注意:
一个是在文件开始和读取中发送通知,
;
另一个是在文件读取完成发送通知
**/
/*
socket建立。对于每个监听到的连接通知,我们可以从fileDescriptor 构造一个NSFileHandle 以接受请求。就会触发 NSFileHandleConnectionAcceptedNotification 通知。
NSFileHandle* listeningHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionAcceptedNotification:) name:NSFileHandleConnectionAcceptedNotification object:listeningHandle];
[listeningHandle acceptConnectionInBackgroundAndNotify];
[self analysis:[listeningHandle availableData]];
*/
/**
这个是当文件内部检测到可用数据的时候,会发起通知
*/
NSFileHandle * fileHandleDataAvailable = [NSFileHandle fileHandleForReadingAtPath:filePath];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailableNotification:) name:NSFileHandleDataAvailableNotification object:fileHandleDataAvailable];
[fileHandleDataAvailable waitForDataInBackgroundAndNotify];
[self analysis:[fileHandleDataAvailable availableData]];
}
#pragma mark --- readCompletionNotification
-(void)readCompletionNotification:(NSNotification*)notification{
NSLog(@"文件被读取了");
}
#pragma mark --- ReadToEndNotification
-(void)ReadToEndNotification:(NSNotification*)notification{
NSLog(@"文件被读取到最后了");
}
#pragma mark --- ReadToEndNotification
-(void)connectionAcceptedNotification:(NSNotification*)notification{
NSLog(@"connectionAcceptedNotification");
}
#pragma mark --- ReadToEndNotification
-(void)dataAvailableNotification:(NSNotification*)notification{
NSLog(@"dataAvailableNotification,文件内部检测到可用数据");
}
#pragma mark --- fileHandleBlock
-(void)fileHandleBlock{
/**
写
*/
NSFileHandle * fileHandleWriteability = [NSFileHandle fileHandleForWritingAtPath:filePath];
fileHandleWriteability.writeabilityHandler = ^(NSFileHandle* handle){
/**
注意:
此处的 handle 不可用于写入
*/
[handle writeData:[self createData:@"特别提供"]];
[handle synchronizeFile];
[handle closeFile];
/**
获取数据
*/
[self analysis:[NSData dataWithContentsOfFile:filePath]];
};
[[NSPipe pipe].fileHandleForWriting writeabilityHandler];
/**
读
*/
NSFileHandle * fileHandleReadability = [NSFileHandle fileHandleForReadingAtPath:filePath];
fileHandleReadability.readabilityHandler = ^(NSFileHandle* handle){
[self analysis:[handle readDataToEndOfFile]];
};
[[NSPipe pipe].fileHandleForReading readabilityHandler];
/**
注意:
发起调用,可以是任意对象的 NSFileHandle 对象。
创建的对象个是个的用途,不可用于其他用途,比如:读的对象就是读,不可用于写
*/
}
#pragma mark --- socketFileHandle
-(void)socketFileHandle{
/**
创建一个socket
知识扩展:
TCP端口12345使用传输控制协议。TCP是TCP/IP网络中的主要协议之一。TCP是面向连接的协议,它要求握手建立端到端的通信。只有在连接建立时,用户的数据才能在连接上双向发送。
注意!TCP保证在端口12345上传送数据包的顺序与它们发送的顺序相同。TCP端口12345的保证通信是TCP和UDP的主要区别。UDP端口12345不会保证TCP的通信。
UDP端口12345提供不可靠的服务和数据报可能会重复、无序,或不通知。在端口12345上的UDP认为在应用程序中不需要进行错误检查和校正,从而避免了在网络接口级别上进行此类处理的开销。
UDP(用户数据报协议)是一个面向消息的最小传输层协议(协议在IETF RFC 768中被记录)。
应用实例,经常使用UDP:语音IP(VoIP),流媒体和实时多人游戏。许多Web应用程序使用UDP,如域名系统(DNS),路由信息协议(RIP),动态主机配置协议(DHCP),简单的网络管理协议(SNMP)。
TCP与UDP TCP:可靠的,有序的,重量级的,UDP流;不可靠的,无序的,轻量级的,报。
*/
NSSocketPort * socketPort = [[NSSocketPort alloc]initWithTCPPort:12345];
NSSocketNativeHandle socketId = [socketPort socket];
/**
创建一个 NSFilehandle 对象
*/
NSFileHandle * socketFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:socketId];
/**
添加链接服务器监控
*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(socketConnectionAcceptedNotification:) name:NSFileHandleConnectionAcceptedNotification object:socketFileHandle];
[socketFileHandle acceptConnectionInBackgroundAndNotify];
/**
连接服务器,并发送消息
*/
[self connectionServer];
}
#pragma mark --- connectionAcceptedNotification
-(void)socketConnectionAcceptedNotification:(NSNotification*)notification{
/**
获取有请求服务器创建的 NSFileHandle 对象
*/
NSFileHandle * connectedSocketFileHandle = [[notification userInfo] objectForKey:NSFileHandleNotificationFileHandleItem];
/**
在监控数据的读取
*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readFileHandle:) name:NSFileHandleReadCompletionNotification object:connectedSocketFileHandle];
/**
发送消息给客户端,确认连接已被接受
*/
[connectedSocketFileHandle writeData:[self createData:@"已经连接成功"]];
/**
开启监控
*/
[connectedSocketFileHandle readInBackgroundAndNotify];
}
#pragma mark --- readFileHandle
-(void)readFileHandle:(NSNotification*)notification{
NSData * data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
/**
对数据的解析
*/
[self analysis:data];
/**
告诉文件句柄继续等待数据
*/
[[notification object] readInBackgroundAndNotify];
}
-(void)connectionServer{
/**
创建socket对象
@param kCFAllocatorDefault 内存分配的类型
@param PF_INET 协议族,一般为Ipv4:PF_INET,(Ipv6,PF_INET6)
@param SOCK_STREAM 套接字类型,TCP用流式—>SOCK_STREAM,UDP用报文式->SOCK_DGRAM
@param IPPROTO_TCP 指定通信协议.如果前一个参数为SOCK_STREAM,则默认使用TCP协议;如果前一个参数SOCK_DGRAM,则默认使用UDP协议
@param kCFSocketNoCallBack 回调的函数类型,有好多类型
@param callout 回调的第2中类型
@param context 用户定义的数据指针,用于对CFSocket对象的额外定义或者申明,可以为NULL
*/
CFSocketRef socketRef = CFSocketCreate(kCFAllocatorDefault,PF_INET,SOCK_STREAM,IPPROTO_TCP,kCFSocketNoCallBack,nil,NULL);
if (socketRef!=nil) {
/**
创建 socket 对象的地址,网络类型
*/
struct sockaddr_in addR4;
memset(&addR4, 0, sizeof(addR4));
addR4.sin_len = sizeof(addR4);
addR4.sin_family = AF_INET;
/**
设置连接服务器的地址
*/
addR4.sin_addr.s_addr = inet_addr("127.0.0.1");
/**
设置远程监听的端口
*/
addR4.sin_port = htons(12345);
/**
进行地址转换
*/
CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addR4, sizeof(addR4));
/**
连接服务器,并返回结果
*/
CFSocketError socketError = CFSocketConnectToAddress(socketRef, dataRef, 10);
if (socketError == kCFSocketSuccess) {
NSLog(@"连接服务器成功");
const char * stringToSend = [@"NetWork小贱" UTF8String];
send(CFSocketGetNative(socketRef),stringToSend, strlen(stringToSend)+1, 1);
/**
输出:
2017-03-27 17:39:05.921 KNSFileHandle[4453:1317371] 连接服务器成功
2017-03-27 17:39:08.647 KNSFileHandle[4453:1317371]
Result:NetWork小贱
*/
}
}
}
#pragma mark --- createData 生成
-(NSData*)createData:(NSString*)string{
NSData * saveData = [string dataUsingEncoding:NSUTF8StringEncoding] ;
return saveData;
}
#pragma mark --- analysis 解析
-(void)analysis:(NSData*)data{
NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"\nResult:%@",string);
}
/***
输出的结果:
1、2017-03-24 11:34:59.606 KNSFileHandle[2436:574179] 成功QQ吧
2、2017-03-24 11:53:27.531 KNSFileHandle[2792:653731]
Result:成
2017-03-24 11:53:27.532 KNSFileHandle[2792:653731]
Result:功QQ吧
*/
@end
三、完整文件的下载
https://pan.baidu.com/s/1nvqJlVV