Saya mengganti aplikasi dari Objective-C ke Swift, yang saya punya beberapa kategori dengan properti tersimpan, misalnya:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Karena ekstensi Swift tidak menerima properti tersimpan seperti ini, saya tidak tahu bagaimana mempertahankan struktur yang sama dengan kode Objc. Properti yang disimpan sangat penting untuk aplikasi saya dan saya percaya Apple pasti telah menciptakan beberapa solusi untuk melakukannya di Swift.
Seperti yang dikatakan oleh jou, apa yang saya cari sebenarnya menggunakan objek terkait, jadi saya melakukannya (dalam konteks lain):
import Foundation
import QuartzCore
import ObjectiveC
extension CALayer {
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
}
set(newValue) {
objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
var initialPath: CGPathRef! {
get {
return objc_getAssociatedObject(self, "initialPath") as CGPathRef
}
set {
objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
Tapi saya mendapatkan EXC_BAD_ACCESS saat melakukan:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Ada ide?
ios
swift
associated-object
Marcos Duarte
sumber
sumber
Jawaban:
Objek terkait API agak rumit untuk digunakan. Anda dapat menghapus sebagian besar pelat dengan kelas pembantu.
Asalkan Anda dapat "menambahkan" properti ke kelas objektif-c dengan cara yang lebih mudah dibaca:
Adapun solusinya:
sumber
NSNumber
atauNSValue
dan menulis sepasang asesoris tambahan yang akan menjadi tipe yang Anda inginkan (Int, Bool, dll).NSObjectProtocol
[String]?
adalah sebuah struct, ini adalah kasus yang sama seperti untuk Int, Bool. Anda bisa menggunakanNSArray
untuk menyimpan koleksiNSString
instance.Seperti di Objective-C, Anda tidak bisa menambahkan properti tersimpan ke kelas yang ada. Jika Anda memperluas kelas Objective-C (
UIView
pasti salah satu), Anda masih bisa menggunakan Objek Terkait untuk meniru properti tersimpan:untuk Swift 1
Kunci asosiasi adalah pointer yang harus menjadi unik untuk setiap asosiasi. Untuk itu, kami membuat variabel global pribadi dan menggunakan alamat memorinya sebagai kunci dengan
&
operator. Lihat Menggunakan Swift dengan Kakao dan Objective-C pada lebih detail bagaimana pointer ditangani di Swift.DIPERBARUI untuk Swift 2 dan 3
DIPERBARUI untuk Swift 4
Di Swift 4, ini jauh lebih sederhana. Struct Holder akan berisi nilai pribadi yang akan diekspos oleh properti yang dikomputasi ke dunia, memberikan ilusi perilaku properti yang tersimpan sebagai gantinya.
Sumber
sumber
objc_AssociationPolicy
, terima kasih! Saya sudah memperbarui jawabannyaJadi saya pikir saya menemukan metode yang bekerja lebih bersih daripada yang di atas karena tidak memerlukan variabel global. Saya mendapatkannya dari sini: http://nshipster.com/swift-objc-runtime/
Intinya adalah bahwa Anda menggunakan struct seperti:
UPDATE untuk Swift 2
sumber
Solusinya ditunjukkan oleh jou tidak mendukung jenis nilai , ini bekerja dengan baik dengan mereka juga
Pembungkus
Kemungkinan ekstensi Kelas (Contoh penggunaan):
Ini benar-benar solusi yang hebat, saya ingin menambahkan contoh penggunaan lain yang menyertakan struct dan nilai yang bukan opsional. Juga, nilai-nilai AssociatedKey dapat disederhanakan.
sumber
lift
metode danLifted
kelas, mereka harus menggunakangetAssociatedObject
&setAssociatedObject
fungsi. Saya akan menambahkan "Contoh penggunaan" antara tanda kurung di sebelah header demi kejelasan.Anda tidak dapat menentukan kategori (ekstensi Swift) dengan penyimpanan baru; setiap properti tambahan harus dihitung daripada disimpan. Sintaks berfungsi untuk Objective C karena
@property
pada suatu kategori pada dasarnya berarti "Saya akan menyediakan pengambil dan penyetel". Di Swift, Anda harus menentukan sendiri hal ini untuk mendapatkan properti yang dihitung; sesuatu seperti:Harus bekerja dengan baik. Ingat, Anda tidak dapat menyimpan nilai baru di setter, hanya bekerja dengan status kelas yang tersedia.
sumber
$ 0,02 saya Kode ini ditulis dalam Swift 2.0
Saya telah mencoba banyak solusi, dan menemukan ini adalah satu-satunya cara untuk benar-benar memperluas kelas dengan parameter variabel tambahan.
sumber
type 'AssociatedKeys' cannot be defined within a protocol extension
...Mengapa mengandalkan runtime objc? Saya tidak mengerti maksudnya. Dengan menggunakan sesuatu seperti yang berikut ini, Anda akan mencapai perilaku yang hampir sama dari properti tersimpan, dengan hanya menggunakan pendekatan Swift murni :
sumber
Saya lebih suka melakukan kode dalam Swift murni dan tidak mengandalkan warisan Objective-C. Karena itu saya menulis solusi Swift murni dengan dua kelebihan dan dua kekurangan.
Keuntungan:
Kode Swift murni
Bekerja pada kelas dan penyelesaian atau lebih khusus pada
Any
objekKekurangan:
Kode harus memanggil metode
willDeinit()
untuk melepaskan objek yang terhubung ke instance kelas tertentu untuk menghindari kebocoran memoriAnda tidak dapat membuat ekstensi langsung ke UIView untuk contoh ini karena
var frame
ekstensi ke UIView, bukan bagian dari kelas.EDIT:
sumber
extensionPropertyStorage
dibagikan dengan semua instance oleh desain. Ini adalah variabel global (Kamus) yang pertama-tama mende-rujukan instance UILabel (NSObject
) dan kemudian propertinya ([String: Any]
).class
properti;)frame
adalah properti instan. Dari mana datangnya properti kelas?Dengan Kategori Obj-c Anda hanya dapat menambahkan metode, bukan variabel instan.
Dalam contoh Anda, Anda telah menggunakan @property sebagai jalan pintas untuk menambahkan deklarasi metode pengambil dan penyetel. Anda masih perlu menerapkan metode-metode itu.
Demikian pula di Swift, Anda dapat menambahkan ekstensi yang digunakan untuk menambahkan metode contoh, properti yang dihitung, dll. Tetapi tidak properti yang disimpan.
sumber
Saya juga mendapatkan masalah EXC_BAD_ACCESS. Nilai dalam
objc_getAssociatedObject()
danobjc_setAssociatedObject()
harus menjadi Obyek. Danobjc_AssociationPolicy
harus cocok dengan Object.sumber
Saya mencoba menggunakan objc_setAssociatedObject seperti yang disebutkan dalam beberapa jawaban di sini, tetapi setelah gagal dengannya beberapa kali saya melangkah mundur dan menyadari bahwa tidak ada alasan saya membutuhkannya. Meminjam dari beberapa ide di sini, saya datang dengan kode ini yang hanya menyimpan array apa pun data tambahan saya (MyClass dalam contoh ini) diindeks oleh objek yang ingin saya kaitkan dengan:
sumber
Inilah solusi yang disederhanakan dan lebih ekspresif. Ini berfungsi untuk tipe nilai dan referensi. Pendekatan mengangkat diambil dari jawaban @HepaKKes.
Kode asosiasi:
Contoh penggunaan:
1) Buat ekstensi dan kaitkan properti ke sana. Mari kita gunakan properti nilai dan tipe referensi.
2) Sekarang Anda dapat menggunakan properti seperti biasa:
Keterangan lebih lanjut:
sumber
Contoh lain dengan menggunakan Objective-C terkait objek dan properti yang dihitung untuk Swift 3 dan Swift 4
sumber
jika Anda ingin mengatur atribut string khusus ke UIView, ini adalah bagaimana saya melakukannya pada Swift 4
Buat ekstensi UIView
Untuk menggunakan ekstensi ini
sumber
Bagaimana dengan menyimpan peta statis ke kelas yang diperluas seperti ini:
sumber
Saya mencoba menyimpan properti dengan menggunakan objc_getAssociatedObject, objc_setAssociatedObject, tanpa hasil. Tujuan saya adalah membuat ekstensi untuk UITextField, untuk memvalidasi panjang karakter input teks. Kode berikut berfungsi dengan baik untuk saya. Semoga ini bisa membantu seseorang.
sumber
_min
dan_max
bersifat global dan akan sama dalam semua hal di UITextField? Bahkan jika itu bekerja untuk Anda, jawaban ini tidak berhubungan karena Marcos bertanya tentang variabel instan .Berikut adalah alternatif yang berfungsi juga
sumber
Saya menemukan solusi ini lebih praktis
DIPERBARUI untuk Swift 3
... lalu dalam kode Anda
sumber