Koleksi tampilan yang kompleks di iOS: masalah dan solusi pada contoh umpan VKontakte

Hai Nama saya Sasha, saya adalah pengembang iOS di tim yang membuat umpan VKontakte. Sekarang saya akan memberi tahu Anda bagaimana kami mengoptimalkan tampilan antarmuka dan mengatasi masalah yang terkait dengan ini.
Saya pikir Anda bisa membayangkan apa itu kaset VK. Ini adalah layar tempat Anda dapat melihat berbagai konten: teks, gambar statis, gif animasi, elemen yang disematkan (video dan musik). Semua ini harus ditampilkan dengan lancar, karenanya tuntutan tinggi pada kinerja solusi.


Sekarang mari kita lihat apa pendekatan standar untuk bekerja dengan pemetaan yang ada dan batasan atau keuntungan apa yang harus diperhitungkan.


Jika Anda lebih suka mendengarkan daripada membaca, rekaman video dari laporan ada di sini .



Isi


  1. Deskripsi dan perhitungan tata letak
    1.1. Tata letak otomatis
    1.2. Perhitungan frame manual
  2. Perhitungan ukuran teks
    2.1. Metode Standar untuk Menghitung Ukuran UILabel / UITextView / UITextField
    2.2. Metode NSAttributedString / NSString
    2.3. Textkit
    2.4. Coretext
  3. Bagaimana cara kerja umpan VKontakte?
  4. Cara mendapatkan kinerja yang lebih baik
    4.1 Mengapa masalah kinerja
    4.2. CATransaction.commit
    4.3. Rendering pipeline
    4.4. Tempat paling rentan untuk kinerja
  5. Alat ukur
    5.1. Jejak sistem logam
    5.2. Kami memperbaiki penurunan kinerja dalam kode saat aplikasi sedang berjalan

  • Cara meneliti masalah. Rekomendasi
  • Kesimpulan
  • Sumber informasi

1. Deskripsi dan perhitungan tata letak


Pertama, mari kita ingat cara membuat struktur antarmuka visual ( tata letak ) menggunakan alat biasa. Demi menghemat ruang, kami akan melakukannya tanpa listing - Saya hanya akan daftar solusi dan menjelaskan fitur-fiturnya.


1.1. Tata letak otomatis


Mungkin cara paling populer untuk membuat antarmuka di iOS adalah menggunakan sistem tata letak Auto Apple Layout . Ini didasarkan pada algoritma Cassowary , terkait erat dengan konsep kendala.


Untuk saat ini, ingatlah bahwa antarmuka yang diimplementasikan menggunakan Tata Letak Otomatis dibuat berdasarkan batasan.


Fitur pendekatan:


  • Sistem kendala diubah menjadi masalah pemrograman linier .
  • Kasuari memecahkan masalah optimisasi yang dihasilkan menggunakan metode simpleks . Metode ini memiliki kompleksitas asimtotik eksponensial. Apa artinya ini? Karena jumlah kendala dalam tata letak meningkat, dalam kasus terburuk, perhitungan dapat melambat secara eksponensial.
  • Nilai frame dihasilkan untuk UIView adalah solusi untuk masalah optimasi yang sesuai.

Manfaat menggunakan Tata Letak Otomatis:


  • Pada pemetaan sederhana, kompleksitas komputasi linier dimungkinkan .
  • Ini cocok dengan semua elemen standar, karena merupakan teknologi "asli" dari Apple.
  • Out of the box bekerja dengan UIView .
  • Tersedia di Interface Builder, yang memungkinkan Anda untuk menggambarkan tata letak di Storyboard atau XIB.
  • Ini menjamin solusi terbaru bahkan selama transisi. Ini berarti bahwa nilai frame setiap UIView selalu (!) Solusi untuk tugas tata letak yang sebenarnya.

Kemampuan sistem sudah cukup untuk sebagian besar tampilan. Tetapi tidak cocok untuk membuat kaset dengan jumlah besar konten heterogen. Mengapa


Penting untuk diingat bahwa Tata Letak Otomatis:


  • Hanya berfungsi di utas utama . Misalkan insinyur Apple telah memilih Mainstream sebagai titik sinkronisasi dari solusi Tata Letak Otomatis dan nilai bingkai dari semua UIView . Tanpa ini, Anda harus menghitung Tata Letak Otomatis di utas terpisah dan terus menyinkronkan nilai-nilai dengan utas Utama.
  • Ini dapat bekerja lambat pada representasi kompleks , karena didasarkan pada algoritma brute force yang kompleksitasnya dalam kasus terburuk adalah eksponensial.
  • Tersedia dengan iOS 6.0 . Sekarang ini bukan masalah, tetapi patut dipertimbangkan.

Kesimpulan: menggunakan Tata Letak Otomatis, nyaman untuk membuat tampilan tanpa atau dengan koleksi, tetapi tanpa hubungan yang kompleks antar elemen.


1.2. Perhitungan frame manual


Inti dari pendekatan: kita menghitung sendiri semua nilai frame . Sebagai contoh, kami menerapkan metode layoutSubviews , sizeThatFits . Artinya, dalam layoutSubviews sendiri mengatur semua elemen anak, dalam sizeThatFits kami menghitung ukuran yang sesuai dengan lokasi elemen dan konten anak yang diinginkan.


Apa yang diberikannya? Kami dapat mentransfer perhitungan kompleks ke aliran Latar Belakang, dan perhitungan yang relatif sederhana dapat dilakukan di aliran Utama.


Apa masalahnya? Anda harus mengimplementasikan perhitungan sendiri, mudah untuk membuat kesalahan. Anda juga perlu memastikan bahwa posisi anak-anak dan hasilnya dikembalikan dalam sizeThatFits .


Penilaian diri dibenarkan jika:


  • Kami telah menemukan atau mengantisipasi bahwa kami akan menghadapi keterbatasan kinerja Tata Letak Otomatis.
  • aplikasi memiliki koleksi yang kompleks, dan ada kemungkinan besar elemen yang dikembangkan akan jatuh ke salah satu selnya;
  • kami ingin menghitung ukuran elemen di utas Latar;
  • kami menampilkan elemen non-standar di layar, yang ukurannya harus terus dihitung ulang tergantung pada konten atau lingkungan.

Sebuah contoh Gambar tooltips yang secara otomatis skala agar sesuai dengan konten. Bagian paling menarik dalam tugas ini adalah bagaimana menghitung ukuran visual teks di setiap tooltip.




2. Perhitungan ukuran teks


Masalah ini dapat diselesaikan setidaknya dalam empat cara, yang masing-masing bergantung pada serangkaian metode sendiri. Dan masing-masing memiliki karakteristik dan keterbatasannya sendiri.


2.1. Metode Standar untuk Menghitung Ukuran UILabel / UITextView / UITextField


Metode sizeThatFits (digunakan secara default di sizeToFit ) dan intrinsicContentSize (digunakan dalam Tata Letak Otomatis) mengembalikan ukuran konten tampilan yang disukai. Misalnya, dengan bantuan mereka, kami dapat mengetahui berapa banyak ruang yang digunakan oleh teks yang ditulis dalam UILabel .


Kelemahannya adalah kedua metode hanya bekerja di utas utama - keduanya tidak dapat dipanggil dari latar belakang.


Kapan metode standar bermanfaat?


  • Jika kita sudah menggunakan sizeToFit atau Auto Layout.
  • Ketika ada elemen standar di layar, dan kami ingin mendapatkan ukurannya dalam kode.
  • Untuk tampilan apa pun tanpa koleksi rumit.

2.2. Metode NSAttributedString / NSString


Perhatikan metode boundingRect dan sizeWithAttributes . Saya tidak menyarankan menggunakannya untuk membaca ukuran isi UILabel / UITextView / UITextField . Saya tidak menemukan informasi dokumentasi bahwa metode NSString dan metode tata letak elemen UIView didasarkan pada kode yang sama (kelas yang sama). Kedua kelompok kelas ini memiliki kerangka kerja yang berbeda: Foundation dan UIKit, masing-masing. Mungkin Anda sudah harus mencocokkan hasil boundingRect dengan ukuran UILabel ? Atau pernahkah Anda menemukan fakta bahwa NSString tidak memperhitungkan ukuran emoji ? Ini adalah masalah yang bisa Anda dapatkan.


Saya juga akan memberi tahu Anda kelas mana yang bertanggung jawab untuk menggambar teks di UILabel / UITextView / UITextField , tetapi untuk sekarang UITextField kembali ke metode.


Menggunakan boundingRect dan sizeWithAttributes layak jika kita:


  • Kami menggambar elemen antarmuka non-standar menggunakan drawInRect , drawAtPoint atau metode lain dari kelas NSString / NSAttributedString .
  • Kami ingin mempertimbangkan ukuran elemen dalam aliran Background. Sekali lagi, ini hanya ketika menggunakan metode rendering yang sesuai.
  • Gambarlah pada konteks sewenang-wenang, misalnya, tampilkan garis di atas gambar.

2.3. Textkit


Alat ini terdiri dari kelas standar NLayoutManager , NSTextStorage dan NSTextContainer . Layout UILabel / UITextView / UITextField juga berdasarkan pada mereka.


TextKit sangat nyaman ketika Anda perlu menjelaskan secara rinci lokasi teks dan menunjukkan bentuk mana yang akan mengalir :



Menggunakan TextKit, Anda dapat menghitung ukuran elemen antarmuka dalam antrian latar belakang, serta frame garis / karakter . Selain itu, kerangka kerja ini memungkinkan Anda untuk menggambar mesin terbang dan sepenuhnya mengubah tampilan teks dalam tata letak yang ada. Semua ini berfungsi di iOS 7.0 dan lebih tinggi.


TextKit berguna ketika Anda perlu:


  • menampilkan teks dengan tata letak yang rumit;
  • menggambar teks pada gambar;
  • menghitung ukuran masing-masing substring;
  • hitung jumlah garis;
  • gunakan hasil perhitungan dalam UITextView .

Saya tekankan lagi. Jika Anda perlu menghitung ukuran UITextView , pertama-tama kita mengkonfigurasi instance NSLayoutManager , NSTextStorage , dan NSTextContainer , dan kemudian meneruskan instance ini ke UITextView sesuai , di mana mereka akan bertanggung jawab atas tata letak. Hanya dengan cara ini kami menjamin semua nilai penuh secara kebetulan.


Jangan gunakan TextKit dengan UILabel dan UITextField ! Bagi mereka (tidak seperti UITextView ) Anda tidak dapat mengonfigurasi NSLayoutManager , NSTextStorage , dan NSTextContainer .


2.4. Coretext


Ini adalah alat teks level terendah di iOS. Ini memberikan kontrol maksimum atas rendering font, karakter, garis, indentasi. Dan dia, seperti TextKit, memungkinkan Anda untuk menghitung parameter tipografi teks, seperti garis dasar dan ukuran bingkai garis individual.


Seperti yang Anda tahu, semakin banyak kebebasan, semakin tinggi tanggung jawabnya. Dan untuk mendapatkan hasil yang baik menggunakan CoreText, Anda harus dapat menggunakan metodenya.


CoreText memberikan keamanan utas untuk operasi pada sebagian besar objek. Ini berarti bahwa kita dapat memanggil metodenya dari utas yang berbeda. Sebagai perbandingan, saat menggunakan TextKit, Anda sendiri harus memikirkan urutan pemanggilan metode.


CoreText harus digunakan jika:


  • API tingkat rendah yang sangat sederhana diperlukan untuk akses langsung ke parameter teks. Saya harus segera mengatakan bahwa untuk sebagian besar tugas, kemampuan TextKit sudah cukup.
  • Ada banyak pekerjaan yang harus dilakukan dengan garis individual ( CTLine ) dan karakter / elemen.
  • Dukungan penting di iOS 6.0.

Untuk umpan VKontakte, kami menggunakan CoreText. Mengapa Tepat pada saat kami menerapkan fungsi dasar bekerja dengan teks, TextKit belum ada di sana.




3. Bagaimana cara kerja umpan VKontakte?


Secara singkat tentang cara kami menerima data dari server, tata letak formulir, dan tampilan.



Pertama, pertimbangkan tugas yang dilakukan dalam antrian Latar Belakang. Kami menerima data dari server, memprosesnya dan mendeskripsikan tampilan selanjutnya secara deklaratif. Pada tahap ini, kami belum memiliki instance UIView , kami hanya menetapkan aturan dan struktur antarmuka masa depan dengan alat deklaratif kami, agak mirip dengan SwiftUI . Untuk menghitung tata letak, kami menghitung seluruh frame dengan mempertimbangkan batasan saat ini, misalnya lebar layar. Kami melakukan pembaruan dataSource saat ini ( dataSourceUpdate ). Di sini, dalam antrian Latar Belakang, kami menyiapkan gambar: melakukan dekompresi (lihat bagian kinerja untuk detail lebih lanjut), menggambar bayangan, putaran, dan efek lainnya.


Sekarang, masuklah ke antrian utama. dataSourceUpdate diterima ke UITableView , menggunakan kembali dan memproses acara antarmuka, mengisi sel.


Untuk menggambarkan sistem tata letak kami, artikel terpisah akan diperlukan, tetapi di sini saya akan mencantumkan fitur utamanya:


  • API deklaratif adalah seperangkat aturan yang membangun antarmuka.
  • Komponen dasar membentuk pohon ( nodes ).
  • Perhitungan sederhana dalam komponen dasar. Misalnya, dalam daftar, kami hanya menghitung offset origin , dengan mempertimbangkan lebar / tinggi semua anak.
  • Elemen dasar tidak membuat "wadah" yang tidak perlu dari UIView dalam hierarki. Misalnya, komponen daftar tidak membentuk UIView tambahan dan tidak menambahkan anak ke dalamnya. Alih-alih, kami menghitung offset origin dari anak-anak relatif terhadap elemen induk (untuk daftar).
  • Manajemen teks tingkat rendah dengan CoreText.

Tetapi bahkan dengan pendekatan ini, menonton kaset mungkin tidak lancar karena masalah kinerja. Mengapa


Setiap sel memiliki hierarki nodes kompleks. Dan meskipun elemen dasar tidak membuat wadah yang tidak perlu, banyak UIView masih ditampilkan di pita. Dan ketika mengisi hierarki dengan "node" (view binding) di Main-queue ada pekerjaan tambahan yang sulit untuk dihilangkan.


Kami mencoba mentransfer sebanyak mungkin tugas ke antrean Latar dan sekarang terus melakukannya. Selain itu, ada operasi intensif CPU dan GPU yang harus diperhitungkan dan dielakkan.




4. Cara mencapai kinerja yang lebih baik


Jawaban paling sederhana adalah dengan melepas thread utama, CPU, dan GPU. Untuk melakukan ini, Anda perlu memahami pekerjaan aplikasi-iOS. Dan yang terpenting, identifikasi sumber masalah.


4.1 Mengapa masalah kinerja


Animasi Inti, RunLoop dan Gulir
Mari kita ingat bagaimana antarmuka dibangun di iOS. Di tingkat atas, ada UIKit , yang bertanggung jawab untuk berinteraksi dengan pengguna: menangani gerakan, membangunkan aplikasi dari tidur, dan hal-hal serupa. Untuk rendering antarmuka, alat level bawah bertanggung jawab - Core Animation (seperti pada macOS). Ini adalah kerangka kerja dengan sistem deskripsi antarmuka sendiri. Pertimbangkan konsep dasar membangun antarmuka.


Untuk Core Animation, seluruh antarmuka adalah lapisan CALayer . Mereka membentuk Render Tree, dikelola melalui transaksi transaksi CATransaction .


Transaksi adalah sekelompok perubahan, lebih tepatnya, informasi tentang perlunya memperbarui sesuatu di antarmuka yang ditampilkan. Setiap perubahan untuk frame atau parameter lapisan lainnya jatuh ke dalam transaksi saat ini. Jika belum, sistem itu sendiri menciptakan transaksi implisit .


Beberapa transaksi membentuk tumpukan. Pembaruan baru termasuk dalam transaksi teratas tumpukan.


Sekarang kita tahu bahwa untuk memperbarui layar kita perlu melakukan transaksi dengan parameter baru untuk pohon lapisan.



Kapan dan bagaimana cara membuat transaksi? Dalam aplikasi kami, utas memiliki entitas yang disebut RunLoop . Secara sederhana, ini adalah loop tak terbatas, di setiap iterasi di mana antrian acara saat ini diproses.


Di utas utama, RunLoop diperlukan untuk memproses acara dari berbagai sumber, seperti antarmuka (gerakan), timer, atau, misalnya, penangan untuk menerima data dari NSStream dan NSPort .



Bagaimana Core Animation dan RunLoop ? Kami menemukan di atas bahwa ketika mengubah properti layer di Render Tree, sistem membuat transaksi implisit jika perlu (oleh karena itu, kami tidak perlu memanggil CATransaction.begin untuk menggambar kembali sesuatu). Selanjutnya, pada setiap iterasi RunLoop sistem secara otomatis menutup transaksi terbuka dan menerapkan perubahan yang dilakukan ( CATransaction.commit ).


Perhatikan! Jumlah iterasi RunLoop tidak tergantung pada kecepatan refresh layar. Siklusnya tidak disinkronkan dengan layar sama sekali dan berfungsi seperti "endless while() ".


Sekarang mari kita lihat apa yang terjadi di iterasi RunLoop pada utas utama selama gulir:


  ... if (dispatchBlocks.count > 0) { //   MainQueue doBlocks() } ... if (hasPanEvent) { handlePan() // UIScrollView change content offset -> change bounds } ... if (hasCATransaction) { CATransaction.commit() } ... 

Pertama, blok yang ditambahkan ke antrian utama melalui dispatch_async / dispatch_sync dieksekusi. Dan sampai mereka selesai, program tidak melanjutkan ke tugas-tugas berikut.


Selanjutnya, UIKit mulai memproses gerakan pan pengguna. Sebagai bagian dari pemrosesan isyarat ini, UIScrollView.contentOffset berubah, dan sebagai hasilnya, UIScrollView.bounds . Mengubah bounds - bounds UIScrollView (masing-masing, dan turunannya UITableView , UICollectionView ) memperbarui bagian yang terlihat dari konten ( viewport ).


Pada akhir iterasi RunLoop , jika kita memiliki transaksi terbuka, commit atau flush terjadi secara otomatis.


Untuk memeriksa bagaimana ini bekerja, letakkan breakpoints di tempat yang tepat.
Beginilah proses pemrosesan gerakan:



Dan di sini adalah CATransaction.commit setelah handlePan :



Saat gulir melambat, UIScrollView membuat timer CADisplayLink untuk menyinkronkan jumlah perubahan pada contentOffset per detik dengan kecepatan refresh layar.



Kami CATransaction.commit bahwa CATransaction.commit tidak terjadi pada akhir iterasi RunLoop , tetapi langsung dalam pemrosesan CADisplayLink waktu CADisplayLink . Tapi ini tidak masalah:



4.2. CATransaction.commit


Faktanya, semua operasi di dalam CATransaction.commit dilakukan pada lapisan CALayer . layoutSublayers memiliki metode mereka sendiri untuk memperbarui layout ( layoutSublayers ) dan gambar ( drawLayer ). Implementasi default metode ini menghasilkan panggilan metode delegasi . Dengan menambahkan instance baru UIView ke hierarki UIView , kami secara implisit menambahkan layer yang sesuai ke hierarki layer Core Animation. Dalam hal ini, UIView default merupakan delegasi dari lapisannya. Seperti yang Anda lihat dari tumpukan panggilan, UIView sebagai bagian dari implementasi metode delegasi CALayer , mengeksekusi metodenya, yang akan dibahas:



Karena kami biasanya bekerja dengan hierarki UIView , uraian akan dilanjutkan dengan contoh-contoh UIView .


Selama CATransaction.commit , tata letak semua UIView ditandai dengan setNeedsLayout . Perhatikan bahwa sekali lagi kami sendiri tidak memanggil layoutSubviews atau layoutIfNeeded karena eksekusi yang ditangguhkan mereka dalam sistem di dalam CATransaction.commit . Bahkan jika dalam satu transaksi (antara panggilan ke CATransaction.begin dan CATransaction.commit ) Anda mengubah frame beberapa kali dan memanggil setNeedsLayout , setiap perubahan tidak akan langsung diterapkan. Perubahan akhir hanya akan berlaku setelah memanggil CATransaction.commit . Metode CALayer relevan: setNeedsLayout , layoutIfNeeded , dan layoutSublayers .


Kumpulan serupa untuk menggambar dibentuk oleh metode setNeedsDisplay dan drawRect . Untuk CALayer ini adalah setNeedsDisplay , displayIfNeeded dan drawLayer . CATransaction.commit memanggil metode rendering pada semua elemen yang ditandai dengan setNeedsDisplay . Langkah ini kadang-kadang disebut sebagai gambar di luar layar.


Sebuah contoh Untuk spesifisitas dan kenyamanan, UITableView :


  ... // Layout UITableView.layoutSubviews() //  ,   .. ... // Offscreen drawing UITableView.drawRect() //    ... 

UIKit menggunakan kembali UICollectionView UITableView / UICollectionView di layoutSubviews : memanggil willDisplayCell delegasi willDisplayCell dan seterusnya. Selama CATransaction.commit , terjadi gambar-layar: metode drawInContext dari semua lapisan atau drawRect semua UIView , ditandai sebagai setNeedsDisplay , setNeedsDisplay . Saya perhatikan ketika kami menggambar sesuatu di drawRect , ini terjadi pada utas utama, dan kami sangat perlu mengubah tampilan lapisan untuk bingkai baru. Jelas bahwa solusi semacam itu bisa sangat tidak efisien.


Apa yang terjadi selanjutnya di CATransaction.commit ? Render Tree dikirim ke Render Server.


4.3. Rendering pipeline


Ingat seluruh proses pembentukan bingkai antarmuka di iOS (rendering pipeline [WWDC 2014 Session 419. Grafik dan Animasi Lanjutan untuk Aplikasi iOS]):



Tidak hanya proses aplikasi kita yang bertanggung jawab untuk pembentukan frame - Core Animation juga berfungsi dalam proses sistem terpisah yang disebut Render Server.


Bagaimana frame terbentuk. Kami (atau sistem untuk kami) membuat transaksi baru ( CATransaction ) dalam aplikasi dengan deskripsi perubahan antarmuka, "melakukan" itu dan mentransfernya ke Server Render. Semuanya, di sisi aplikasi, pekerjaan selesai. Selanjutnya, Server Render menerjemahkan transaksi (Render Tree), memanggil perintah yang diperlukan pada chip video, menggambar bingkai baru dan menampilkannya di layar.


Menariknya, saat membuat bingkai, "multithreading" tertentu digunakan. Jika kecepatan refresh layar adalah 60 frame per detik, total frame baru terbentuk bukan di 1/60, tetapi dalam 1/30 detik. Ini karena saat aplikasi sedang mempersiapkan bingkai baru, Server Render masih memproses yang sebelumnya:



Secara kasar, total waktu pembentukan frame sebelum ditampilkan di layar terdiri dari 1/60 detik dalam proses kami untuk pembentukan transaksi dan 1/60 detik dalam proses Render Server selama pemrosesan transaksi.


Saya ingin membuat komentar berikut. Kita dapat memparalelkan gambar lapisan diri kita sendiri dan membuat konten lapisan UIImage / CGImage dalam aliran Background. Setelah itu, di utas utama, Anda perlu menetapkan gambar yang dibuat ke properti CALayer.contents . Dalam hal kinerja, ini adalah pendekatan yang sangat bagus. Pengembanglah yang menggunakannya Tekstur . Tetapi karena kita dapat mengubah konten CALayer.contents Hanya dalam proses menghasilkan transaksi dalam proses aplikasi kita, kita hanya memiliki 1/60 detik pada 60 frame untuk membuat dan mengganti gambar baru, alih-alih 1/30 detik (dengan mempertimbangkan optimisasi akun dan paralelisasi pipa render dengan Server Render dengan Render Server )


Selain itu, Server Render masih dapat menangani pencampuran (lihat di bawah) dan caching lapisan jangka pendek [iOS Core Animation: Advanced Techniques. Nick Lockwood]. 1/60 CALayer.contents , . .


: , .


4.4.


Main-thread



1. ( CATransaction.commit ) - UIView.layoutSubviews UIView (, CALayer ). , layoutSubviews / cellForRow / willDisplayCell .


2. drawInContext / drawRect . - Main- ( CATransaction.commit ) โ€” . , .


3. . . CATransaction.commit , , .


4. . UIImage / CGImage .


5. . Main-thread , scroll. - , UI.


6. Main-. , RunLoop Main- , , Main-. .


GPU



Blending . GPU ( Render Server GPU, ). , , Background-.


. , UIBlurEffect , UIVibrancyEffect , , (Render Pass). , , .


Offscreen rendering (Render Server)



Render Server . , , :



CALayer , , Offscreen rendering. , UIVisualEffect ( , Render Server CPU, GPU).


, .




5.


, , Time Profiler. Metal System Trace โ€” Time Profiler .


5.1. Metal System Trace


, ( ). , : , .


, Metal System Trace , . , Render Server. , Main-, โ€” , .



- , :



Metal System Trace . 64- , iPhone 5s. , . , - , , UI.


5.2.


. , - - . , CADisplayLink .


CADisplayLink timestamp โ€” ( Render Server). CADisplayLink.timestamp timestamp . , (, 1/60 ) :


  //  CADisplayLink. link = [CADisplayLink displayLinkWithTarget:target selector:selector] [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode] //    CADisplayLink : diff = prevTimestamp - link.timestamp if (diff > 1/fps) { //  freeze } prevTimestamp = link.timestamp 

CADisplayLink UITrackingRunLoopMode , .


Rendering Pipeline:


UI-, . ยซยป freezeFrameTimeRate :


 scrollTime //    Scroll freezeFrameTime //    ,  "",       freezeFrameTimeRate = freezeFrameTime / scrollTime 

, - UIView . , ยซยป:



, , ยซ UIView ยป . Mengapa , . , , , : CADisplayLink , Render Server link.timetamp , Render Server , . 60 UI-, Render Server. Render Server , .


, , , Render Server . Metal , Render Server. , , iOS, Render Server .


.


, , . , .


: โ€” ! โ€” .




Kesimpulan


โ€” . , , .





, โ€” . , .


, :


  1. Apple .
  2. Auto Layout .
  3. The Cassowary Linear Arithmetic Constraint Solving Algorithm .
  4. iOS Core Animation: Advanced Techniques. Nick Lockwood.
  5. WWDC 2014 Session 419. Advanced Graphics and Animations for iOS Apps.

Source: https://habr.com/ru/post/id481626/


All Articles