npx claudepluginhub caphtech/claude-marketplace --plugin mobile-pluginWant just this skill?
Then install: npx claudepluginhub u/[userId]/[slug]
iOSアプリのパフォーマンス最適化支援スキル。Instruments活用、メモリ/CPU/GPU使用率分析、アプリ起動時間・バッテリー消費の最適化をサポート。Use when: パフォーマンス問題の調査、アプリ最適化、メモリリーク検出、起動時間短縮、バッテリー消費改善。
This skill uses the workspace's default tool permissions.
references/battery-optimization.mdreferences/cpu-optimization.mdreferences/instruments-guide.mdreferences/launch-optimization.mdreferences/memory-optimization.mdreferences/rendering-optimization.mdiOS Performance(パフォーマンス最適化支援)
概要
iOSアプリケーションのパフォーマンス最適化を包括的に支援するスキル。 Instruments等のツールを活用した計測から、具体的な改善施策の実装までをカバーする。
対象領域
- メモリ使用量: リーク検出、メモリフットプリント最適化
- CPU使用率: 処理の効率化、バックグラウンド処理最適化
- GPU使用率: レンダリング最適化、フレームレート改善
- 起動時間: Cold/Warm起動の高速化
- バッテリー消費: 電力効率の改善
- ネットワーク: 通信効率の最適化
実行条件
以下の状況でこのスキルを起動する:
- アプリの動作が遅いと感じる時
- メモリ使用量が高いと報告された時
- バッテリー消費が多いと指摘された時
- App Store Connect でパフォーマンス警告が出た時
- ユーザーからパフォーマンスに関するフィードバックがあった時
- 新機能追加後のパフォーマンス影響を確認したい時
- リリース前のパフォーマンス検証を行う時
プロセス
Phase 1: パフォーマンス計測
1.1 計測指標の定義
| 指標 | 目標値 | 計測方法 |
|---|---|---|
| 起動時間(Cold) | < 400ms | Instruments / MetricKit |
| 起動時間(Warm) | < 200ms | Instruments / MetricKit |
| メモリ使用量 | < 100MB(基本機能) | Instruments / Xcode Memory Gauge |
| CPU使用率(アイドル) | < 1% | Instruments |
| フレームレート | 60fps / 120fps | Instruments / CADisplayLink |
| バッテリー消費 | 低〜中 | Instruments / MetricKit |
1.2 Instruments の選択
詳細は references/instruments-guide.md を参照。
| 問題 | 推奨 Instrument |
|---|---|
| メモリリーク | Leaks |
| メモリ使用量 | Allocations |
| CPU使用率 | Time Profiler |
| UI性能 | Core Animation / Animation Hitches |
| 起動時間 | App Launch |
| バッテリー | Energy Log |
| ネットワーク | Network |
Phase 2: メモリ最適化
詳細は references/memory-optimization.md を参照。
2.1 メモリリーク検出
// 循環参照の典型例と修正
// Bad: 循環参照
class ViewController: UIViewController {
var completion: (() -> Void)?
func setup() {
completion = {
self.doSomething() // strong reference cycle
}
}
}
// Good: weak self で循環参照を回避
class ViewController: UIViewController {
var completion: (() -> Void)?
func setup() {
completion = { [weak self] in
self?.doSomething()
}
}
}
2.2 メモリフットプリント削減
// 画像メモリ最適化
// Bad: 大きな画像をそのまま保持
let image = UIImage(named: "large_image")
// Good: 適切なサイズにリサイズ
func resizedImage(_ image: UIImage, targetSize: CGSize) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: targetSize))
}
}
// Better: ImageIO でメモリ効率的に読み込み
func downsampledImage(at url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage? {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, imageSourceOptions) else {
return nil
}
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
return nil
}
return UIImage(cgImage: downsampledImage)
}
Phase 3: CPU最適化
詳細は references/cpu-optimization.md を参照。
3.1 Time Profiler による分析
// 重い処理の特定と最適化
// Bad: メインスレッドでの重い処理
func processData() {
let result = heavyComputation(data) // UI がブロックされる
updateUI(with: result)
}
// Good: バックグラウンドスレッドで処理
func processData() {
Task.detached(priority: .userInitiated) {
let result = heavyComputation(data)
await MainActor.run {
updateUI(with: result)
}
}
}
3.2 計算の最適化
// アルゴリズムの改善
// Bad: O(n^2) の検索
func findDuplicates(in array: [Int]) -> [Int] {
var duplicates: [Int] = []
for i in 0..<array.count {
for j in (i+1)..<array.count {
if array[i] == array[j] && !duplicates.contains(array[i]) {
duplicates.append(array[i])
}
}
}
return duplicates
}
// Good: O(n) の検索
func findDuplicates(in array: [Int]) -> [Int] {
var seen = Set<Int>()
var duplicates = Set<Int>()
for element in array {
if seen.contains(element) {
duplicates.insert(element)
} else {
seen.insert(element)
}
}
return Array(duplicates)
}
Phase 4: GPU/レンダリング最適化
詳細は references/rendering-optimization.md を参照。
4.1 フレームドロップの検出
// CADisplayLink でフレームレート監視
class FrameRateMonitor {
private var displayLink: CADisplayLink?
private var lastTimestamp: CFTimeInterval = 0
func start() {
displayLink = CADisplayLink(target: self, selector: #selector(tick))
displayLink?.add(to: .main, forMode: .common)
}
@objc private func tick(_ link: CADisplayLink) {
if lastTimestamp > 0 {
let duration = link.timestamp - lastTimestamp
let fps = 1.0 / duration
if fps < 55 { // 60fps を下回ったら警告
print("Frame drop detected: \(fps) fps")
}
}
lastTimestamp = link.timestamp
}
func stop() {
displayLink?.invalidate()
displayLink = nil
}
}
4.2 オフスクリーンレンダリングの回避
// Bad: オフスクリーンレンダリングを発生させる
view.layer.cornerRadius = 10
view.layer.masksToBounds = true
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 2)
view.layer.shadowOpacity = 0.3
// Good: 別レイヤーで影を描画
view.layer.cornerRadius = 10
view.layer.masksToBounds = true
let shadowView = UIView(frame: view.frame)
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowOffset = CGSize(width: 0, height: 2)
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowPath = UIBezierPath(
roundedRect: view.bounds,
cornerRadius: 10
).cgPath
// Better: SwiftUI の compositingGroup
view
.cornerRadius(10)
.compositingGroup()
.shadow(radius: 5)
Phase 5: 起動時間最適化
詳細は references/launch-optimization.md を参照。
5.1 起動フェーズの理解
Cold Launch:
1. dyld: ライブラリ読み込み
2. Runtime: クラス初期化、+load メソッド
3. UIKit: AppDelegate/SceneDelegate 初期化
4. Initial Frame: 最初の画面描画
Warm Launch:
- プロセスは生きているが、バックグラウンドから復帰
- UIの再構築が必要
5.2 起動時間計測
// アプリ起動時間の計測
class AppLaunchTimer {
static let shared = AppLaunchTimer()
private let processStartTime: CFAbsoluteTime
init() {
// プロセス起動時刻を取得
var kinfo = kinfo_proc()
var size = MemoryLayout<kinfo_proc>.stride
var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
sysctl(&mib, u_int(mib.count), &kinfo, &size, nil, 0)
let startTime = kinfo.kp_proc.p_starttime
processStartTime = CFAbsoluteTime(startTime.tv_sec) + CFAbsoluteTime(startTime.tv_usec) / 1_000_000
}
func markFirstFrame() {
let now = CFAbsoluteTimeGetCurrent()
let launchTime = now - processStartTime
print("App launch time: \(launchTime * 1000) ms")
}
}
// AppDelegate で使用
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 最初のフレーム描画後に計測
DispatchQueue.main.async {
AppLaunchTimer.shared.markFirstFrame()
}
return true
}
5.3 起動最適化テクニック
// 1. 遅延初期化
class HeavyService {
static let shared = HeavyService() // lazy by default
private init() {
// 重い初期化処理
}
}
// 2. バックグラウンドでの初期化
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 起動に必須な処理のみメインスレッドで
setupCriticalServices()
// 非必須の処理はバックグラウンドで
DispatchQueue.global(qos: .utility).async {
self.setupAnalytics()
self.warmUpCache()
self.preloadData()
}
return true
}
// 3. 静的初期化の回避
// Bad: グローバル変数の重い初期化
let heavyFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ja_JP")
formatter.dateStyle = .full
return formatter
}()
// Good: 必要時に初期化
class DateFormatterProvider {
private var _formatter: DateFormatter?
var formatter: DateFormatter {
if _formatter == nil {
let f = DateFormatter()
f.locale = Locale(identifier: "ja_JP")
f.dateStyle = .full
_formatter = f
}
return _formatter!
}
}
Phase 6: バッテリー最適化
詳細は references/battery-optimization.md を参照。
6.1 バッテリー消費の主要因
| 要因 | 影響度 | 対策 |
|---|---|---|
| 位置情報 | 高 | 精度を下げる、バックグラウンド更新を制限 |
| ネットワーク | 高 | バッチ処理、適切なタイミングで通信 |
| CPU | 中 | 処理の効率化、アイドル時の処理削減 |
| GPU | 中 | 不要なアニメーション削減 |
| センサー | 低〜中 | 必要時のみ使用 |
6.2 位置情報の最適化
// Bad: 常に高精度
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
// Good: 用途に応じた精度
// ナビゲーション時
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
// 大まかな位置のみ必要な場合
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
// 重要な位置変化のみ
locationManager.startMonitoringSignificantLocationChanges()
6.3 ネットワーク最適化
// バッチ処理でネットワークアクセスをまとめる
class BatchNetworkManager {
private var pendingRequests: [URLRequest] = []
private var batchTimer: Timer?
func enqueue(_ request: URLRequest) {
pendingRequests.append(request)
// 5秒後にバッチ実行
batchTimer?.invalidate()
batchTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { [weak self] _ in
self?.executeBatch()
}
}
private func executeBatch() {
guard !pendingRequests.isEmpty else { return }
// バッチリクエストを実行
let requests = pendingRequests
pendingRequests = []
// 実行処理...
}
}
Phase 7: MetricKit によるモニタリング
import MetricKit
class MetricsManager: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsManager()
private override init() {
super.init()
MXMetricManager.shared.add(self)
}
// iOS 13+: 日次レポート
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
// 起動時間
if let launchMetrics = payload.applicationLaunchMetrics {
analyzelaunchMetrics(launchMetrics)
}
// メモリ
if let memoryMetrics = payload.memoryMetrics {
analyzeMemoryMetrics(memoryMetrics)
}
// CPU
if let cpuMetrics = payload.cpuMetrics {
analyzeCPUMetrics(cpuMetrics)
}
}
}
// iOS 14+: 診断レポート
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
// クラッシュ
if let crashDiagnostics = payload.crashDiagnostics {
analyzeCrashes(crashDiagnostics)
}
// ハング
if let hangDiagnostics = payload.hangDiagnostics {
analyzeHangs(hangDiagnostics)
}
}
}
private func analyzelaunchMetrics(_ metrics: MXAppLaunchMetric) {
let resumeTime = metrics.histogrammedApplicationResumeTime
let launchTime = metrics.histogrammedTimeToFirstDraw
// 閾値を超えたらアラート
// ...
}
}
出力形式
パフォーマンスレポート
# パフォーマンス分析レポート
## 概要
- 分析日: YYYY-MM-DD
- 対象バージョン: x.x.x
- テストデバイス: iPhone 15 Pro (iOS 17.2)
## 計測結果
### 起動時間
| 種別 | 計測値 | 目標 | 判定 |
|-----|--------|-----|------|
| Cold Launch | 350ms | <400ms | OK |
| Warm Launch | 180ms | <200ms | OK |
### メモリ使用量
| 状態 | 使用量 | 目標 | 判定 |
|-----|--------|-----|------|
| 起動直後 | 45MB | <50MB | OK |
| リスト表示 | 80MB | <100MB | OK |
| ピーク | 150MB | <200MB | OK |
### CPU使用率
| 状態 | 使用率 | 目標 | 判定 |
|-----|--------|-----|------|
| アイドル | 0.5% | <1% | OK |
| スクロール中 | 25% | <30% | OK |
## 検出された問題
### 問題1: メモリリーク
- 場所: ProfileViewController
- 原因: クロージャでのstrong reference cycle
- 影響: 画面遷移ごとに2MB増加
- 推奨修正: [weak self] の追加
### 問題2: フレームドロップ
- 場所: ProductListView
- 原因: セル再利用時の画像デコード
- 影響: スクロール時に55fps以下
- 推奨修正: 画像のプリフェッチと適切なサイズへのダウンサンプリング
## 改善提案
1. **優先度: 高**
- メモリリークの修正
- 推定効果: メモリ使用量 20% 削減
2. **優先度: 中**
- 画像処理の最適化
- 推定効果: スクロール性能 30% 向上
ガードレール
必須遵守事項
- 計測前後の比較: 最適化前後で必ず計測し効果を検証
- 複数デバイスでの検証: 最低スペックのサポートデバイスでテスト
- リリース構成でのテスト: Debug ではなく Release でプロファイリング
- 継続的モニタリング: MetricKit でリリース後も監視
禁止事項
- 早すぎる最適化: 計測なしでの最適化は行わない
- 可読性の犠牲: 極端な最適化で保守性を損なわない
- 機能の削減: パフォーマンスのためにUXを損なわない
警告事項
- シミュレータでの計測: 実機と性能特性が異なる
- デバッグビルド: オーバーヘッドが大きい
- サンプルサイズ: 複数回計測して平均を取る
- 環境要因: バッテリー残量、温度、他アプリの影響
参照
references/instruments-guide.md: Instruments活用ガイドreferences/memory-optimization.md: メモリ最適化詳細references/cpu-optimization.md: CPU最適化詳細references/rendering-optimization.md: レンダリング最適化詳細references/launch-optimization.md: 起動時間最適化詳細references/battery-optimization.md: バッテリー最適化詳細
Similar Skills
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.