Unix文件系统中对单个文件提供了很多属性,比如创建时间、文件大小、修改时间等,这些信息都保存在文件系统中的inode节点中,获取这些属性并不需要读取文件,所以获取速度是很快的。在开发过程中常常会使用这些属性,但更多的时候我们需要一个自定义的属性,来保存我们需要的一些值。比如我们给文件定义一个有效期,或者给文件加一个特殊标记等。
我们可以建立一个数据库,每个文件为一条数据,再自定义一些字段,然而这样做会有些同步问题,比如文件不小心被删除了,或者文件名字改了,这会导致数据库操作过于复杂;或者我们在文件中添加一些自定义内容,加入文件的特定部分,但这样每次获取这些属性时都要读取这个文件,开销代价很高。
其实Unix系统中提供了文件扩展属性的功能,可以非常方便的实现为文件添加自定义属性,这些属性会保存到文件节点数据中。如果文件删除了,这些数据也同时删除;如果文件改名了,也不会影响节点数据,因此这些属性依然会和改名后的文件关联;同时获取这些属性也不用去读取文件。设置一个自定义属性需要用到:
int setxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
int fsetxattr(int fd, const char *name, void *value, size_t size, u_int32_t position, int options);
这两个函数都是用来设置一个文件的扩展属性,唯一的区别是其中setxattr第一个参数是文件路径,而fsetxattr是已打开的文件描述符(由open/creat函数返回)。name是指定的属性的名字,是一个c字符串,为了保证名字的唯一性,建议使用反域名格式的名字,如“com.example.propertyname”。第三个参数value是值的地址,第四个参数size是值的大小,position设置为0。options可以有以下几个:
成功后这两个函数会返回0;否则返回-1,同时设置了系统的errno。
同时Unix还提供了相应的获取已设置扩展属性的方法:
ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options);
ssize_t fgetxattr(int fd, const char *name, void *value, size_t size, u_int32_t position, int options);
参数和设置方法的对应,只是传入的size是value的内存空间,返回的是值实际的大小。
设置文件的过期日期
NSDate *expireDate = ...;
const char* cfilePath = [filePath fileSystemRepresentation];
const char* attrExpireDate = "com.xcodev.file.expireDate";
double attrDate = [expireDate timeIntervalSince1970];
setxattr(cfilePath, attrExpireDate, &attrDate, sizeof(attrDate), 0, 0);
获取文件过期日期,如果过期了就删除。
const char* cfilePath = [path fileSystemRepresentation];
const char* attrExpireDate = "com.xcodev.file.expireDate";
double expireTimestamp = 0;
if (getxattr(cfilePath, attrExpireDate, &expireTimestamp, sizeof(expireTimestamp), 0, 0)>0) {
NSDate *expireDate = [NSDate dateWithTimeIntervalSince1970:expireTimestamp];
if ([expireDate compare:[NSDate date]]==NSOrderedAscending) {
[[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
}
}
文件扩展属性的另一个在iOS中的具体应用就是设置不让iCould备份标记。
const char* cfilePath = [filePath fileSystemRepresentation];
const char* attrBackup = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
setxattr(cfilePath, attrBackup, &attrValue, sizeof(attrValue), 0, 0);
把“com.apple.MobileBackup”的值设置为1后,iCloud就不再去备份这个文件了。