提交 cda96d46 编写于 作者: 0 0xd-cc

feat: 内存检测

上级 a421a77a
......@@ -122,7 +122,7 @@ SPEC CHECKSUMS:
FBRetainCycleDetector: 46daef95c2dfa9be34b53087edf6a8f34e4c749c
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
GCDWebServer: c0ab22c73e1b84f358d1e2f74bf6afd1c60829f2
libwebp: 04242ef451672185c1fe41b285a034d2fbda483c
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965
SDWebImageWebPCoder: ce58a29621c2806dd5c36695ed192cf67163ff9d
SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531
......
......@@ -43,6 +43,7 @@ public class DoKit {
addPlugin(plugin: LogPlugin())
addPlugin(plugin: ANRPlugin())
addPlugin(plugin: MemoryPlugin())
setup()
}
......
//
// MemoryCalculator.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/24.
//
import Foundation
struct MemoryCalculator {
static func memoryUsage() -> Double {
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)
}
return Double(used) / 1024 / 1024
}
static var total: Double {
return Double(ProcessInfo.processInfo.physicalMemory) / 1024 / 1024
}
}
//
// MemoryPlugin.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/24.
//
import Foundation
struct MemoryPlugin: Plugin {
static var isOn = false
var module: PluginModule { .performance }
var title: String { "Memory" }
var icon: UIImage? { DKImage(named: "doraemon_kadun") }
func onInstall() { /* do nothing */ }
func onSelected() {
HomeWindow.shared.openPlugin(vc: MemoryViewController())
}
}
//
// MemoryViewController.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/24.
//
import UIKit
class MemoryViewController: BaseViewController {
static let oscillogramView = OscillogramView()
private let switchView = CellSwitch(frame: .zero)
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
set(title: LocalizedString("内存检测"))
switchView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(switchView)
if #available(iOS 11.0, *) {
switchView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
} else {
switchView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
let switchViewConstraints = [
switchView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
switchView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
switchView.heightAnchor.constraint(equalToConstant: CellSwitch.defaultHeight)
]
NSLayoutConstraint.activate(switchViewConstraints)
switchView.renderUIWithTitle(title: LocalizedString("内存检测开关"), on: MemoryPlugin.isOn)
switchView.needTopLine()
switchView.needDownLine()
switchView.delegate = self
}
}
extension MemoryViewController: CellSwitchDelegate {
func changeSwitchOn(on: Bool) {
MemoryPlugin.isOn = on
if on {
OscillogramWindowManager.shared.add(oscillogramView: MemoryViewController.oscillogramView)
MemoryViewController.oscillogramView.delegate = self
} else {
OscillogramWindowManager.shared.remove(oscillogramView: MemoryViewController.oscillogramView)
}
}
}
extension MemoryViewController: OscillogramViewDelegate {
var oscillogramMaxValue: Double {
return MemoryCalculator.total
}
func collectData() -> Double {
MemoryCalculator.memoryUsage()
}
func oscillogramViewDidColsed() {
MemoryPlugin.isOn = false
OscillogramWindowManager.shared.remove(oscillogramView: MemoryViewController.oscillogramView)
switchView.renderUIWithTitle(title: LocalizedString("内存检测开关"), on: MemoryPlugin.isOn)
}
}
//
// OscillogramView.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/30.
//
import UIKit
class OscillogramView: UIView {
weak var delegate: OscillogramViewDelegate?
private var timer: DispatchSourceTimer?
private var maxValue: Double {
return delegate?.oscillogramMaxValue ?? 100
}
private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(DKImage(named: "doraemon_close_white"), for: .normal)
button.addTarget(self, action: #selector(stop), for: .touchUpInside)
return button
}()
private lazy var tipLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(0x00DFDD)
label.font = UIFont.systemFont(ofSize: kSizeFrom750_Landscape(20))
label.textAlignment = .center
label.frame = CGRect(x: 0, y: self.frame.height, width: 100, height: 10)
return label
}()
private var nodes: [Double] = []
override init(frame: CGRect) {
super.init(frame: frame)
clearsContextBeforeDrawing = true
backgroundColor = UIColor(0x000000, alphaValue: 0.33)
let closeButtonWidth: CGFloat = kSizeFrom750_Landscape(80)
addSubview(closeButton)
addSubview(tipLabel)
closeButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
closeButton.rightAnchor.constraint(equalTo: rightAnchor),
closeButton.topAnchor.constraint(equalTo: topAnchor),
closeButton.widthAnchor.constraint(equalToConstant: closeButtonWidth),
closeButton.heightAnchor.constraint(equalToConstant: closeButtonWidth)
])
}
func start() {
timer?.cancel()
timer = DispatchSource.makeTimerSource()
timer?.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(0))
timer?.setEventHandler { [weak self] in
DispatchQueue.main.async {
guard let self = self else { return }
if let value = self.delegate?.collectData() {
self.nodes.append(value)
self.setNeedsDisplay()
}
}
}
timer?.resume()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
if points().isEmpty { return }
let path = UIBezierPath()
path.lineWidth = 1
var p = points()
let startPoint = p.removeFirst()
path.move(to: startPoint)
UIColor(0x00DFDD).set()
p.forEach { (point) in
path.addLine(to: point)
path.addArc(withCenter: point, radius: 1, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
}
path.stroke()
tipLabel.text = String(format: "%.2f", p.last?.y ?? 0)
tipLabel.center = CGPoint(x: p.last?.x ?? 0, y: (p.last?.y ?? 0) - tipLabel.height)
tipLabel.sizeToFit()
}
private func points() -> [CGPoint] {
let padding = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
let spacing: CGFloat = 10
let lineChartWidth = kScreenWidth - padding.left - padding.right
let lineChartHeight = bounds.size.height - padding.top - padding.bottom
let count = Int(floor(lineChartWidth / spacing))
let array = Array(nodes.suffix(count))
var points = [CGPoint]()
var x: CGFloat = padding.left
array.forEach { (value) in
let y = lineChartHeight * CGFloat(1 - Double(value) / maxValue) + padding.bottom
points.append(CGPoint(x: x, y: y))
x += spacing
}
return points
}
@objc func stop() {
timer?.cancel()
timer = nil
nodes = []
delegate?.oscillogramViewDidColsed()
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isUserInteractionEnabled else { return nil }
guard !isHidden else { return nil }
guard alpha >= 0.01 else { return nil }
guard self.point(inside: point, with: event) else { return nil }
for subview in subviews.reversed() {
let convertedPoint = subview.convert(point, from: self)
if let candidate = subview.hitTest(convertedPoint, with: event) , candidate is UIButton {
return candidate
}
}
return nil
}
}
protocol OscillogramViewDelegate: class {
var oscillogramMaxValue: Double {get}
func collectData() -> Double
func oscillogramViewDidColsed()
}
//
// OscillogramWindow.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/24.
//
import UIKit
class OscillogramWindow: UIWindow {
override init(frame: CGRect) {
super.init(frame: frame)
windowLevel = .statusBar + 2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return handle(point: point, event: event)
}
private func handle(point: CGPoint, event: UIEvent?) -> Bool {
let b = subviews.reversed().map({recursion(subView: $0, point: point, event: event)})
return b.first(where: {$0}) ?? false
}
private func recursion(subView: UIView, point: CGPoint, event: UIEvent?) -> Bool {
let convertedPoint = subView.convert(point, from: self)
if let candidate = subView.hitTest(convertedPoint, with: event), candidate is UIButton {
return true
}
return false
}
}
//
// OscillogramWindowManager.swift
// DoraemonKit-Swift
//
// Created by hash0xd on 2020/6/30.
//
import Foundation
class OscillogramWindowManager {
static let shared = OscillogramWindowManager()
private lazy var stackView: UIStackView = {
let statckView = UIStackView()
statckView.alignment = .fill
statckView.axis = .vertical
statckView.distribution = .equalSpacing
statckView.spacing = 10
return statckView
}()
private let window = OscillogramWindow(frame: CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight))
private init () {
window.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: window.topAnchor, constant: 100),
stackView.leadingAnchor.constraint(equalTo: window.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: window.trailingAnchor),
])
}
func add(oscillogramView: OscillogramView) {
guard !stackView.arrangedSubviews.contains(window) else {
return
}
defer {
window.isHidden = stackView.arrangedSubviews.isEmpty
}
oscillogramView.heightAnchor.constraint(equalToConstant: kSizeFrom750_Landscape(240)).isActive = true
stackView.addArrangedSubview(oscillogramView)
oscillogramView.start()
}
func remove(oscillogramView: OscillogramView) {
defer {
window.isHidden = stackView.arrangedSubviews.isEmpty
}
stackView.removeArrangedSubview(oscillogramView)
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册