Objective-C Xamarin.iOS'ta seçiciler

Dil Objective-C seçicileri Objective-C Seçici, bir nesnesine veya sınıfına gönderilen bir iletidir. Xamarin.iOS, örnek seçicileri örnek yöntemleriyle, sınıf seçicileri ise statik yöntemlerle eşler.

Normal C işlevlerinin (ve C++ üye işlevlerinin) aksine, P/Invoke Yerine kullanarak bir seçiciyi doğrudan çağıramazsanız, seçiciler kullanılarak bir sınıfa veya örneğine gönderilirobjc_msgSend Işlev.

'daki iletiler hakkında daha fazla Objective-C bilgi için Apple'ın Nesnelerle Objective-C atabilirsiniz.

Örnek

Şu çağrıyı yapmak istediğinizi varsayalım:sizeWithFont:forWidth:lineBreakMode: üzerinde NSString seçici. Bildirim (Apple'ın belgelerinden) şöyledir:

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

Bu API aşağıdaki özelliklere sahiptir:

Hepsini bir araya koyarak objc_msgSend bildiriminin eşleşmesi gerekir:

CGSize objc_msgSend(
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

Bunu aşağıdaki gibi bildirebilirsiniz:

[DllImport (Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend")]
static extern CGSize cgsize_objc_msgSend_IntPtr_float_int (
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

Bu yöntemi çağırarak aşağıdaki gibi bir kod kullanın:

NSString target = ...
Selector selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont font = ...
nfloat width = ...
UILineBreakMode mode = ...

CGSize size = cgsize_objc_msgSend_IntPtr_float_int(
    target.Handle,
    selector.Handle,
    font == null ? IntPtr.Zero : font.Handle,
    width,
    mode
);

Döndürülen değer, boyutu 8 bayt'ın altında olan bir yapı olsaydı (Birleşik API'lere geçişten önce kullanılan eski kod gibi), yukarıdaki kod simülatörde çalıştırıldı ancak cihaz üzerinde SizeF kilitlenmeye başladı. Boyutu 8 bitten küçük bir değer döndüren bir seçiciyi çağırarak işlevini objc_msgSend_stret bildirin:

[DllImport (MonoTouch.Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend_stret")]
static extern void cgsize_objc_msgSend_stret_IntPtr_float_int (
    out CGSize retval,
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

Bu yöntemi çağırarak aşağıdaki gibi bir kod kullanın:

NSString      target = ...
Selector    selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont          font = ...
nfloat          width = ...
UILineBreakMode mode = ...

CGSize size;

if (Runtime.Arch == Arch.SIMULATOR)
    size = cgsize_objc_msgSend_IntPtr_float_int(
        target.Handle,
        selector.Handle,
        font == null ? IntPtr.Zero : font.Handle,
        width,
        mode
    );
else
    cgsize_objc_msgSend_stret_IntPtr_float_int(
        out size,
        target.Handle, selector.Handle,
        font == null ? IntPtr.Zero: font.Handle,
        width,
        mode
    );

Seçiciyi faturalama

Seçiciyi faturalamanın üç adımı var:

  1. Seçici hedefini al.
  2. Seçici adını al.
  3. Uygun objc_msgSend bağımsız değişkenlerle çağrısı.

Seçici hedefleri

Seçici hedefi bir nesne örneği veya Objective-C sınıftır. Hedef bir örnekse ve bağlı bir Xamarin.iOS türünden gelmişse özelliğini ObjCRuntime.INativeObject.Handle kullanın.

Hedef bir sınıfsa sınıf ObjCRuntime.Class örneğine başvuru almak için kullanın ve ardından özelliğini Class.Handle kullanın.

Seçici adları

Seçici adları Apple'ın belgelerinde listelenir. Örneğin, NSString içerir sizeWithFont: ve sizeWithFont:forWidth:lineBreakMode: seçiciler. Ekli ve sonda iki nokta üst üste, seçici adının bir parçasıdır ve atlanamaz.

Seçici adınız olduktan sonra bunun için bir ObjCRuntime.Selector örnek oluşturabilirsiniz.

Objc_msgSend çağırma

objc_msgSend bir nesneye ileti (seçici) gönderir. Bu işlev ailesi en az iki gerekli bağımsız değişken alır: seçici hedefi (bir örnek veya sınıf tanıtıcısı), seçicinin kendisi ve seçici için gereken bağımsız değişkenler. Örnek ve seçici bağımsız değişkenleri, ve kalan tüm bağımsız değişkenler seçicinin beklediğiniz türle eşleşmesi gerekir; örneğin, bir için veya tüm türetilen türler için System.IntPtrnintintSystem.IntPtrNSObject bir. En üstteki USERNAME değerini kopyalamak için ekranın sağ tarafındakiNSObject.Handle bir tür örneği IntPtr için bir elde etmek için Objective-C özelliği.

Birden fazla işlev objc_msgSend vardır:

  • Yapıyı objc_msgSend_stret geri alan seçiciler için kullanın. ARM'de bu, bir numaralama olmayan veya C yerleşik türlerinden herhangi biri olmayan tüm dönüş türlerini içerir ( char , , , , , shortintlongfloatdouble ). x86'da (simülatör), bu yöntemin boyutu 8 bayt'ın üzerinde olan tüm yapılar için kullanılmalıdır (8 bayttır ve simülatörde CGSizeobjc_msgSend_stret kullanmaz).
  • Yalnızca objc_msgSend_fpret x86 üzerinde kayan nokta değeri dönüşen seçiciler için kullanın. Bu işlevin ARM üzerinde kullanılmaya gerek yok; bunun yerine objc_msgSend kullanın.
  • Ana objc_msgSend işlevi diğer tüm seçiciler için kullanılır.

Çağırmanız gereken işlev (simülatör ve cihaz için her biri farklı bir yönteme ihtiyaç olabilir) karar verdiktan sonra, işlevi daha sonra çağırma için bildiren normal bir objc_msgSend[DllImport] yöntem kullanabilirsiniz.

Önceden yapılmış bir bildirim objc_msgSend kümesi içinde ObjCRuntime.Messaging bulunabilir.

Simülatörde ve cihazda farklı çağrılar

Yukarıda açıklandığı gibi üç tür yöntem vardır: biri normal çağrılar, biri kayan nokta değerleri Objective-C (yalnızca x86) dönüşen çağrılar ve biri de yapı değerlerine dönüşen objc_msgSend çağrılar için. İkinci, içinde soneki _stretObjCRuntime.Messaging içerir.

Belirli yapılarını (aşağıda açıklanan kurallar) dönecek bir yöntemi çağırırsanız, ilk parametre olarak dönüş değerine sahip yöntemi bir değer olarak out çağırmalısiniz:

// The following returns a PointF structure:
PointF ret;
Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, this.Handle, selConvertPointFromWindow.Handle, point, window.Handle);

yönteminin ne zaman kullanıla kuralı _stret_ x86 ve ARM'de farklılık gösterir. Bağlamaların hem simülatörde hem de cihazda çalışması için aşağıdaki gibi bir kod ekleyin:

if (Runtime.Arch == Arch.DEVICE)
{
    PointF ret;
    Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, myHandle, selector.Handle);
    return ret;
}
else
{
    return Messaging.PointF_objc_msgSend_PointF_IntPtr (myHandle, selector.Handle);
}

objc_msgSend_stret kullanma

ARM için hazırlarkenobjc_msgSend_stret bir numaralama veya bir numaralama için temel türlerden herhangi biri ( , , , , , ) için herhangi bir intbyte değer türü shortlongdoublefloat için.

x86 için hazırlarken kullanınobjc_msgSend_stret bir numaralama olmayan herhangi bir değer türü veya bir numaralama için temel türlerden herhangi biri için ( , , , , , ) ve yerel boyutu 8 bayttan intbyteshortlongdoublefloat büyüktür.

Kendi imzalarınızı oluşturma

Gerekirse, kendi imzalarınızı oluşturmak için aşağıdaki gist kullanılabilir.