Проблемы с быстрым указателем с MACH_TASK_BASIC_INFO
Я пытаюсь преобразовать ObjC stackru ответ в Swift и не удается. Похоже, я прохожу UnsafeMutablePointer<mach_msg_type_number_t>
когда я должен был передать inout mach_msg_type_number_t
и я не могу решить свою проблему. Из того, что я понимаю из документации указателя Swift (не очень), они должны быть взаимозаменяемыми..?
Дальнейшая информация ниже.
Вот цель C:
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
и вот, насколько я получил в Swift (много строк для более легкой проверки типов)
let name: task_name_t = mach_task_self_
let flavor: task_flavor_t = task_flavor_t(MACH_TASK_BASIC_INFO)
var info: mach_task_basic_info
var size: mach_msg_type_number_t = UnsignedFixed(sizeof(mach_task_basic_info_t))
let kerr = task_info(name, flavor, info as task_info_t, &size)
task_info
подпись это:
func task_info(target_task: task_name_t, flavor: task_flavor_t, task_info_out: task_info_t, task_info_outCnt: UnsafeMutablePointer<mach_msg_type_number_t>) -> kern_return_t
и ошибка в последней строке:
Cannot convert the expression's type '(@!lvalue task_name_t, task_flavor_t, task_info_t, inout mach_msg_type_number_t)' to type 'kern_return_t'
7 ответов
При взаимодействии с функциями C вы не можете полагаться на сообщения об ошибках компилятора - разбивайте его на параметры по параметрам, нажимая команды, пока не узнаете, с чем работаете. Для начала, типы, с которыми вы сталкиваетесь:
task_name_t
:UInt32
task_flavor_t
:UInt32
task_info_t
:UnsafeMutablePointer<Int32>
UnsafeMutablePointer<mach_msg_type_number_t>
:UnsafeMutablePointer<UInt32>
kern_return_t
-Int32
Здесь есть один хитрый момент Swift и ошибка в вашем коде. Во-первых, task_info_out
параметр должен быть UnsafeMutablePointer<UInt32>
, но на самом деле нужно указать на экземпляр mach_task_basic_info
, Мы можем обойти это, создав UnsafeMutablePointer<mach_task_basic_info>
и завернуть в другое UnsafeMutablePointer
во время вызова - компилятор будет использовать вывод типа, чтобы знать, что мы хотим, чтобы указатель переноса был подтипирован как UInt32
,
Во-вторых, ты звонишь sizeof(mach_task_basic_info_t)
(указатель на mach_task_basic_info
) когда тебе следует звонить sizeinfo(mach_task_basic_info)
Таким образом, ваш счетчик байтов оказывается слишком низким для хранения структуры данных.
При дальнейших исследованиях все стало немного сложнее. Исходный код для этого был неверным, в этом size
должен быть инициализирован к константе MACH_TASK_BASIC_INFO_COUNT
, К сожалению, это макрос, а не простая константа:
#define MACH_TASK_BASIC_INFO_COUNT (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
Swift не импортирует их, поэтому нам нужно переопределить их самим. Вот рабочий код для всего этого:
// constant
let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
// prepare parameters
let name = mach_task_self_
let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
// allocate pointer to mach_task_basic_info
var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)
// call task_info - note extra UnsafeMutablePointer(...) call
let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
// get mach_task_basic_info struct out of pointer
let info = infoPointer.move()
// deallocate pointer
infoPointer.dealloc(1)
// check return value for success / failure
if kerr == KERN_SUCCESS {
println("Memory in use (in bytes): \(info.resident_size)")
} else {
let errorString = String(CString: mach_error_string(kerr), encoding: NSASCIIStringEncoding)
println(errorString ?? "Error: couldn't parse error string")
}
Мне потребовалось немного, чтобы обновить ответ Airspeed Velocity на последний синтаксис swift (Swift 3, бета 6), но вот что я получил:
func report_memory() {
var info = mach_task_basic_info()
let MACH_TASK_BASIC_INFO_COUNT = MemoryLayout<mach_task_basic_info>.stride/MemoryLayout<natural_t>.stride
var count = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: MACH_TASK_BASIC_INFO_COUNT) {
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
$0,
&count)
}
}
if kerr == KERN_SUCCESS {
print("Memory in use (in bytes): \(info.resident_size)")
}
else {
print("Error with task_info(): " +
(String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error"))
}
}
Надеюсь, это полезно.
Для быстрого копирования и вставки решения в Swift 5 используйте
func reportMemory() {
var taskInfo = task_vm_info_data_t()
var count = mach_msg_type_number_t(MemoryLayout<task_vm_info>.size) / 4
let result: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count)
}
}
let usedMb = Float(taskInfo.phys_footprint) / 1048576.0
let totalMb = Float(ProcessInfo.processInfo.physicalMemory) / 1048576.0
result != KERN_SUCCESS ? print("Memory used: ? of \(totalMb)") : print("Memory used: \(usedMb) of \(totalMb)")
}
Ответ Нейта превосходен, но вы можете внести некоторые изменения, чтобы упростить его.
Во-первых, вместо того, чтобы распределять / освобождать task_basic_info
указатель, вы можете создать структуру в стеке, а затем использовать withUnsafeMutablePointer
получить указатель непосредственно на него, который вы можете передать.
func report_memory() {
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(sizeofValue(info))/4
let kerr: kern_return_t = withUnsafeMutablePointer(&info) {
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
task_info_t($0),
&count)
}
if kerr == KERN_SUCCESS {
println("Memory in use (in bytes): \(info.resident_size)")
}
else {
println("Error with task_info(): " +
(String.fromCString(mach_error_string(kerr)) ?? "unknown error"))
}
}
Swift 5 + Combine, непрерывный мониторинг памяти
Показать точную память в МБ, например XCODE
import Foundation
import Combine
enum MemoryMonitorState {
case started
case paused
}
class MemoryUsageCustom {
private var displayLink: CADisplayLink!
var state = MemoryMonitorState.paused
let subject = PassthroughSubject<String, Never>()
private static var sharedInstance: MemoryUsageCustom!
public class func shared() -> MemoryUsageCustom {
if self.sharedInstance == nil {
self.sharedInstance = MemoryUsageCustom()
}
return self.sharedInstance
}
private init() {
self.configureDisplayLink()
}
func startMemoryMonitor() {
if self.state == .started {
return
}
self.state = .started
self.start()
}
func stopMemoryMonitor() {
self.state = .paused
self.pause()
}
//--------------------------------------------------------------------------------
//MARK:- Display Link
//--------------------------------------------------------------------------------
func configureDisplayLink() {
self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkAction(displayLink:)))
self.displayLink.isPaused = true
self.displayLink?.add(to: .current, forMode: .common)
}
private func start() {
self.displayLink?.isPaused = false
}
/// Pauses performance monitoring.
private func pause() {
self.displayLink?.isPaused = true
}
@objc func displayLinkAction(displayLink: CADisplayLink) {
let memoryUsage = self.memoryUsage()
let bytesInMegabyte = 1024.0 * 1024.0
let usedMemory = Double(memoryUsage.used) / bytesInMegabyte
let totalMemory = Double(memoryUsage.total) / bytesInMegabyte
let memory = String(format: "%.1f of %.0f MB used", usedMemory, totalMemory)
// self.memoryString = memory
subject.send(memory)
}
func memoryUsage() -> (used: UInt64, total: UInt64) {
var taskInfo = task_vm_info_data_t()
var count = mach_msg_type_number_t(MemoryLayout<task_vm_info>.size) / 4
let result: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count)
}
}
var used: UInt64 = 0
if result == KERN_SUCCESS {
used = UInt64(taskInfo.phys_footprint)
}
let total = ProcessInfo.processInfo.physicalMemory
return (used, total)
}
}
Как пользоваться
//Start Monitoring
MemoryUsageCustom.shared().startMemoryMonitor()
var storage = Set<AnyCancellable>()
MemoryUsageCustom.shared().subject.sink {[weak self] (string) in
print(string)
}.store(in: &storage)
Ответ Скорости Скорости в Свифте 3...
func GetMemory()
{
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout.size(ofValue: info))/4
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info)
{
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
$0.withMemoryRebound(to: Int32.self, capacity: 1) { zeroPtr in
task_info_t(zeroPtr)
},
&count)
}
if kerr == KERN_SUCCESS {
print("Memory in use (in bytes): \(info.resident_size)")
}
else {
print("Error with task_info(): " +
(String.init(validatingUTF8: mach_error_string(kerr)) ?? "unknown error"))
}
}
Для Linux:
import Foundation
@available(macOS 10.13, *)
public func shell(_ args: String...) throws -> String? {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = String(data: data, encoding: String.Encoding.utf8) {
if output.count > 0 {
//remove newline character.
let lastIndex = output.index(before: output.endIndex)
return String(output[output.startIndex ..< lastIndex])
}
task.waitUntilExit()
return output
} else {
return nil
}
}
@available(macOS 10.13, *)
public func shellWithPipes(_ args: String...) throws -> String? {
var task: Process!
var prevPipe: Pipe? = nil
guard args.count > 0 else {
return nil
}
for i in 0..<args.count {
task = Process()
task.launchPath = "/usr/bin/env"
let taskArgs = args[i].components(separatedBy: " ")
var refinedArgs = [String]()
var refinedArg = ""
for arg in taskArgs {
if !refinedArg.isEmpty {
refinedArg += " " + arg
if arg.suffix(1) == "'" {
refinedArgs.append(refinedArg.replacingOccurrences(of: "\'", with: ""))
refinedArg = ""
}
} else {
if arg.prefix(1) == "'" {
refinedArg = arg
} else {
refinedArgs.append(arg)
}
}
}
task.arguments = refinedArgs
let pipe = Pipe()
if let prevPipe = prevPipe {
task.standardInput = prevPipe
}
task.standardOutput = pipe
task.standardError = pipe
try task.run()
prevPipe = pipe
}
if let data = prevPipe?.fileHandleForReading.readDataToEndOfFile(),
let output = String(data: data, encoding: String.Encoding.utf8) {
if output.count > 0 {
//remove newline character.
let lastIndex = output.index(before: output.endIndex)
return String(output[output.startIndex ..< lastIndex])
}
task.waitUntilExit()
return output
}
return nil
}
#if os(Linux)
public func reportMemory() {
do {
if let usage = try shellWithPipes("free -m", "grep Mem", "awk '{print $3 \"MB of \" $2 \"MB\"}'") {
NSLog("Memory used: \(usage)")
}
} catch {
NSLog("reportMemory error: \(error)")
}
}
public func availableMemory() -> Int {
do {
if let avaiable = try shellWithPipes("free -m", "grep Mem", "awk '{print $7}'") {
return Int(avaiable) ?? -1
}
} catch {
NSLog("availableMemory error: \(error)")
}
return -1
}
public func freeMemory() -> Int {
do {
if let result = try shellWithPipes("free -m", "grep Mem", "awk '{print $4}'") {
return Int(result) ?? -1
}
} catch {
NSLog("freeMemory error: \(error)")
}
return -1
}
#endif