Мой рабочий компьютер — iMac 27″ 5 K 2 017 года: Intel Core i5, 32 ГБ памяти, SSD и macOS Sonoma. Он всё ещё справляется с повседневной работой. Менять его только ради того, чтобы утилиты снова считали его «современным Mac», мне не хотелось.
Зато хотелось понимать, чем заняты CPU, две видеокарты и память, почему греется машина и куда исчезло место на диске. Постепенно для этого набирается целая полка приложений: монитор в меню, управление вентиляторами, клинер, деинсталлятор. Одни требуют подписку, другие продаются отдельно, третьи меняют системные требования. И в какой‑то момент я решил собрать нужные мне вещи в одном нативном приложении.
Так появился MacPulse — бесплатный проект на Swift и SwiftUI под MIT. Сейчас это Universal‑приложение для Intel и Apple Silicon с минимальной системой macOS 13 Ventura. Monterey пока не поддерживается. Основная физическая тестовая машина — тот самый Intel iMac; универсальная сборка не означает, что каждый датчик проверен на каждом Mac.
Сразу расшифрую цифру в заголовке: 0,2% CPU и около 28 МиБ RSS — результат конкретного замера в простое, с закрытым окном и только CPU в монохромной строке меню. С цветным графиком финальная сборка показывала примерно 0,28–0,30% и 32 МиБ. Это не обещание такой нагрузки при открытом дашборде, включённых виджетах или стресс‑тесте. Ниже — методика и ограничения.

Монитор на Intel iMac. Адреса на снимках обезличены до отрисовки; показания загрузки взяты из работающих сервисов.
Сначала пришлось перестать верить красивым цифрам
SMC: четыре символа ещё не делают датчик достоверным
Температуры на моём Intel Mac читаются через IOKit и драйвер AppleSMC. Сервис открывает соединение, получает информацию о ключе, проверяет тип и размер ответа, затем декодирует значение. Например, sp78 — знаковое число с восемью дробными битами:
let bits = UInt16(bytes[0]) << 8 | UInt16(bytes[1]) let celsius = Double(Int16(bitPattern: bits)) / 256
Все обращения сериализованы внутри actor. Метаданные ключей кэшируются, неудачные обращения получают паузу перед повтором, соединение закрывается явно. В самом сервисе чтения нет команд записи.
Это важно: отсутствующий ключ нельзя превращать в 0 °C, а подозрительное значение — в уверенный датчик на экране. На машине с переходником для диска один из ключей мог выдавать отрицательную температуру. Вместо «диск замёрз до −7 °C» поле скрывается.
AppleSMC — не стабильный документированный контракт на все поколения Mac. Для Apple Silicon я не перенёс Intel‑ключи по принципу «авось сработает»: там доступен безопасный fallback с системным термосостоянием. Числовую температуру, которой реализованный API не отдаёт, приложение не придумывает.
VRAM: занято драйвером не всегда значит занято приложениями
Следующая ловушка была у AMD Radeon Pro. На 5 K‑экране драйвер может держать почти всю видеопамять в выделенном пуле. Если вывести его размер как активное использование, пользователь увидит вечные 99%.
Вместо привязки к Radeon Pro 570 и фиксированным 4 ГБ пришлось разделить несколько задач:
находить устройства по идентификаторам реестра, чтобы не смешивать счётчики Intel и AMD;
получать реальный объём памяти из свойств устройства;
разбирать доступные показатели использования по смыслу, а не складывать все похожие ключи;
оставлять значение недоступным, если надёжного счётчика нет.
Отдельная видеопамять и unified memory — тоже разные сущности. Поэтому Apple Silicon показывает объединённую память без выдуманного «выделенного VRAM». Системная загрузка GPU на этой платформе через реализованный fallback может быть недоступна.
Главный потребитель ресурсов монитора — иногда сам монитор
Утилита, которая рисует красивые графики и сама постоянно будит процессор, быстро теряет смысл. У меня оптимизация началась не с Mach API, а с интерфейса.
Первый дашборд использовал LazyVGrid. Карточек было мало, но каждые две секунды менялись подписи, сетка пересчитывалась во время прокрутки. При скрытии недоступных датчиков менялась высота документа, и прокрутка могла прыгнуть вверх.
В итоге я заменил сетку на обычные VStack/HStack с двумя или тремя колонками. Карточки сохраняют идентичность, недоступные блоки схлопываются. Одного схлопывания недостаточно: нативный якорь прокрутки сохраняет допустимое смещение после изменения высоты и перехода между разделами.
Во время живой прокрутки новые показания не публикуются в дашборд на каждом тике: после её завершения применяется последний снимок. История ограничена, форматтеры переиспользуются, маленькое изображение строки меню кэшируется до изменения отображаемых значений.
Второе изменение оказалось ещё полезнее: не опрашивать то, что никто не видит.
let snapshot = await collector.sample(selection: requestedMetrics)
Когда закрыты окно, поповер и виджеты, набор requestedMetrics строится по включённым показателям строки меню. Нужен только CPU — не нужно перечислять процессы, обходить GPU, читать SMC, батарею и SMART. Фоновый интервал в таком режиме — три секунды. Открытый интерфейс использует настроенный интервал 1–3 секунды.
Виджеты запрашивают объединение нужных им датчиков. Их общая история ограничена двадцатью точками; для мини‑графика берутся двенадцать. У каждой панели нет собственного таймера опроса. Цикл безопасности активного управления вентиляторами, напротив, не замедляется ради красивого результата профилирования.
Что получилось в измерениях
Процент ниже считается относительно одного полностью занятого логического ядра, а не всей мощности четырёхъядерного компьютера. Из замеров исключён прогрев в восемь секунд; CPU считался по приращению пользовательского и системного времени процесса.
Конфигурация |
Длительность |
CPU |
RSS в конце |
|---|---|---|---|
1.0.0, только CPU в меню, прежний опрос |
30 с |
0,733% |
28,4 МиБ |
1.0.1, монохромный текст, опрос 3 с |
60 с |
0,200% |
27,7 МиБ |
Финальная 1.0.1, цветной график и текст CPU |
60 с |
0,300% |
32,3 МиБ |
То есть путь был примерно от 0,7–1% к 0,2–0,3% в определённом фоновом режиме. Не «приложение всегда ест 0,2%». Короткий замер также не доказывает отсутствие утечек за неделю. Методика, дополнительные замеры и ограничения лежат в репозитории.
Зачем ещё один клинер — и почему я не доверяю кнопке «Удалить всё»
В клинере самое сложное — не вызвать FileManager.removeItem, а решить, что действительно можно удалить.
Для браузеров по умолчанию сканируется безопасный кэш внутри ~/Library/Caches. Профили, cookies, пароли и активные сессии в этот режим не входят. Глубокая очистка выделена отдельно и требует подтверждения.
Ручной режим сначала показывает категории, размеры и пути. Удаляются только выбранные объекты, прошедшие проверку разрешённых корней. Проверка учитывает симлинки: удалить саму ссылку внутри разрешённого кэша и пойти за ней в чужую папку — разные операции.
Отдельно пришлось исправлять поиск «хвостов». Папка в Application Support без очевидного одноимённого .app ещё не является мусором. Системные com.apple.*, установленные владельцы и запущенные приложения требуют защиты. Совпадения только по названию нельзя автоматически считать доказательством.
Быстрая очистка действительно запускается в один клик: доступные безопасные кэши, пользовательские логи и Корзина. Поэтому здесь есть важный компромисс: очистка Корзины необратима. Для выбора конкретных категорий лучше ручной режим. Кэш открытых программ может пропускаться; нейтральный отчёт объясняет причину вместо красной стены технических ошибок.

Это экран подготовки: ради скриншота удаление не запускалось. Домашние пути не публикуются.
Full Disk Access не требуется для обычного мониторинга. Для части очистки он может понадобиться, но приложение не обходит TCC. Индикатор доступа — эвристика, а не официальный API, который достоверно возвращает статус разрешения для всего диска.
Что вошло в MacPulse 1.0.1
Строка меню, которая не съедает весь экран
Классический текст, компактные подписи, спарклайны или графики вместе с текстом. Можно выбрать монохром, акцент, цвета категорий или динамическую подсветку. CPU и сеть следуют одной выбранной схеме.
Здесь обнаружилась особенность интеграции SwiftUI с системной строкой меню: соседство графика и текста в предпросмотре не гарантировало, что macOS оставит тексту ширину в настоящем status item. Цветные варианты теперь отрисовываются как одна кэшированная композиция; монохромный режим сохраняет нативный текст.

Виджеты: не три копии одного дашборда
CPU/GPU, память/диск и сеть включаются независимо. Есть отдельная сводная панель. Размеры — 160×160, 340×160 и 340×340 точек; сводка доступна в двух последних.
В маленьком размере помещаются кольца или скорости, в среднем — графики и точные объёмы, в большом — подробности именно выбранной темы. Панели запоминают координаты, размер и закрепление. Можно менять прозрачность и выбирать рабочий стол либо размещение поверх окон.
Реализация — NSPanel с материалом SwiftUI. Оказалось, что уровня чуть выше обоев не всегда достаточно: Finder может перехватывать клики своей областью иконок. Для интерактивных панелей приходится учитывать и этот уровень. А для меню размеров в неактивирующей панели нужно явно задавать target/action и отключать автоматическую валидацию пунктов.

Реальные панели на фоне обоев, без значков, соседних окон и содержимого рабочего стола.
Деинсталлятор и подписи приложений
Деинсталлятор ищет программы в /Applications и ~/Applications, собирает связанные пути по Bundle ID и названию и показывает отдельные чекбоксы. Системные, запущенные приложения и общие данные требуют особого обращения. Полного обнаружения каждого стороннего helper я не обещаю.
Для подписи используются SecStaticCodeCreateWithPath и SecStaticCodeCheckValidity: интерфейс различает Apple / Developer ID, Self‑Signed / Ad‑Hoc, Unsigned, а также недействительную или недоступную подпись. Зеленый бейдж не является отдельной проверкой нотарификации и не означает «программа безопасна».
Дата использования берётся из Spotlight; если её нет, запасная дата модификации помечается как приблизительная. Метаданные не дают разрешения на удаление — это информационная часть интерфейса.

На публичном снимке оставлена ограниченная выборка известных приложений, пользовательские пути заменены на ~/Library/….
Вентиляторы без постоянного root у всего приложения
На поддерживаемых Intel Mac доступны автоматический режим, минимальные обороты и температурные кривые. Изменения выполняет отдельный привилегированный helper после явного разрешения администратора. Обычное чтение метрик не требует повышения прав.
Контрольная сессия имеет heartbeat; её политика безопасности отделена от интерфейса. Это не отменяет осторожности при выборе кривых. На Apple Silicon управление пока read‑only, на безвентиляторных машинах раздел скрывается.
Сетевая лаборатория
Кроме текущих скоростей и трафика сессии есть сетевой паспорт, история смен IP, IPv6-проверка и трассировка через /usr/sbin/traceroute. Измерение скорости использует системный /usr/bin/networkQuality; тест передаёт реальные данные и запускается отдельно.
Публичный IP запрашивается только после включения функции. Можно задать HTTPS‑сервис и интервал. Выбранный сервис при этом видит публичный IP пользователя. История остаётся локальной; признаки tunnel‑интерфейса не являются гарантией, что весь трафик действительно проходит через VPN.
Отдельная таблица показывает видимые TCP/UDP‑сокеты процессов. Это наблюдение, а не фаервол: наличие соединения само по себе не доказывает передачу данных.

IP заменён на 192.168.1.100, MAC — на 00:1A:2B:3C:4D:5E. Это подстановки для публикации, не реальные реквизиты сети.
Проверки и границы проекта
В текущем наборе 122 теста: 116 обычных проходят, шесть аппаратных и сетевых запускаются отдельно. CI проверяет Intel и Apple Silicon. Дополнительные нативные проверки покрывают одиннадцать компоновок виджетов, размеры окон, цвета графиков и сохранение прокрутки.
Это не основание обещать постоянные 60 fps на любом Mac или поддержку всех температурных датчиков. Цель проще: честно показывать доступные данные, не выполнять лишнюю работу и не превращать монитор в ещё одну причину нагрева.
Локализаций двенадцать. Телеметрии и аккаунта нет. Есть явно запускаемые сетевые функции и проверка обновлений через GitHub — это тоже сетевые запросы, а не «приложение вообще никогда не выходит в интернет».
Где скачать
Исходники MacPulse на GitHub · Релиз 1.0.1 и Universal DMG
DMG устанавливается обычным перетаскиванием в «Программы». Для обновления сначала завершите запущенный MacPulse, затем замените приложение. Настройки сохранятся.
Через собственный Homebrew tap проекта:
brew tap makskondrasov9-dev/macpulse https://github.com/makskondrasov9-dev/MacPulse.git brew install --cask makskondrasov9-dev/macpulse/macpulse
Это не официальный cask из homebrew/cask: формула поддерживается в репозитории проекта.
У релиза пока нет сертификата Developer ID и нотарификации Apple: применяется ad‑hoc подпись. Если macOS блокирует запуск, после проверки источника используйте «Открыть» в контекстном меню либо «Системные настройки → Конфиденциальность и безопасность → Всё равно открыть». Apple описывает этот порядок отдельно.
Для тех, кто осознанно выбирает терминал и проверил скачанный пакет, можно убрать extended attributes только у установленного приложения:
xattr -cr /Applications/MacPulse.app open /Applications/MacPulse.app
Эта команда снимает в том числе метку карантина. Она не заменяет проверку доверия к скачанному файлу и не делает приложение нотарифицированным. Контрольная сумма DMG находится в SHA256SUMS рядом с релизом.
Что дальше
Мне интересно развивать MacPulse как небольшой открытый инструмент для реальных машин, включая старые Intel Mac. Не хочется ни подписки за набор базовых датчиков, ни обещаний «ускорить Mac на 300%» удалением кэша.
Если попробуете проект, полезнее всего отчёт с моделью Mac, версией macOS и описанием недоступного датчика или ошибки. Личные пути, IP и содержимое рабочих файлов в issue прикладывать не нужно. А звезда на GitHub поможет проекту найти свою аудиторию.
Код открыт под MIT. Можно изучать, проверять и предлагать изменения — в том числе спорить с архитектурой. Именно за этим я и принёс его на Хабр.
Комментарии (5)

hapcode
20.10.2026 14:46Вместо iStat Menus есть бесплатный Stats.

max_228_0 Автор
20.10.2026 14:46Stats отличный проект, сам им пользовался! Но MacPulse решает задачу шире: в Stats нет умного клинера диска, деинсталлятора программ с поиском хвостов, тестов железа и виджетов на рабочий стол. Хотелось закрыть сразу всё одной лёгкой утилитой.
bkisonka
Спасибо! Интересно, попробую. Для справки - собственное творчество или с ассистентом в лице ИИ?
max_228_0 Автор
Архитектура, отладка на реальном iMac 2017 и продуктовые решения — мои, а Codex в VS Code закрывал рутину, писал обвязку API и тесты. Без живых тестов на железе нейросеть такое не соберет :)
bkisonka
Да, разумеется! Я прекрасно понимаю. Спасибо, за ответ и интересное приложение :)