Objective-C ad alanı çarpışmasını çözmenin en iyi yolu nedir?


174

Objective-C'nin isim alanı yoktur; C'ye çok benziyor, her şey tek bir global isim alanında. Yaygın uygulama, sınıflara baş harfleri önekini eklemektir; örneğin, IBM'de çalışıyorsanız, bunları "IBM" ile ön ek yapabilirsiniz; Microsoft için çalışıyorsanız, "MS" kullanabilirsiniz; ve bunun gibi. Bazen baş harfleri projeye atıfta bulunur, örneğin Adium "AI" ile sınıflara önek ekler (bunun arkasında hiçbir şirket olmadığından baş harflerini alabilirsiniz). Apple, sınıfları NS ile ön ekler ve bu ön ekin yalnızca Apple için ayrıldığını söyler.

Şimdiye kadar çok iyi. Ancak öndeki bir sınıf adına 2 ila 4 harf eklemek çok, çok sınırlı bir ad alanıdır. Örneğin, MS veya AI tamamen farklı anlamlara sahip olabilir (AI Yapay Zeka olabilir) ve diğer bazı geliştiriciler bunları kullanmaya ve eşit derecede adlandırılmış bir sınıf oluşturmaya karar verebilir. patlama , ad alanı çarpışması.

Tamam, eğer bu kendi sınıflarınızdan biri ile kullandığınız harici bir çerçeve arasında bir çarpışma ise, sınıfınızın adını kolayca değiştirebilirsiniz, önemli değil. Peki ya iki dış çerçeve kullanırsanız, her ikisi de kaynağınız olmayan ve değiştiremeyeceğiniz çerçeveler? Uygulamanız her ikisiyle de bağlantı kurar ve ad çakışmaları yaşarsınız. Bunları nasıl çözerdiniz? Her iki sınıfı da kullanabilmeniz için etraflarında çalışmanın en iyi yolu nedir?

C'de doğrudan kütüphaneye bağlanarak bunları çözebilir, bunun yerine kütüphaneyi çalışma zamanında yükler, dlopen () kullanarak, sonra dlsym () kullanarak aradığınız sembolü bulup genel bir sembole ( istediğiniz şekilde adlandırabilirsiniz) ve ardından bu global sembolle erişebilirsiniz. Örneğin, bazı C kütüphanelerinin open () adında bir işlevi olduğu için çakışma yaşıyorsanız, myOpen adında bir değişken tanımlayabilir ve kütüphanenin open () işlevine işaret etmesini sağlayabilirsiniz, böylece sistemi open () kullanmak istediğinizde , sadece open () kullanırsınız ve diğerini kullanmak istediğinizde, myOpen tanımlayıcı üzerinden erişirsiniz.

Objective-C de benzer bir şey mümkün mü ve eğer değilse, ad alanı çatışmalarını çözmek için kullanabileceğiniz başka akıllı, zor bir çözüm var mı? Herhangi bir fikir?


Güncelleme:

Sadece bunu açıklığa kavuşturmak için: ad alanı çarpışmalarından nasıl kaçınılacağını veya daha iyi bir ad alanının nasıl oluşturulacağını öneren cevaplar kesinlikle hoş karşılanır; ancak sorunumu çözmedikleri için cevap olarak kabul etmeyeceğim . İki kütüphanem var ve sınıf isimleri çarpışıyor. Onları değiştiremem; İkisinden birinin kaynağı yok. Çarpışma zaten orada ve bundan nasıl kaçınılabileceğine dair ipuçları artık yardımcı olmayacak. Onları bu çerçevelerin geliştiricilerine iletebilirim ve gelecekte daha iyi bir ad alanı seçtiklerini umuyorum, ancak şimdilik tek bir uygulamada çerçevelerle çalışmak için bir çözüm arıyorum. Bunu mümkün kılmak için herhangi bir çözüm var mı?


7
İyi bir sorunuz var (bir isim çarpışması olan iki çerçeveye ihtiyacınız varsa ne yapmalısınız) ancak metne gömülü. Daha net hale getirmek için gözden geçirin ve şimdi sahip olduğunuz gibi basit cevaplardan kaçınacaksınız.
benzado

4
Bu, Objective-C dilinin şu anki tasarımı ile en büyük tutkum. Aşağıdaki cevaplara bakınız; soruyu gerçekten ele alanlar (NSBundle boşaltma, DO, vb. kullanma), bir ad alanı çakışmasından kaçınmak kadar önemsiz bir şey için gerekli olmayan iğrenç saldırılardır.
erikprice

@erikprice: Amin. Obj-c öğreniyorum ve bu konuya değindim. Burada basit bir çözüm arıyor .... topal.
Dave Mateer

1
Kayıt için, teknik olarak hem C hem de Objective-C, OP'nin tam olarak aradığı şeyi değil, birden çok ad alanı için destek sağlar. Bkz objectivistc.tumblr.com/post/3340816080/...

Hmm, bunu bilmiyordum. Korkunç bir tasarım kararı değil mi?
Nico

Yanıtlar:


47

Her iki çerçeveden de sınıfları aynı anda kullanmanız gerekmiyorsa ve NSBundle yüklemesini (OS X 10.4 veya üstü, GNUStep desteği yok) destekleyen platformları hedefliyorsanız ve performans gerçekten sizin için bir sorun değil, bir sınıfı her kullanmanız gerektiğinde bir çerçeveyi yükleyebilir ve ardından diğer çerçeveyi kullanmanız gerektiğinde diğerini yükleyebilir ve diğerini yükleyebilirsiniz.

İlk fikrim, çerçevelerden birini yüklemek için NSBundle kullanmak, daha sonra bu çerçevenin içindeki sınıfları kopyalamak veya yeniden adlandırmak ve sonra diğer çerçeveyi yüklemekti. Bununla birlikte iki tane sorun var. İlk olarak, bir sınıfı yeniden adlandırmak veya kopyalamak için işaret edilen verileri kopyalamak için bir işlev bulamadım ve bu ilk çerçevede yeniden adlandırılan sınıfa başvuran diğer sınıflar artık sınıfı diğer çerçeveden referans gösterecektir.

Bir IMP tarafından işaretlenen verileri kopyalamanın bir yolu varsa, bir sınıfı kopyalamanız veya yeniden adlandırmanız gerekmez. Yeni bir sınıf oluşturabilir ve ardından ivars, yöntemler, özellikler ve kategoriler üzerine kopyalayabilirsiniz. Çok daha fazla iş, ama mümkün. Ancak, çerçeve içinde yanlış sınıfa gönderme yapan diğer sınıflarla ilgili bir sorun yaşamaya devam edersiniz.

DÜZENLEME: C ve Objective-C çalışma zamanları arasındaki temel fark, anladığım kadarıyla, kütüphaneler yüklendiğinde, bu kütüphanelerdeki işlevler referans verdikleri herhangi bir simgeye işaretçiler içerirken, Objective-C'de thsoe sembollerinin isimleri. Böylece, örneğinizde, sembolün adresini belleğe almak ve başka bir sembole eklemek için dlsym kullanabilirsiniz. Orijinal sembolün adresini değiştirmediğiniz için kütüphanedeki diğer kod hala çalışır. Objective-C sınıf adlarını adreslerle eşlemek için bir arama tablosu kullanır ve bu 1-1 eşlemedir, böylece aynı ada sahip iki sınıfınız olamaz. Bu nedenle, her iki sınıfı da yüklemek için, birinin adının değiştirilmesi gerekir. Ancak, diğer sınıfların bu ada sahip sınıflardan birine erişmesi gerektiğinde,


5
Paket boşaltma işleminin 10.5 veya sonrasına kadar desteklenmediğine inanıyorum .
Quinn Taylor

93

Sınıflarınıza benzersiz bir önek eklemek, temel olarak tek seçenektir, ancak bunu daha az zahmetli ve çirkin yapmanın birkaç yolu vardır. Burada seçenekler hakkında uzun bir tartışma var . Benim favorim @compatibility_aliasObjective-C derleyici yönergesidir ( burada açıklanmıştır ). Sen kullanabilirsiniz @compatibility_aliasFQDN veya bazı tür önek kullanarak sınıfını belirleme olanağı sağlayacağı bir sınıf "yeniden adlandırma" için:

@interface COM_WHATEVER_ClassName : NSObject
@end

@compatibility_alias ClassName COM_WHATEVER_ClassName
// now ClassName is an alias for COM_WHATEVER_ClassName

@implementation ClassName //OK
//blah
@end

ClassName *myClass; //OK

Tam bir stratejinin bir parçası olarak, tüm sınıflarınızı FQDN gibi benzersiz bir önekle ön ekleyebilir ve ardından tümüyle bir başlık oluşturabilirsiniz @compatibility_alias(söz konusu başlığı otomatik olarak oluşturabileceğinizi düşünürdüm).

Bunun gibi önekin dezavantajı COM_WHATEVER_ClassName, derleyicinin yanında bir dizeden sınıf adına ihtiyaç duyan herhangi bir şeye gerçek sınıf adını (örn. Yukarıda) girmeniz gerektiğidir. Özellikle, @compatibility_aliasbir derleyici yönergesi, bir çalışma zamanı işlevi değil, bu yüzden NSClassFromString(ClassName)başarısız olur (dönüş nil) - kullanmanız gerekir NSClassFromString(COM_WHATERVER_ClassName). ibtoolArabirim Oluşturucu ucunda / xib'de sınıf adlarını değiştirmek için derleme aşaması aracılığıyla kullanabilirsiniz , böylece Arabirim Oluşturucu'ya tam COM_WHATEVER _... yazmanız gerekmez.

Son uyarı: çünkü bu bir derleyici yönergesi (ve belirsiz bir yönerge) olduğu için derleyiciler arasında taşınabilir olmayabilir. Özellikle, LLVM projesinden Clang ön ucuyla çalışıp çalışmadığını bilmiyorum, ancak LLVM-GCC (GCC ön ucunu kullanan LLVM) ile çalışması gerekir.


4
Compatibility_alias için oy verin, bunu bilmiyordum! Teşekkürler. Ama sorunumu çözmüyor. Kullandığım her iki çerçevenin öneklerini değiştirmek için kontrolde değilim, sadece ikili biçimde var ve çarpışıyorlar. Bununla ilgili ne yapmalıyım?
Mecki

@Compatibility_alias satırı hangi dosyaya (.h veya .m) gitmeli?
Alex Basson

1
@Alex_Basson @compatibility_alias muhtemelen @interface bildirimi görünür olduğunda görünür olması için başlığa gitmelidir.
Barry Wark

Acaba @compatibility_alias protokol isimleri, veya typedef tanımları, ya da bu konuda başka bir şey kullanabilirsiniz?
Ali

Burada güzel düşünceler. NSClassFromString'in (ClassName) takma sınıflar için nil döndürmesini önlemek için yöntem swizzling kullanılabilir mi? Bir sınıf adı alan tüm yöntemleri bulmak zor olurdu.
Dickey Singh

12

Birkaç kişi, sorunu çözmeye yardımcı olabilecek bazı zor ve akıllı kodları zaten paylaştı. Önerilerin bazıları işe yarayabilir, ancak hepsi idealden daha azdır ve bazıları uygulamak düpedüz kötüdür. (Bazen çirkin hackler kaçınılmazdır, ancak elimden geldiğince kaçınmaya çalışırım.) Pratik bir bakış açısından, benim önerilerim.

  1. Her durumda, geliştiricileri çatışmanın her iki çerçevesi hakkında bilgilendirin ve bunlardan kaçınma ve / veya bununla başa çıkamamalarının size gerçek iş sorunlarına neden olduğunu ve bu da çözülmediyse kaybedilen iş gelirine dönüşebileceğini açıkça belirtin. Sınıf başına mevcut çatışmaları çözerken daha az müdahaleci bir çözüm olduğunu vurgulayın, öneklerini tamamen değiştirmenin (veya şu anda değilse ve bunlardan utanç duymaktan birini kullanmanın) en iyi yol olmadığını vurgulayın aynı sorunu tekrar görmek.
  2. Adlandırma çakışmaları oldukça küçük bir sınıf kümesiyle sınırlıysa, özellikle de çakışan sınıflardan biri doğrudan veya dolaylı olarak kodunuz tarafından kullanılmıyorsa, yalnızca bu sınıflar üzerinde çalışıp çalışmayacağınıza bakın. Öyleyse, satıcının çatışan sınıfları içermeyen çerçevenin özel bir sürümünü sağlayıp sağlamadığını görün. Değilse, esnek olmamalarının yatırım getirinizi çerçevelerini kullanmasını azalttığından emin olun. Neden içinde saldırgan olmak konusunda üzülmeyin - müşteri her zaman haklıdır. ;-)
  3. Bir çerçeve daha "dağıtılabilir" ise, bunu üçüncü taraf veya homebrew gibi başka bir çerçeve (veya kod kombinasyonu) ile değiştirmeyi düşünebilirsiniz. (İkincisi istenmeyen en kötü durumdur, çünkü hem geliştirme hem de bakım için kesinlikle ek iş maliyetleri doğuracaktır.) Bunu yaparsanız, satıcıyı tam olarak çerçevelerini neden kullanmayacağınıza karar verdiğiniz konusunda satıcıya bildirin.
  4. Her iki çerçevenin de uygulamanız için aynı derecede vazgeçilmez olduğu düşünülüyorsa, bunlardan birinin kullanımını bir veya daha fazla ayrı sürece ayırmanın yollarını araştırın, belki de Louis Gerbarg'ın önerdiği gibi DO aracılığıyla iletişim kurun. İletişim derecesine bağlı olarak, bu beklediğiniz kadar kötü olmayabilir. Birçok program (QuickTime dahil, sanırım) , Leopard'da Emniyet Kemeri korumalı alan profilleri kullanarak sağlanan daha ayrıntılı güvenlik sağlamak için bu yaklaşımı kullanır , böylece kodunuzun yalnızca belirli bir alt kümesi kritik veya hassas işlemleri gerçekleştirebilir. Performans bir takas olacaktır, ancak tek seçeneğiniz olabilir

Lisans ücretlerinin, şartlarının ve sürelerinin bu noktaların herhangi birinde anında işlem yapılmasını engelleyebileceğini tahmin ediyorum. Umarım çatışmayı mümkün olan en kısa sürede çözebilirsiniz. İyi şanslar!


8

Bu brüttür, ancak sınıflardan birini yalnızca bir alt program adresinde ve ona RPC'de tutmak için dağıtılmış nesneleri kullanabilirsiniz . Bir sürü şeyi ileri geri geçiriyorsanız dağınık hale gelecektir (ve her iki sınıf da görünümleri doğrudan manipüle ediyorsa, vb. Mümkün olmayabilir).

Başka potansiyel çözümler de var, ancak bunların çoğu kesin duruma bağlı. Özellikle, modern veya eski çalışma zamanlarını mı kullanıyorsunuz, şişman veya tek mimari mi, 32 veya 64 bit mi, hangi OS sürümlerini hedefliyorsunuz, dinamik olarak bağlıyor musunuz, statik olarak bağlanıyor musunuz veya bir seçeneğiniz var mı ve potansiyel olarak mı Tamam, yeni yazılım güncellemeleri için bakım gerektirebilecek bir şey yapmak.

Gerçekten çaresizseniz, yapabileceğiniz şey:

  1. Doğrudan kütüphanelerden birine bağlantı vermeyin
  2. Yükleme zamanında adı değiştiren objc çalışma zamanı yordamlarının alternatif bir sürümünü uygulayın ( objc4 projesini kontrol edin , tam olarak yapmanız gereken şey yukarıda sorduğum bazı sorulara bağlıdır, ancak cevapların ne olduğuna bakılmaksızın mümkün olmalıdır ).
  3. Yeni uygulamanızı enjekte etmek için mach_override gibi bir şey kullanın
  4. Yeni kitaplığı normal yöntemleri kullanarak yükleyin, yamalı bağlayıcı yordamından geçer ve className değiştirilir

Yukarıdakiler oldukça emek yoğun olacak ve birden fazla kemer ve farklı çalışma zamanı sürümlerine karşı uygulamanız gerekiyorsa, çok tatsız olacak, ancak kesinlikle işe yarayabilir.


4

Çakışan sınıflardan birini çarpışmayan bir sınıfa kopyalamak ve sonra da çarpışan sınıf çerçevesini yüklemek için çalışma zamanı işlevlerini (/usr/include/objc/runtime.h) kullanmayı düşündünüz mü? (bu, çarpışan çerçevelerin çalışması için farklı zamanlarda yüklenmesini gerektirir.)

Sınıflar ivars, yöntemler (adlar ve uygulama adresleri ile) ve isimleri çalışma zamanı ile inceleyebilir ve aynı ivar düzenine, yöntem adlarına / uygulama adreslerine sahip olmak ve dinamik olarak kendi adınızı oluşturabilirsiniz (yalnızca çarpışma)


3

Umutsuz durumlar umutsuz önlemler gerektirir. Kütüphanelerden birinin nesne kodunu (veya kütüphane dosyasını) hacklemeyi, çarpışma sembolünü aynı uzunlukta ancak farklı bir yazım (ancak öneri, aynı ad uzunluğu) olan alternatif bir adla değiştirmeyi düşündünüz mü? Doğal olarak kötü.

Kodunuzun aynı adı taşıyan iki işlevi doğrudan çağırıp çağırmadığı, ancak farklı uygulamalar veya çakışmanın dolaylı olup olmadığı açık değildir (ya da herhangi bir fark yaratıp yaratmadığı açık değildir). Ancak, yeniden adlandırmanın işe yaraması için en azından dışarıdan bir şans var. Yazımlardaki farkı en aza indirgemek de bir fikir olabilir, böylece semboller bir tabloda sıralı bir sıradaysa, yeniden adlandırma işleri sıra dışı bırakmaz. İkili arama gibi şeyler, aradıkları dizi beklendiği gibi sıralanmamışsa bozulur.


Diskteki kitaplığın değiştirilmesi söz konusu değildir, çünkü lisans buna izin vermez. Bellekteki sembolleri değiştirmek mümkün olabilir, ancak bunun nasıl yapılacağını göremiyorum (lib'i belleğe yüklemek, değiştirmek ve daha sonra dinamik bağlayıcıya aktarmak ... bunun için hiçbir yöntem görmüyorum).
Mecki

3
Tamam - iki kütüphaneden hangisinin daha az önemli olduğuna karar verme ve yeni bir yer bulma zamanı. Ve ödediğiniz parayı açıklayın, ancak değiştirmenizin nedeninin bu çarpışmadan kaynaklandığını ve sorununuzu çözerek işinizi koruyabildiklerinden vazgeçtiğinizi açıklayın.
Jonathan Leffler

2

@compatibility_alias sınıf ad alanı çakışmalarını çözebilecektir, ör.

@compatibility_alias NewAliasClass OriginalClass;

Ancak, bu numaralandırmalar, typedefs veya protokol ad alanı çakışmaları çözülmez . Ayrıca, @classorijinal sınıfın ileri decls ile iyi oynamıyor. Çoğu çerçeve typedefs gibi bu sınıf dışı şeylerle birlikte geleceğinden, ad alanı sorununu sadece compatibility_alias ile düzeltemezsiniz.

Sizinkine benzer bir soruna baktım , ancak kaynağa erişebildim ve çerçeveleri oluşturuyordum. Bunun için bulduğum en iyi çözüm @compatibility_alias, enums / typedefs / protocols / etc'yi desteklemek için #defines ile koşullu olarak kullanmaktı . Bunu, diğer çakışan çerçevede bir şeyleri genişletme riskini en aza indirmek için söz konusu başlık için derleme biriminde koşullu olarak yapabilirsiniz.


1

Sorun, aynı çeviri birimindeki (kaynak dosya) her iki sistemden de başlık dosyalarına başvuramayacağınız anlaşılıyor. Kitaplıkların etrafında objektif-c sarmalayıcılar oluşturursanız (bunları işlemde daha kullanılabilir hale getirir) ve sarmalayıcı sınıflarının uygulanmasında her kitaplığın üstbilgilerini yalnızca # çarpıştırarak ad çakışmalarını etkili bir şekilde ayırırsanız.

Bu konuda objektif-c'de yeterli deneyimim yok (yeni başlıyorum), ama C'de yapacağımın bu olduğuna inanıyorum.


1
Ancak, her iki sarmalayıcı başlığını aynı dosyaya eklemeyi denediyseniz, yine de çarpışmalara maruz kalmazsınız (çünkü her birinin ilgili çerçevenin başlıklarını içermesi gerekir)?
Wilco

0

Dosyalara ön ek uygulamak, farkında olduğum en basit çözümdür. Cocoadev, ad alanı çarpışmalarını önlemek için bir topluluk çabası olan bir ad alanı sayfasına sahiptir. Bu listeye kendiniz eklemekten çekinmeyin, bunun için olduğuna inanıyorum.

http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix


Dosyalar içinde tanımlanan nesneler soruna neden oluyorsa dosyaların önekinin eklenmesi nasıl yardımcı olur? Kesinlikle h dosyalarını manipüle edebilir, ancak daha sonra bağlayıcı çerçeve karşı sevme artık nesneleri bulamazsınız: - /
Mecki

İnanıyorum ki, bu ilk soruna bir çözümden ziyade en iyi uygulama olacaktır.
Ali

Bu, tüm adreste soru değil
Madbreaks

0

Bir çarpışmanız varsa, başvurunuzdaki çerçevelerden birini nasıl yeniden düzenleyebileceğiniz konusunda çok düşünmenizi öneririm. Bir çarpışma olması, ikisinin de benzer şeyleri yaptığını gösterir ve muhtemelen uygulamanızı yeniden düzenleyerek ekstra bir çerçeve kullanarak etrafta dolaşabilirsiniz. Bu sadece ad alanı sorununuzu çözmekle kalmaz, aynı zamanda kodunuzu daha sağlam, bakımı daha kolay ve daha verimli hale getirir.

Daha teknik bir çözüm üzerinde, ben senin pozisyonunda olsaydım bu benim seçimim olurdu.


0

Çarpışma yalnızca statik bağlantı düzeyindeyse, sembolleri çözmek için hangi kitaplığın kullanılacağını seçebilirsiniz:

cc foo.o -ldog bar.o -lcat

Eğer foo.ove bar.osembol hem referans ratardından libdogçözecektir foo.o'ın ratve libcatçözecektir bar.o' s rat.


0

Sadece bir düşünce .. test edilmemiş veya kanıtlanmamış ve işaretin yolu olabilir ama içinde çerçevelerin daha basit kullandığınız sınıf için bir adaptör yazmayı düşündünüz mü .. ya da en azından arayüzleri?

Çerçevelerin daha basitine (veya en az eriştiğiniz arabirimlere sahip olanın) etrafına bir sargı yazacak olsaydınız, bu sargıyı bir kütüphaneye derlemek mümkün olmazdı. Kütüphane precompiled Verilen ve sadece it'i başlıkları dağıtılacak gerekmez, sen etkin bir altta yatan çerçeveyi gizleme olurdu ve uzlaşmazlığıyı ikinci çerçeve ile birleştirmek ücretsiz olacaktır.

Tabii ki, her iki çerçeveden de sınıfları kullanmanız gereken zamanlar olabileceğini takdir ediyorum, ancak bu çerçevenin daha fazla sınıf bağdaştırıcısı için fabrikalar sağlayabilirsiniz. Bu noktanın arkasında, her iki çerçeveden kullandığınız arayüzleri çıkarmak için biraz yeniden düzenleme yapmanız gerekecek ve bu da ambalajınızı oluşturmak için güzel bir başlangıç ​​noktası sağlayacaktır.

Kitaplığı sizin gibi ve sarılmış kitaplıktan daha fazla işlevselliğe ihtiyaç duyduğunuzda oluşturabilir ve değiştiğinde yeniden derleyebilirsiniz.

Yine, hiçbir şekilde kanıtlanmamış, ancak bir perspektif eklemek gibi hissettim. Umarım yardımcı olur :)


-1

Aynı işlev adına sahip iki çerçeveniz varsa, çerçeveleri dinamik olarak yüklemeyi deneyebilirsiniz. Yetersiz ama mümkün olacak. Objective-C sınıflarıyla nasıl yapıldığını bilmiyorum. NSBundleSınıfın belirli bir sınıfı yükleyecek yöntemlere sahip olacağını tahmin ediyorum .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.