Unity3D: Ubah Delegasi Aplikasi iOS

Saya pikir banyak orang dalam proses mengembangkan game untuk iOS harus menghadapi kenyataan bahwa menjadi perlu untuk menggunakan satu atau beberapa fungsi asli. Mengenai Unity3D, banyak masalah dapat muncul dalam masalah ini: untuk menerapkan beberapa jenis fitur, Anda harus melihat ke arah plugin asli yang ditulis dalam Objective-C. Seseorang pada saat ini segera putus asa dan meninggalkan ide itu. Seseorang mencari solusi yang sudah jadi di AssetStore atau di forum, berharap solusi yang sudah jadi sudah ada. Jika tidak ada solusi siap pakai, maka yang paling gigih dari kita tidak melihat cara lain selain terjun ke jurang pemrograman iOS dan interaksi Unity3D dengan kode Objective-C.

Mereka yang memilih jalan terakhir (walaupun, saya pikir, mereka sendiri tahu), akan menghadapi banyak masalah di jalan yang sulit dan sulit ini:

  • iOS adalah ekosistem yang benar-benar asing dan terisolasi, berkembang dengan caranya sendiri. Minimal, Anda harus meluangkan banyak waktu untuk memahami bagaimana Anda bisa sampai ke aplikasi, dan di mana di kedalaman proyek Xcode yang dihasilkan secara otomatis adalah kode untuk mesin Unity3D untuk berinteraksi dengan komponen asli aplikasi.
  • Objective-C adalah bahasa pemrograman yang agak terpisah dan mirip. Dan ketika berinteraksi dengan kode C ++ dari aplikasi Unity3D, "dialek" bahasa ini, yang disebut Objective-C ++, memasuki adegan. Ada sangat sedikit informasi tentang dia, sebagian besar adalah kuno dan arsip.
  • Protokol interaksi antara aplikasi Unity3D dan iOS tidak dijelaskan dengan baik. Anda harus mengandalkan tutorial para penggemar jaringan yang menulis cara mengembangkan plugin asli yang paling sederhana. Pada saat yang sama, hanya sedikit orang yang menyinggung masalah yang lebih dalam dan masalah yang timbul dari kebutuhan untuk melakukan sesuatu yang rumit.

Mereka yang ingin belajar tentang mekanisme interaksi Unity3D dengan aplikasi iOS, silakan, di bawah cat.

Untuk memperjelas hambatan ketat interaksi Unity3D dengan kode asli, artikel ini menjelaskan aspek interaksi dari delegasi aplikasi iOS dengan kode Unity3D, di mana C ++ dan Objective-C alat itu diimplementasikan, dan bagaimana memodifikasi delegasi aplikasi sendiri. Informasi ini dapat berguna baik untuk pemahaman yang lebih baik tentang mekanisme pertautan Unity3D + iOS, dan untuk penggunaan praktis.

Interaksi antara iOS dan aplikasi


Sebagai pengantar, mari kita lihat bagaimana interaksi aplikasi dengan sistem diimplementasikan di iOS dan sebaliknya. Secara skematis, peluncuran aplikasi iOS terlihat seperti ini:

gambar

Untuk mempelajari mekanisme ini dari sudut pandang kode, aplikasi baru yang dibuat dalam Xcode menggunakan templat "Aplikasi Tampilan Tunggal" cocok.



Memilih template ini, hasilnya akan memberi Anda aplikasi iOS paling sederhana yang dapat berjalan di perangkat atau emulator dan menampilkan layar putih. Xcode akan membantu membuat proyek di mana hanya akan ada 5 file dengan kode sumber (dengan 2 di antaranya adalah file header .h) dan beberapa file tambahan yang tidak menarik bagi kami (pengaturan huruf, konfigurasi, ikon).



Mari kita lihat apa yang bertanggung jawab atas file kode sumber:

  • ViewController.m / ViewController.h - kode sumber tidak terlalu menarik bagi kami. Karena aplikasi Anda memiliki Tampilan (yang tidak diwakili oleh kode, tetapi menggunakan Storyboard), Anda akan memerlukan kelas Controller, yang akan mengontrol Tampilan ini. Secara umum, cara ini Xcode sendiri mendorong kita untuk menggunakan pola MVC. Proyek yang menghasilkan Unity3D tidak akan memiliki file sumber ini.
  • AppDelegate.m / AppDelegate.h adalah delegasi aplikasi Anda. Tempat menarik dalam aplikasi tempat kerja kode aplikasi khusus dimulai.
  • main.m - titik awal aplikasi. Dengan cara aplikasi C / C ++ apa pun, ini berisi fungsi utama, yang dengannya program dimulai.

Sekarang, mari kita lihat kode yang dimulai dengan file main.m :

int main(int argc, char * argv[]) { //1 @autoreleasepool { //2 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); // 3 } } 

Dengan baris 1, semuanya jelas dan tanpa penjelasan, mari beralih ke jalur 2. Ini menunjukkan bahwa siklus hidup aplikasi akan terjadi di dalam kumpulan Autorelease. Menggunakan kumpulan autorelease memberi tahu kami bahwa kami akan mempercayakan pengelolaan memori aplikasi ke kumpulan khusus ini, yaitu, ia akan menangani masalah ketika perlu membebaskan memori untuk variabel tertentu. Cerita tentang manajemen memori pada iOS berada di luar cakupan cerita ini, jadi tidak ada gunanya mempelajari topik ini. Bagi mereka yang tertarik dengan topik ini, Anda dapat menemukan, misalnya, artikel ini .

Mari kita beralih ke jalur 3. Ini memanggil fungsi UIApplicationMain . Parameter startup program (argc, argv) diteruskan ke sana. Kemudian, dalam fungsi ini, ditunjukkan kelas mana yang akan digunakan sebagai kelas utama aplikasi, instance-nya dibuat. Dan, akhirnya, ditunjukkan kelas mana yang akan digunakan sebagai delegasi aplikasi, instansasinya dibuat, koneksi antara instance kelas aplikasi dan delegasinya dikonfigurasikan.

Dalam contoh kita, nil dilewatkan sebagai kelas yang akan mewakili contoh aplikasi - secara kasar, analog lokal adalah nol. Selain nil, Anda dapat lulus kelas khusus yang diwarisi dari aplikasi UIA di sana . Jika nihil ditentukan, maka aplikasi UIA akan digunakan. Kelas ini adalah titik terpusat untuk mengelola dan mengoordinasi pekerjaan aplikasi pada iOS dan merupakan singleton. Dengannya, Anda dapat mempelajari hampir semua hal tentang kondisi aplikasi saat ini, pemberitahuan, jendela, peristiwa yang terjadi dalam sistem itu sendiri yang mempengaruhi aplikasi ini dan banyak lagi. Kelas ini hampir tidak pernah mewarisi. Kami akan membahas penciptaan kelas Delegasi Aplikasi lebih detail.

Buat Delegasi Aplikasi


Indikasi kelas mana yang digunakan sebagai delegasi aplikasi terjadi dalam panggilan fungsi

 NSStringFromClass([AppDelegate class]) 

Mari kita menganalisis panggilan ini dalam beberapa bagian.

 [AppDelegate class] 

Konstruk ini mengembalikan objek dari kelas AppDelegate (yang dideklarasikan di AppDelegate.h / .m), dan fungsi NSStringFromClass mengembalikan nama kelas sebagai string. Kami hanya meneruskan nama string dari kelas yang akan dibuat dan digunakan sebagai delegasi ke fungsi UIApplicationMain. Untuk pemahaman yang lebih baik, baris 3 di file main.m bisa diganti dengan yang berikut:

 return UIApplicationMain(argc, argv, nil, @"AppDelegate"); 

Dan hasil implementasinya akan identik dengan versi aslinya. Rupanya, para pengembang memutuskan untuk datang ke pendekatan ini agar tidak menggunakan string konstan. Dengan pendekatan standar, jika Anda mengganti nama kelas delegasi, parser akan segera melempar kesalahan. Jika Anda menggunakan baris yang biasa, kode akan berhasil dikompilasi, dan Anda akan menerima kesalahan hanya dengan memulai aplikasi.

Mekanisme serupa untuk membuat kelas, hanya menggunakan nama string kelas, dapat mengingatkan Anda tentang Refleksi dari C #. Objective-C dan runtime-nya jauh lebih kuat daripada Refleksi di C #. Ini adalah poin yang cukup penting dalam konteks artikel ini, tetapi akan membutuhkan banyak waktu untuk menggambarkan semua fitur. Namun, kita masih akan bertemu dengan "Refleksi" di Objective-C di bawah ini. Masih memahami konsep delegasi aplikasi dan fungsinya.

Delegasi aplikasi


Semua interaksi aplikasi dengan iOS terjadi di kelas aplikasi UIA. Kelas ini mengambil banyak tanggung jawab - memberitahukan tentang asal mula acara, status aplikasi, dan banyak lagi lainnya. Sebagian besar, perannya adalah memberi tahu. Tetapi ketika sesuatu terjadi dalam sistem, kita harus dapat menanggapi perubahan ini, entah bagaimana, melakukan semacam fungsi khusus. Jika sebuah instance dari kelas aplikasi UIA melakukan ini, praktik ini akan mulai menyerupai pendekatan yang disebut Obyek Ilahi . Karena itu, ada baiknya memikirkan membebaskan kelas ini dari bagian dari tanggung jawabnya.

Untuk tujuan inilah ekosistem iOS menggunakan hal semacam itu sebagai delegasi aplikasi. Dari namanya sendiri, kita dapat menyimpulkan bahwa kita berurusan dengan pola desain seperti Delegasi . Singkatnya, kami hanya memindahkan tanggung jawab untuk memproses respons terhadap peristiwa aplikasi tertentu ke delegasi aplikasi. Untuk tujuan ini, dalam contoh kita, kelas AppDelegate dibuat di mana kita dapat menulis fungsionalitas khusus, sementara meninggalkan kelas aplikasi UIA untuk bekerja dalam mode kotak hitam. Pendekatan ini mungkin tampak kontroversial bagi seseorang dalam hal keindahan desain arsitektur, tetapi penulis iOS sendiri mendorong kami untuk pendekatan ini dan sebagian besar pengembang (jika tidak semua) menggunakannya.

Untuk memverifikasi secara visual seberapa sering selama pekerjaan aplikasi, delegasi aplikasi menerima pesan tertentu, lihat diagram:

gambar

Kotak kuning menunjukkan panggilan dari satu atau lain metode delegasi dalam menanggapi peristiwa tertentu kehidupan aplikasi (siklus hidup aplikasi). Diagram ini hanya mengilustrasikan peristiwa yang terkait dengan perubahan status aplikasi dan tidak menampilkan banyak aspek lain dari tanggung jawab delegasi, seperti menerima pemberitahuan atau berinteraksi dengan kerangka kerja.

Berikut adalah beberapa contoh di mana kita mungkin memerlukan akses ke delegasi aplikasi dari Unity3D:

  1. menangani pemberitahuan push dan lokal
  2. mencatat peristiwa peluncuran aplikasi ke analytics
  3. penentuan cara meluncurkan aplikasi - "bersih" atau keluar dari latar belakang
  4. bagaimana aplikasi diluncurkan - dengan tach untuk notifikasi, menggunakan Tindakan Cepat Layar Utama atau hanya dengan tach on incon
  5. interaksi dengan WatchKit atau HealthKit
  6. membuka dan memproses URL dari aplikasi lain. Jika URL ini berlaku untuk aplikasi Anda, Anda dapat memprosesnya di aplikasi Anda alih-alih membiarkan sistem membuka URL itu di browser

Ini bukan daftar seluruh skenario. Selain itu, perlu dicatat bahwa delegasi memodifikasi banyak analitik dan sistem periklanan di plugin asli mereka.

Bagaimana Unity3D mengimplementasikan delegasi aplikasi


Sekarang mari kita lihat proyek Xcode yang dihasilkan oleh Unity3D dan cari tahu bagaimana delegasi aplikasi diimplementasikan di Unity3D. Saat membangun untuk platform iOS, Unity3D secara otomatis menghasilkan proyek Xcode untuk Anda, yang menggunakan banyak kode boilerplate. Kode templat ini juga termasuk kode Delegasi Aplikasi. Di dalam proyek yang dibuat, Anda dapat menemukan file UnityAppController.h dan UnityAppController.mm . File-file ini berisi kode kelas UnityAppController yang menarik minat kami.

Bahkan, Unity3D menggunakan versi modifikasi dari template "Aplikasi Tampilan Tunggal". Hanya dalam templat ini, Unity3D menggunakan delegasi aplikasi tidak hanya untuk menangani acara iOS, tetapi juga untuk menginisialisasi mesin itu sendiri, menyiapkan komponen grafis, dan banyak lagi. Ini sangat mudah dimengerti jika Anda melihat metodenya.

 - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions 

dalam kode kelas UnityAppController. Metode ini dipanggil pada saat inisialisasi aplikasi, ketika Anda dapat mentransfer kontrol ke kode kustom Anda. Di dalam metode ini, misalnya, Anda dapat menemukan baris berikut:

 UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]); [self selectRenderingAPI]; [UnityRenderingView InitializeForAPI: self.renderingAPI]; _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds]; _unityView = [self createUnityView]; [DisplayManager Initialize]; _mainDisplay = [DisplayManager Instance].mainDisplay; [_mainDisplay createWithWindow: _window andView: _unityView]; [self createUI]; [self preStartUnity]; 

Tanpa merinci apa yang menjadi tantangan ini, Anda dapat menebak bahwa itu terkait dengan persiapan Unity3D untuk bekerja. Ternyata skenario berikut:

  1. Fungsi utama dipanggil dari main.mm
  2. Kelas instance aplikasi dan delegasinya dibuat.
  3. Delegasi aplikasi mempersiapkan dan meluncurkan mesin Unity3D
  4. Kode khusus Anda mulai berfungsi. Jika Anda menggunakan il2cpp, maka kode Anda diterjemahkan dari C # ke IL dan kemudian ke kode C ++, yang langsung masuk ke proyek Xcode.

Skrip ini terdengar sangat sederhana dan logis, tetapi membawa masalah potensial: bagaimana kita dapat memodifikasi delegasi aplikasi jika kita tidak memiliki akses ke kode sumber saat bekerja di Unity3D?

Unity3D yang terkena dampak untuk memodifikasi delegasi aplikasi


Kita dapat melihat file AppDelegateListener.mm/.h . Mereka berisi makro yang memungkinkan Anda untuk mendaftarkan kelas apa pun sebagai pendengar acara untuk delegasi aplikasi. Ini adalah pendekatan yang baik, kita tidak perlu mengubah kode yang ada, tetapi tambahkan saja yang baru. Tetapi memiliki kelemahan yang signifikan: tidak semua acara aplikasi didukung dan tidak ada cara untuk mendapatkan informasi tentang peluncuran aplikasi.

Namun, jalan keluar yang paling jelas adalah untuk mengubah kode sumber delegasi dengan tangan setelah Unity3D membangun proyek Xcode. Masalah dengan pendekatan ini jelas - opsi ini cocok jika Anda membuat rakitan dengan tangan Anda dan Anda tidak bingung dengan kebutuhan untuk memodifikasi kode secara manual setelah setiap rakitan. Dalam hal menggunakan pembangun (Unity Cloud Build atau mesin build lainnya), opsi ini benar-benar tidak dapat diterima. Untuk tujuan ini, pengembang Unity3D memberi kami celah.

File UnityAppController.h , selain mendeklarasikan variabel dan metode, juga mengandung definisi makro:

 #define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) ... 

Makro ini hanya memungkinkan untuk mengabaikan delegasi aplikasi. Untuk melakukan ini, Anda perlu mengambil beberapa langkah sederhana:

  1. Tuliskan delegasi aplikasi Anda sendiri di Objective-C
  2. Di suatu tempat di dalam kode sumber tambahkan baris berikut
     IMPL_APP_CONTROLLER_SUBCLASS(___) 
  3. Masukkan sumber ini ke dalam folder Plugins / iOS proyek Unity3D Anda

Sekarang Anda akan menerima proyek di mana delegasi aplikasi Unity3D standar akan diganti dengan proyek khusus Anda.

Bagaimana cara kerja delegasi pengganti makro


Mari kita lihat kode sumber lengkap makro:

 #define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) ... @interface ClassName(OverrideAppDelegate) \ { \ } \ +(void)load; \ @end \ @implementation ClassName(OverrideAppDelegate) \ +(void)load \ { \ extern const char* AppControllerClassName; \ AppControllerClassName = #ClassName; \ } \ @end 

Menggunakan makro ini di sumber Anda akan menambahkan kode yang dijelaskan dalam makro ke tubuh sumber Anda pada tahap kompilasi. Makro ini melakukan hal berikut. Pertama, ini akan menambahkan metode memuat ke antarmuka kelas Anda. Antarmuka dalam konteks Objective-C dapat dianggap sebagai kumpulan bidang dan metode publik. Di C #, metode beban statis akan muncul di kelas Anda yang tidak menghasilkan apa-apa. Selanjutnya, implementasi metode pemuatan ini akan ditambahkan ke kode kelas Anda. Dalam metode ini, variabel AppControllerClassName akan dideklarasikan, yang merupakan array dari tipe char dan kemudian variabel ini akan diberi nilai. Nilai ini adalah nama string kelas Anda. Jelas, informasi ini tidak cukup untuk memahami mekanisme operasi makro ini, oleh karena itu, kita harus memahami apa metode "memuat" ini dan mengapa suatu variabel dinyatakan.

Dokumentasi resmi mengatakan bahwa load adalah metode khusus yang dipanggil satu kali untuk setiap kelas (khususnya kelas, bukan instansnya) pada tahap awal peluncuran aplikasi, bahkan sebelum fungsi utama dipanggil. Lingkungan runtime Objective-c (runtime) pada saat startup aplikasi akan mendaftarkan semua kelas yang akan digunakan selama operasi aplikasi dan akan memanggil metode load di dalamnya, jika diimplementasikan. Ternyata bahkan sebelum dimulainya kode apa pun dalam aplikasi kita, variabel AppControllerClassName akan ditambahkan ke kelas Anda.

Maka Anda mungkin berpikir: "Dan apa gunanya memiliki variabel ini jika dinyatakan di dalam metode dan akan dihapus dari memori ketika Anda keluar dari metode ini?". Jawaban atas pertanyaan ini terletak sedikit di luar batas Objective-C.

Dan di mana C ++?


Mari kita lihat lagi deklarasi variabel ini

 extern const char* AppControllerClassName; 

Satu-satunya hal yang mungkin tidak dapat dipahami dalam deklarasi ini adalah pengubah eksternal. Jika Anda mencoba menggunakan pengubah ini di Objective-C murni, maka Xcode akan melempar kesalahan. Faktanya adalah bahwa pengubah ini bukan bagian dari Objective-C, melainkan diimplementasikan dalam C ++. Objective-C dapat secara ringkas dijelaskan dengan mengatakan bahwa itu adalah "bahasa C dengan kelas." Ini adalah ekstensi dari bahasa C dan memungkinkan penggunaan kode C tanpa batas diselingi dengan kode Objective-C.

Namun, untuk menggunakan fitur extern dan C ++ lainnya, Anda perlu melakukan beberapa trik - gunakan Objective-C ++. Praktis tidak ada informasi tentang bahasa ini, karena fakta bahwa itu hanya kode Objective-C yang memungkinkan penyisipan kode C ++. Agar kompiler mempertimbangkan bahwa beberapa file sumber harus dikompilasi sebagai Objective-C ++, dan bukan Objective-C, Anda hanya perlu mengubah ekstensi file ini dari .m ke .mm .

Pengubah extern itu sendiri digunakan untuk mendeklarasikan variabel global. Lebih tepatnya, untuk memberitahu kompiler β€œPercayalah, variabel seperti itu ada, tetapi memori untuk itu dialokasikan tidak di sini, tetapi di sumber lain. Dan dia juga memiliki nilai, saya jamin. ” Dengan demikian, baris kode kita hanya menciptakan variabel global dan menyimpan nama kelas khusus kita di dalamnya. Tetap hanya untuk memahami di mana variabel ini dapat digunakan.

Kembali ke utama


Kami ingat apa yang dikatakan sebelumnya - delegasi aplikasi dibuat dengan menentukan nama kelas. Jika delegasi dibuat menggunakan nilai konstan [kelas myClass] dalam template proyek Xcode biasa, maka, tampaknya, orang-orang dari Unity memutuskan bahwa nilai ini harus dibungkus dalam variabel. Menggunakan metode poke ilmiah, kami mengambil proyek Xcode yang dihasilkan oleh Unity3D dan pergi ke file main.mm.

Di dalamnya kita melihat kode yang lebih kompleks daripada sebelumnya, bagian dari kode ini hilang sebagai tidak perlu:

 // WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value) const char* AppControllerClassName = "UnityAppController"; int main(int argc, char* argv[]) { ... UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: AppControllerClassName]); } return 0; } 

Di sini kita melihat deklarasi dari variabel ini, dan pembuatan delegasi aplikasi dengan bantuannya.
Jika kami membuat delegasi khusus, maka variabel yang diperlukan ada dan sudah penting - nama kelas kami. Mendeklarasikan dan menginisialisasi variabel sebelum fungsi utama memastikan bahwa ia memiliki nilai default - UnityAppController.

Sekarang dengan keputusan ini semuanya harus sangat jelas.

Masalah makro


Tentu saja, untuk sebagian besar situasi, menggunakan makro ini adalah solusi yang bagus. Tetapi perlu dicatat bahwa ada jebakan besar di dalamnya: Anda tidak dapat memiliki lebih dari satu delegasi khusus. Ini terjadi karena jika 2 kelas atau lebih menggunakan makro IMPL_APP_CONTROLLER_SUBCLASS (ClassName), maka untuk yang pertama nilai dari variabel yang kita perlukan akan ditugaskan, dan penugasan lebih lanjut akan diabaikan. Dan variabel ini adalah string, yaitu, tidak dapat ditetapkan lebih dari satu nilai.

Anda mungkin berpikir bahwa masalah ini merosot dan tidak mungkin dalam praktiknya. Tetapi, artikel ini tidak akan terjadi jika masalah seperti itu tidak benar-benar terjadi, dan bahkan dalam keadaan yang sangat aneh. Situasinya mungkin sebagai berikut. Anda memiliki proyek di mana Anda menggunakan banyak analitik dan layanan iklan. Banyak dari layanan ini memiliki komponen Objective-C. Mereka telah berada di proyek Anda untuk waktu yang lama dan Anda tidak tahu masalah dengan mereka. Di sini Anda perlu menulis delegasi khusus. Anda menggunakan makro ajaib yang dirancang untuk menyelamatkan Anda dari masalah, membangun proyek, dan mendapatkan laporan tentang keberhasilan perakitan. Jalankan proyek di perangkat, dan fungsionalitas Anda tidak berfungsi dan Anda tidak menerima satu kesalahan pun.

Dan masalahnya mungkin bahwa salah satu plugin iklan atau analitik menggunakan makro yang sama. Misalnya, dalam plugin dari AppsFlyer , makro ini digunakan.

Berapa nilai variabel extern dalam kasus deklarasi berganda?


Sangat menarik untuk mengetahui apakah variabel ekstern yang sama dideklarasikan dalam beberapa file, dan mereka diinisialisasi dengan cara makro kita (dalam metode memuat), lalu bagaimana kita bisa memahami nilai apa yang akan diambil oleh variabel? Untuk memahami polanya, aplikasi uji sederhana telah dibuat, kode yang dapat ditemukan di sini .

Inti dari aplikasi ini sederhana. Ada 2 kelas A dan B, di kedua kelas variabel extern AexternVar dideklarasikan, ditugaskan nilai tertentu. Nilai-nilai variabel di kelas diatur secara berbeda. Dalam fungsi utama, nilai variabel ini dicatat. Ditemukan secara eksperimental bahwa nilai variabel tergantung pada urutan sumber ditambahkan ke proyek. Urutan runtime Objective-C mendaftarkan kelas selama eksekusi aplikasi tergantung pada ini. Jika Anda ingin mengulangi percobaan, buka proyek dan pilih tab Bangun Fase di pengaturan proyek. Karena proyek ini uji dan kecil, ia hanya memiliki 8 kode sumber. Semuanya ada di tab Bangun Fase di daftar Sumber Kompilasi.



Jika dalam daftar ini sumber kelas A lebih tinggi dari sumber kelas B, maka variabel akan mengambil nilai dari kelas B. Jika tidak, variabel akan mengambil nilai dari kelas A.

Bayangkan saja berapa banyak masalah yang secara teoritis dapat menyebabkan ini adalah nuansa kecil. Terutama jika proyek ini sangat besar, dihasilkan secara otomatis dan Anda tidak tahu di kelas mana variabel seperti itu dideklarasikan.

Pemecahan masalah


Sebelumnya dalam artikel tersebut, dikatakan bahwa Objective-C akan memberikan C # Reflection sebagai permulaan. Khususnya, untuk menyelesaikan masalah kami, Anda dapat menggunakan mekanisme yang disebut Metode Swizzling . Inti dari mekanisme ini adalah bahwa kita memiliki kesempatan untuk mengganti implementasi metode kelas mana pun dengan yang lain selama aplikasi. Dengan demikian, kita dapat mengganti metode yang diminati di UnityAppController dengan yang kustom. Kami mengambil implementasi yang ada dan melengkapi kode yang kami butuhkan. Kami menulis kode yang menggantikan implementasi metode yang ada dengan yang kami butuhkan. Selama pekerjaan aplikasi, delegasi yang menggunakan makro akan bekerja seperti sebelumnya, menyebut implementasi dasar UnityAppController, dan di sana metode kustom kami akan ikut bermain dan kami akan mencapai hasil yang diinginkan. Pendekatan ini ditulis dengan baik dan diilustrasikan dalam artikel ini . Dengan teknik ini, kita dapat membuat kelas tambahan - analog dari delegasi khusus.Di kelas ini, kita akan menulis semua kode khusus, menjadikan kelas khusus semacam Wrapper untuk memanggil fungsionalitas kelas lain. Pendekatan ini akan berhasil, tetapi sangat implisit karena fakta bahwa sulit untuk melacak di mana metode tersebut diganti dan apa konsekuensi yang akan ditimbulkannya.

Solusi lain untuk masalah ini


Aspek utama dari masalah yang terjadi adalah bahwa ada banyak delegasi khusus, atau Anda hanya dapat memiliki satu, atau sebagian menggantinya dengan yang kedua. Pada saat yang sama, tidak ada cara untuk memastikan bahwa kode delegasi khusus tidak masuk ke file sumber yang berbeda. Ternyata situasinya dapat dianggap sebagai referensi ketika hanya ada satu delegasi dalam aplikasi, Anda harus dapat membuat kelas kustom sebanyak yang Anda suka, sementara tidak satu pun dari kelas ini menggunakan makro untuk menghindari masalah.

Masalahnya kecil, masih untuk menentukan bagaimana hal ini dapat dilakukan menggunakan Unity3D, sambil meninggalkan kemampuan untuk membangun proyek menggunakan mesin build. Algoritma solusinya adalah sebagai berikut:

  1. Kami menulis delegasi khusus dalam jumlah yang diperlukan, membagi logika plugin ke dalam kelas yang berbeda, mengamati prinsip-prinsip SOLID dan tidak menggunakan kecanggihan.
  2. UnityAppController XCode . UnityAppController .
  3. UnityAppController Unity .
  4. XCode UnityAppController ,

Barang yang paling sulit dari daftar ini tidak diragukan lagi adalah yang terakhir. Namun, fitur ini dapat diimplementasikan di Unity3D menggunakan skrip build post-process. Skrip semacam itu ditulis pada suatu malam yang indah, Anda dapat menontonnya di GitHub .

Pasca proses ini cukup mudah digunakan, pilihlah dalam proyek Unity. Lihat di jendela Inspektur dan lihat bidang di sana bernama NewDelegateFile. Seret dan letakkan UnityAppController Anda yang dimodifikasi ke dalam bidang ini dan simpan.



Saat membangun proyek iOS, delegasi standar akan diganti dengan yang dimodifikasi, dan tidak diperlukan intervensi manual. Sekarang, ketika menambahkan delegasi khusus ke proyek, Anda hanya perlu mengubah opsi UnityAppController yang ada di proyek Unity Anda.

PS


Terima kasih kepada semua orang yang sampai pada akhirnya, artikelnya ternyata sangat panjang. Saya harap informasi yang dilukis sangat membantu.

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


All Articles