ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ヒープメモリの分析
アプリのダイナミックメモリであるヒープの基本を解説します。InstrumentsとXcodeを使用して、ヒープに関する一般的な問題を測定、分析、修正する方法を確認しましょう。一時的な増加、永続的な増加、リークをアプリで診断するための手法とベストプラクティスもご紹介します。
関連する章
- 0:00 - Introduction
- 1:05 - Heap memory overview
- 3:45 - Tools for inspecting heap memory issues
- 7:40 - Transient memory growth overview
- 10:34 - Managing autorelease pool growth in Swift
- 13:57 - Persistent memory growth overview
- 16:00 - How the Xcode memory graph debugger works
- 20:15 - Reachability and ensuring memory is deallocated appropriately
- 21:54 - Resolving leaks of Swift closure contexts
- 24:13 - Leaks FAQ
- 26:51 - Comparing performance of weak and unowned
- 30:44 - Reducing reference counting overhead
- 32:06 - Cost of measurement
- 32:30 - Wrap up
リソース
関連ビデオ
WWDC24
WWDC22
WWDC21
WWDC18
-
ダウンロードArray
-
-
10:01 - ThumbnailLoader.makeThumbnail(from:) implementation
func makeThumbnail(from photoURL: URL) -> PhotoThumbnail { validate(url: photoURL) var coreImage = CIImage(contentsOf: photoURL)! let sepiaTone = CIFilter.sepiaTone() sepiaTone.inputImage = coreImage sepiaTone.intensity = 0.4 coreImage = sepiaTone.outputImage! let squareSize = min(coreImage.extent.width, coreImage.extent.height) coreImage = coreImage.cropped(to: CGRect(x: 0, y: 0, width: squareSize, height: squareSize)) let targetSize = CGSize(width:64, height:64) let scalingFilter = CIFilter.lanczosScaleTransform() scalingFilter.inputImage = coreImage scalingFilter.scale = Float(targetSize.height / coreImage.extent.height) scalingFilter.aspectRatio = Float(Double(coreImage.extent.width) / Double(coreImage.extent.height)) coreImage = scalingFilter.outputImage! let imageData = context.generateImageData(of: coreImage) return PhotoThumbnail(size: targetSize, data: imageData, url: photoURL) }
-
10:23 - ThumbnailLoader.loadThumbnails(with:), with autorelease pool growth issues
func loadThumbnails(with renderer: ThumbnailRenderer) { for photoURL in urls { renderer.faultThumbnail(from: photoURL) } }
-
10:33 - Simple autorelease example
print("Now is \(Date.now)") // Produces autoreleased .description String
-
11:08 - Autorelease pool growth in loop
autoreleasepool { // ... for _ in 1...1000 { // Autoreleases into single pool, causing growth as loop runs print("Now is \(Date.now)") } // ... }
-
11:50 - Autorelease pool growth in loop, managed by nested pool
autoreleasepool { // ... for _ in 1...1000 { autoreleasepool { // Autoreleases into nested pool, preventing outer pool from bloating print("Now is \(Date.now)") } } // ... }
-
12:16 - ThumbnailLoader.loadThumbnails(with:), with nested autorelease pool growth issues fixed
func loadThumbnails(with renderer: ThumbnailRenderer) { for photoURL in urls { autoreleasepool { renderer.faultThumbnail(from: photoURL) } } }
-
17:27 - C++ class with virtual method
class Coconut { Swallow *swallow; virtual void virtualMethod() {} };
-
17:40 - C++ class without virtual method
class Coconut { Swallow *swallow; };
-
18:41 - ThumbnailRenderer.faultThumbnail(from:), caching thumbnails incorrectly
func faultThumbnail(from photoURL: URL) { // Cache the thumbnail based on url + creationDate let timestamp = UInt64(Date.now.timeIntervalSince1970) // Bad - caching with wrong timestamp let cacheKey = CacheKey(url: photoURL, timestamp: timestamp) let thumbnail = cacheProvider.thumbnail(for: cacheKey) { return makeThumbnail(from: photoURL) } images.append(thumbnail.image) }
-
19:28 - ThumbnailRenderer.faultThumbnail(from:), caching thumbnails correctly
func faultThumbnail(from photoURL: URL) { // Cache the thumbnail based on url + creationDate let timestamp = cacheKeyTimestamp(for: photoURL) // Fixed - caching with correct timestamp let cacheKey = CacheKey(url: photoURL, timestamp: timestamp) let thumbnail = cacheProvider.thumbnail(for: cacheKey) { return makeThumbnail(from: photoURL) } images.append(thumbnail.image) }
-
22:19 - Code creating reference cycle with closure context
let swallow = Swallow() swallow.completion = { print("\(swallow) finished carrying a coconut") }
-
23:11 - PhotosView image loading code, with leak
// ... let renderer = ThumbnailRenderer(style: .vibrant) let loader = ThumbnailLoader(bundle: .main, completionQueue: .main) loader.completionHandler = { self.thumbnails = renderer.images // implicit strong capture of renderer causes strong reference cycle } loader.beginLoading(with: renderer) // ...
-
23:40 - PhotosView image loading code, with leak fixed
// ... let renderer = ThumbnailRenderer(style: .vibrant) let loader = ThumbnailLoader(bundle: .main, completionQueue: .main) loader.completionHandler = { [weak renderer] in guard let renderer else { return } self.thumbnails = renderer.images } loader.beginLoading(with: renderer) // ...
-
24:24 - Intentional leak of manually-managed allocation
let oops = UnsafeMutablePointer<Int>.allocate(capacity: 16) // intentional mistake: missing `oops.deallocate()`
-
25:12 - Loop over intentional leak of manually-managed allocations
for _ in 0..<100 { let oops = UnsafeMutablePointer<Int>.allocate(capacity: 16) // intentional mistake: missing `oops.deallocate()` }
-
26:11 - Nonreturning function which can see leaks of allocations owned by local variables
func beginServer() { let singleton = Server(delegate: self) dispatchMain() // __attribute__((noreturn)) }
-
26:22 - Fix for reported leak in nonreturning function
static var singleton: Server? func beginServer() { Self.singleton = Server(delegate: self) dispatchMain() }
-
27:21 - Weak reference example
weak var holder: Swallow?
-
27:43 - Unowned reference example
unowned let holder: Swallow
-
29:07 - Implicit use of self by method causes reference cycle
class ByteProducer { let data: Data private var generator: ((Data) -> UInt8)? = nil init(data: Data) { self.data = data generator = defaultAction // Implicitly uses `self` } func defaultAction(_ data: Data) -> UInt8 { // ... } }
-
29:25 - Break reference cycle cause day implicit use of self by method, using weak
class ByteProducer { let data: Data private var generator: ((Data) -> UInt8)? = nil init(data: Data) { self.data = data generator = { [weak self] data in return self?.defaultAction(data) } } func defaultAction(_ data: Data) -> UInt8 { // ... } }
-
29:41 - Break reference cycle cause day implicit use of self by method, using unowned
class ByteProducer { let data: Data private var generator: ((Data) -> UInt8)? = nil init(data: Data) { self.data = data generator = { [unowned self] data in return self.defaultAction(data) } } func defaultAction(_ data: Data) -> UInt8 { // ... } }
-
31:14 - Struct with non-trivial init/copy/deinit
struct Nontrivial { var number: Int64 var simple: CGPoint? var complex: String // Copy-on-write, requires non-trivial struct init/copy/destroy }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。