Вызов функции C с указателем на массив и указателем int из Swift - PullRequest
0 голосов
/ 08 июля 2020

Я создаю C lib плюс оболочку для удобного использования в Swift. Функция C принимает два параметра, указатель на массив и указатель int:

int crgetproclist(struct kinfo_proc *proc_list, size_t *count) {
    int err = 0;
    size_t length = 0;
    
    static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
    
    // Call sysctl with a NULL buffer to get proper length
    err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0);
    //if (err) return [-1];
    
    // Get the actual process list
    err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, proc_list, &length, NULL, 0);
    //if (err) return [-1];
    
    *count = length / sizeof(struct kinfo_proc);
    
    for (int i = 0; i < *count; i++) {
        struct kinfo_proc proc = proc_list[i];
        proc = proc;
    }
       
    return 1;
}

Я вызываю эту функцию из моей оболочки Swift:

var data: [Process] = []

override func viewDidLoad() {
    super.viewDidLoad()
    
    let proc_list: UnsafeMutablePointer<kinfo_proc> = UnsafeMutablePointer<kinfo_proc>.allocate(capacity: 500)
    var count: size_t = 0
    let result = crgetproclist(proc_list, &count)
    
    var foobar: [Process] = []
    
    if (result == 1) {
        for i in 1..<count {
            var proc: kinfo_proc = proc_list[i]
            var process = Process(proc: proc)
            foobar.append(process)              // <---- works
            self.data.append(process)           // <---- EXC_BAD_ACCESS ????
        }
        
        self.data.sort(by: {
            (a: Process, b: Process) -> Bool in
            return a.name.lowercased() < b.name.lowercased()
        })
        
        self.myTable.reloadData()
    }
}
class Process: NSObject {
    
    var _proc: kinfo_proc
    var pid: pid_t
    var name: String
    var icon: NSImage?
    var isAlive: Bool = false
    var uid: uid_t = 0
    
    init(proc: kinfo_proc) {
        self._proc = proc
        self.pid = proc.kp_proc.p_pid
        self.name = String(cString: crgetprocname(pid))
        self.uid = crgetuid(pid)
        
        super.init()
    }
}

Вопросы

  1. Как правильно создать и передать UnsafeMutablePointer в функцию C? Я жестко запрограммировал емкость: 500, что работает, но как сделать это правильно без жестко запрограммированной емкости?

  2. Когда я пытаюсь добавить его в данные массива переменных моего класса, он запускает EXC_BAD_ACCESS, но когда я добавляю его в foobar того же типа, он работает. Зачем? Как присвоить его переменной класса без ошибки памяти?

1 Ответ

1 голос
/ 08 июля 2020

Я могу ответить только на первую часть вашего вопроса: чтобы определить необходимое количество выделений для списка процессов, вы должны разрешить вызов crgetproclist() с аргументом NULL (точно так же, как sysctl() может быть вызывается с аргументом NULL для oldp, чтобы получить необходимый размер буфера):

int crgetproclist(struct kinfo_proc *proc_list, size_t *count) {
    int err = 0;
    size_t length;

    static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
    
    if (proc_list == NULL) {
        // Call sysctl with a NULL buffer to get proper length
        length = 0;
        err = sysctl((int *)name, (sizeof(name) / sizeof(*name)), NULL, &length, NULL, 0);
    } else {
        // Get the actual process list
        length = *count * sizeof(struct kinfo_proc);
        err = sysctl((int *)name, (sizeof(name) / sizeof(*name)), proc_list, &length, NULL, 0);
    }
    if (err) return -1;
    *count = length / sizeof(struct kinfo_proc);
    return 1;
}

Теперь вы можете вызвать эту функцию из Swift дважды: сначала для определения счетчика выделения, а затем еще раз для получения процесса list:

var count: size_t = 0
crgetproclist(nil, &count)

let procList = UnsafeMutablePointer<kinfo_proc>.allocate(capacity: count)
if crgetproclist(procList, &count) == 1 {
    for i in 0..<count {
        let proc = procList[i]
        // ...
    }
}
procList.deallocate()

Обратите внимание, что вы можете легко реализовать функцию в чистом Swift:

func getProcessList() -> [kinfo_proc]? {
    var name : [Int32] = [ CTL_KERN, KERN_PROC, KERN_PROC_ALL ]
    var length = size_t()
    sysctl(&name, UInt32(name.count), nil, &length, nil, 0)
    let count = length / MemoryLayout<kinfo_proc>.size
    var procList = Array(repeating: kinfo_proc(), count: count)
    let result = sysctl(&name, UInt32(name.count), &procList, &length, nil, 0)
    guard result == 0 else { return nil } // Some error ...
    return Array(procList.prefix(length / MemoryLayout<kinfo_proc>.size))
}
...