Objective-C: id ve void arasındaki fark *


Yanıtlar:


240

void * "türlenmemiş / bilinmeyen içerikli bazı rastgele yığın belleklere referans"

id "Sınıfı bilinmeyen bazı rastgele Objective-C nesnelerine başvuru"

Ek anlamsal farklılıklar vardır:

  • Yalnızca GC veya GC Destekli modlar altında, derleyici tür referansları için yazma engelleri gönderir id, ancak tür için değil void *. Yapıları bildirirken, bu kritik bir fark olabilir. İVars gibi bildirmek , aslında bir nesne ise void *_superPrivateDoNotTouch;nesnelerin erken biçilmesine neden olur _superPrivateDoNotTouch. Bunu yapma.

  • void *tür referansı üzerinde bir yöntemi çağırmaya çalışmak derleyici uyarısını engeller.

  • bir idtür üzerinde bir yöntem çağırmaya çalışmak , yalnızca çağrılan yöntem @interfacederleyici tarafından görülen hiçbir bildirimde bildirilmemişse uyarır .

Dolayısıyla, bir nesneye asla bir void *. Benzer şekilde, idbir nesneyi belirtmek için yazılan bir değişken kullanmaktan kaçınılmalıdır . Yapabileceğiniz en spesifik sınıf tipi referansı kullanın. Hatta NSObject *daha iyi olduğu idderleyici, en azından, bu referansa karşı metot çağrımı daha iyi bir kanıt sağlar, çünkü.

Bir yaygın ve geçerli kullanımı, void *başka bir API'den geçirilen opak bir veri referansıdır.

Şu sortedArrayUsingFunction: context:yöntemi düşünün NSArray:

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;

Sıralama işlevi şu şekilde bildirilir:

NSInteger mySortFunc(id left, id right, void *context) { ...; }

Bu durumda, NSArray yalnızca contextargüman olarak geçtiğiniz her şeyi yönteme contextargüman olarak iletir. NSArray söz konusu olduğunda, işaretçi boyutundaki verilerin opak bir parçası ve istediğiniz herhangi bir amaç için kullanmakta özgürsünüz.

Dilde bir kapatma türü özelliği olmadan, bir işlevle bir veri yığınını taşımanın tek yolu budur. Misal; mySortFunc () öğesinin koşullu olarak büyük / küçük harfe duyarlı veya büyük / küçük harfe duyarlı olmamasını istiyorsanız, yine de iş parçacığı için güvenli olsa da, büyük / küçük harf duyarlı göstergeyi bağlamda geçirirsiniz, büyük olasılıkla içeri ve dışarı çıkarken döküm yapar.

Kırılgan ve hata eğilimli, ancak tek yol.

Bloklar bunu çözer - Bloklar C için kapanır. Clang'da mevcuttur - http://llvm.org/ ve Kar Leoparı'nda yaygındır ( http://developer.apple.com/library/ios/documentation/Performance /Referans/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).


Buna ek olarak, eminim bir idcevap vermek için varsayılır -retainve -releaseoysa a void*callee tamamen opak. -performSelector:withObject:afterDelay:(Nesneyi korur) seçeneğine rastgele bir işaretçi +[UIView beginAnimations:context:]iletemezsiniz ve bağlamı koruyacağını varsayamazsınız (animasyon delegesi içeriğin sahipliğini korumalıdır; UIKit animasyon delegesini korur).
tc.

3
Bir idyanıtın neye yanıt verdiği hakkında hiçbir varsayım yapılamaz . Bir id, kendiliğinden olmayan bir sınıf örneğine kolayca başvurabilir NSObject. Ancak pratik olarak, ifadeniz gerçek dünyadaki davranışlarla en iyi şekilde eşleşir; <NSObject>uygulanmayan sınıfları Foundation API ile karıştıramazsınız ve çok uzağa gidemezsiniz , bu kesinlikle kesin!
Barbekü

2
Şimdi idve Classtürleri ARC altında korunabilir nesne işaretçisi olarak kabul edilmektedir . Dolayısıyla, varsayım en azından ARC altında doğrudur.
eonil

"Erken hasat" nedir?
Brad Thomas

1
@BradThomas Bir çöp toplayıcı program tamamlanmadan önce bellek topladığında.
bbum

21

id, objid C nesnesinin bir göstergesidir; burada void *, herhangi bir şeyin göstergesidir.

id ayrıca bilinmeyen mthod'ları çağırmayla ilgili uyarıları da kapatır, örneğin:

[(id)obj doSomethingWeirdYouveNeverHeardOf];

bilinmeyen yöntemler hakkında olağan uyarı vermeyecektir. Nesnel sıfır değilse veya gerçekten bu yöntemi uygulamadığı sürece, çalışma zamanında bir istisna oluşturacaktır.

Genellikle NSObject*veya en azından geri döndürülen nesnenin bir Kakao nesnesi olduğunu onaylayan veya id<NSObject>tercih ettiğiniz şekilde id, üzerinde tutma / bırakma / otomatik serbest bırakma gibi yöntemleri güvenle kullanabilirsiniz.


2
Yöntem çağrıları için, hedeflenen yöntem hiçbir yerde bildirilmemişse, tür (id) türünde bir hedef uyarı oluşturur. Böylece, örneğin, doSomethingWeirdYouveNeverHeardOf bir uyarı olmaması için bir yerde ilan edilmiş olmalıydı.
bbum

Sen haklısın, daha iyi bir örnek storagePolicy gibi bir şey olurdu.
Peter N Lewis

@PeterNLewis Bunun Often you should use NSObject*yerine katılmıyorum id. Belirterek NSObject*aslında nesnenin bir NSObject olduğunu açıkça söylüyorsunuz. Nesneye yapılan herhangi bir yöntem çağrısı bir uyarı ile sonuçlanır, ancak bu nesne gerçekten yöntem çağrısına yanıt verdiği sürece çalışma zamanı istisnası yoktur. Uyarı açıkçası sinir bozucu yani iddaha iyidir. id<MKAnnotation>Kabadan, örneğin bu durumda nesne ne olursa olsun, MKAnnotation protokolüne uyması gerektiğini söyleyerek daha spesifik olabilirsiniz .
pnizzle

1
<MKAnnotation> kimliğini kullanacaksanız, NSObject <MKAnnotation> * 'ı da kullanabilirsiniz. Bu, daha sonra MKAnnotation'daki yöntemlerden herhangi birini ve NSObject'teki yöntemlerden herhangi birini (yani, normal NSObject kök sınıfı hiyerarşisindeki tüm nesneler) kullanmanıza ve başka hiçbir şey için uyarı almanıza izin verir. bir çalışma zamanı çökmesi.
Peter N Lewis

8

Bir yöntemin bir dönüş türü idvarsa, herhangi bir Objective-C nesnesi döndürebilirsiniz.

void yani yöntem hiçbir şey döndürmez.

void *sadece bir işaretçi. İşaretçinin işaret ettiği adresteki içeriği düzenleyemezsiniz.


2
Bir yöntemin dönüş değeri için geçerli olduğundan, çoğunlukla doğru. Değişkenleri veya argümanları bildirmek için geçerli olduğundan, tam olarak değil. İçeriği okumak / yazmak istiyorsanız her zaman daha özel bir türe (void *) atayabilirsiniz - bunu yapmak iyi bir fikir değildir.
bbum

8

idObjective-C nesnesinin bir göstergesidir. void *her şeyin bir göstergesidir . Bunun void *yerine kullanabilirsiniz id, ancak hiçbir şey için derleyici uyarıları almayacağınız için önerilmez.

Stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject ve unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs adresini görmek isteyebilirsiniz. -id.html .


1
Pek değil. (void *) yazılan değişkenler yöntem çağırmalarının hedefi olamaz. Derleyiciden "uyarı: geçersiz alıcı türü" void * "ile sonuçlanır.
bbum

@bbum: void *yazılan değişkenler kesinlikle yöntem çağrılarının hedefi olabilir - bu bir uyarı değil, bir uyarıdır. Sadece bunu yapabileceğini sözlerine: int i = (int)@"Hello, string!";ve takip: printf("Sending to an int: '%s'\n", [i UTF8String]);. Bu bir uyarı değildir, bir hata değildir (ve tam olarak önerilmez veya taşınabilir değildir). Ancak bu şeyleri yapabilmenizin nedeni tüm temel C.
johne

1
Afedersiniz. Haklısın; bu bir uyarı, bir hata değil. Uyarıları her zaman ve her yerde hata olarak görüyorum.
bbum

4
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

Yukarıdaki kod objc.h'den alınmıştır, bu nedenle id, objc_object yapısının bir örneğidir ve isa işaretçisi herhangi bir Objective C Sınıfı nesnesine bağlanabilirken, void * yalnızca türlenmemiş bir işaretçidir.


2

Anladığım kadarıyla id, bir nesnenin işaretçisini temsil ederken void * gerçekten kullanmak istediğiniz türe yönlendirdiğiniz sürece gerçekten herhangi bir şeye işaret edebilir


(Void *) 'den id dahil olmak üzere bazı nesne türlerine yayın yapıyorsanız, büyük olasılıkla yanlış yapıyorsunuzdur. Bunu yapmak için nedenler vardır, ancak bunlar tasarım kusurunun az, çok arasında ve neredeyse her zaman göstergesidir.
bbum

1
alıntı "bunu yapmak için nedenler vardır, ama bunlar arasında çok az," doğru. Bu duruma bağlıdır. Ancak, bazı bağlamlar dışında "büyük olasılıkla yanlış yapıyorsunuz" gibi bir açıklama yapmam.
hhafez

Battaniye bir açıklama yaparım; aralarında boşluk * ile yanlış tipte döküm nedeniyle çok fazla lanet olası hatayı avlamak ve düzeltmek zorunda kaldı. Tek istisna, sözleşmesi geri çağrının ayarlanması ve geri çağrının alınması arasında bağlamın dokunulmadan kalacağını geçersiz kılan * bağlam argümanı alan geri arama tabanlı API'lerdir.
bbum

0

Daha önce söylenenlere ek olarak, koleksiyonlarla ilgili nesneler ve işaretçiler arasında bir fark vardır. Örneğin, NSArray'e bir şey koymak istiyorsanız, bir nesneye ("id" türünde) ihtiyacınız vardır ve orada ham veri işaretçisini ("void *" türünde) kullanamazsınız. Bir koleksiyon içinde kullanmak için "id" türüne [NSValue valueWithPointer:rawData]dönüştürmek void *rawDdataiçin kullanabilirsiniz . Genel olarak "id" daha esnektir ve ona bağlı nesnelerle ilgili daha fazla anlambilime sahiptir. Burada Objective C'nin id türünü açıklayan daha fazla örnek var .

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.