You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift修改kMDItemDateAdded元数据失败:setattrlist返回-1求修复

修改文件kMDItemDateAdded元数据的Swift实现问题修复

问题背景

macOS/iOS中,无法通过FileManager的setAttributes方法修改文件的kMDItemDateAdded(即"Date added")属性,因为FileAttributeKey没有对应的dateAdded键。示例代码如下:

var attrs: [FileAttributeKey: Date] = [:]
// 没有对应的FileAttributeKey.dateAdded可设置
attrs[FileAttributeKey.modificationDate] = Date.now 

try FileManager.default.setAttributes(attrs, ofItemAtPath: file.path)

通过C语言的setattrlist系统调用可以实现该功能,但转译为Swift后,setattrlist始终返回-1,无法生效。

原C语言实现参考

以下是可正常工作的C语言代码:

#include <stdlib.h>
#include <string.h>
#include <sys/attr.h>
#include <unistd.h>

/*
 * 设置文件的kMDItemDateAdded属性
 * 返回值:
 *   • 0 成功
 *   • 1 系统调用失败,可检查errno
 */
int set_date_added(const char* path, struct timespec in) {
    attrgroup_t request_attrs = ATTR_CMN_ADDEDTIME;

    struct attrlist request;
    memset(&request, 0, sizeof(request));
    request.bitmapcount = ATTR_BIT_MAP_COUNT;
    request.commonattr = request_attrs;

    typedef struct {
        struct timespec added;
    } __attribute__((aligned(4), packed)) request_buf_t;

    request_buf_t request_buf;
    request_buf.added.tv_sec = in.tv_sec;
    request_buf.added.tv_nsec = in.tv_nsec;

    int err = setattrlist(path, &request, &request_buf, sizeof(request_buf), 0);
    if (err != 0) {
        return 1;
    }

    return 0;
}

Swift转译的问题排查

转译后的Swift代码失败的核心原因有以下几点:

  • 路径传递错误:直接将Swift String传入setattrlist,没有正确转换为C风格字符串,可能导致路径解析失败。
  • 时间值转换不精确:将Date.timeIntervalSince1970(Double类型)直接转为Int,丢失了小数部分的纳秒值,且tv_sec的类型应为Int64(对应C的time_t)而非Int。
  • 结构体内存对齐不匹配:C代码中对request_buf_t使用了packed属性确保内存紧凑,Swift默认结构体对齐规则与C不一致,导致setattrlist无法正确解析缓冲区数据。
  • 错误排查缺失:未获取errno值定位具体失败原因,比如权限问题、路径无效等。

修复后的Swift实现

import Darwin

func setDateAdded(path: String, toDate date: Date) -> Bool {
    guard FileManager.default.fileExists(atPath: path) else {
        print("文件不存在")
        return false
    }

    // 1. 初始化attrlist并清零
    var attrList = attrlist()
    memset(&attrList, 0, MemoryLayout<attrlist>.size)
    attrList.bitmapcount = UInt16(ATTR_BIT_MAP_COUNT)
    attrList.commonattr = attrgroup_t(ATTR_CMN_ADDEDTIME)

    // 2. 定义与C代码内存布局一致的缓冲区结构体(packed属性确保紧凑对齐)
    @_packed struct AttrBuffer {
        var added: timespec
    }

    // 3. 精确转换Date到timespec
    let timeInterval = date.timeIntervalSince1970
    let seconds = Int64(floor(timeInterval))
    let nanoseconds = Int((timeInterval - floor(timeInterval)) * 1_000_000_000)
    var attrBuffer = AttrBuffer(added: timespec(tv_sec: seconds, tv_nsec: nanoseconds))

    let bufferSize = MemoryLayout<AttrBuffer>.size
    var result: Int32 = -1

    // 4. 正确传递C风格路径
    path.withCString { cPath in
        result = setattrlist(cPath, &attrList, &attrBuffer, bufferSize, 0)
    }

    // 5. 打印错误信息便于排查
    if result != 0 {
        let errorStr = String(cString: strerror(errno))
        print("设置失败:\(errorStr) (errno: \(errno))")
        return false
    }

    return true
}

关键修复点说明

  • 路径处理:使用String.withCString确保传递给setattrlist的是有效的C风格字符串,避免内存管理问题。
  • 结构体对齐:给AttrBuffer添加@_packed属性,匹配C代码中的__attribute__((packed)),保证内存布局一致。
  • 时间转换:精确拆分秒和纳秒部分,且tv_sec使用Int64类型,适配64位系统的time_t定义。
  • 错误排查:通过errno和strerror获取具体失败原因,便于定位权限、路径等问题。

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 11:53:17