NS Aptallar için Aşılama?


139

Tam olarak nasıl NSInvocationçalışır? İyi bir tanıtım var mı?

Özellikle aşağıdaki kodun ( Mac OS X için Cocoa Programlama, 3. Baskı ) nasıl çalıştığını anlama konusunda sorun yaşıyorum , ancak kavramları öğretici örnekten bağımsız olarak da uygulayabiliyorum. Kod:

- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index
{
    NSLog(@"adding %@ to %@", p, employees);
    // Add inverse of this operation to undo stack
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index];
    if (![undo isUndoing])
        [undo setActionName:@"Insert Person"];

    // Finally, add person to the array
    [employees insertObject:p atIndex:index];
}

- (void)removeObjectFromEmployeesAtIndex:(int)index
{
    Person *p = [employees objectAtIndex:index];
    NSLog(@"removing %@ from %@", p, employees);
    // Add inverse of this operation to undo stack
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocationTarget:self] insertObject:p
                                       inEmployeesAtIndex:index];
    if (![undo isUndoing])
        [undo setActionName:@"Delete Person"];

    // Finally, remove person from array
    [employees removeObjectAtIndex:index];
}

Ne yapmaya çalıştığını anladım. (Btw, employeesbir olan NSArrayözel bir bölgesinin Personsınıfı).

Bir .NET adamı olarak, tanımadığım Obj-C ve Kakao kavramlarını kabaca benzer .NET kavramlarıyla ilişkilendirmeye çalışıyorum. Bu, .NET'in temsilci kavramına benzer, ancak türü yok mu?

Bu kitaptan% 100 net değil, bu yüzden gerçek Kakao / Obj-C uzmanlarından tamamlayıcı bir şey arıyorum, yine basit (-ish) örneğin altındaki temel konsepti anlıyorum. Gerçekten bilgiyi bağımsız olarak uygulayabiliyorum - bölüm 9'a kadar bunu yapmakta hiç zorluk çekmedim. Ama şimdi ...

Şimdiden teşekkürler!

Yanıtlar:


284

Göre Apple'ın NSInvocation sınıf referansı :

An NSInvocation, statik hale getirilmiş bir Objective-C mesajıdır, yani bir nesneye dönüştürülmüş bir eylemdir.

Ve biraz daha ayrıntılı olarak:

Mesajlar kavramı, obj-c felsefesinin merkezinde yer alır. Bir yöntemi çağırdığınızda veya bir nesnenin değişkenine her eriştiğinizde, ona bir mesaj gönderirsiniz. NSInvocationbir nesneye farklı bir zamanda mesaj göndermek veya aynı mesajı birkaç kez göndermek istediğinizde kullanışlıdır. daha sonra göndereceğiniz mesajı tanımlamanıza ve daha sonra çağırmanıza (aslında hedef nesneye göndermenize) NSInvocationolanak tanır .


Örneğin, bir diziye dize eklemek istediğinizi varsayalım. Normalde addObject:mesajı aşağıdaki gibi gönderirsiniz :

[myArray addObject:myString];

Şimdi, NSInvocationbu mesajı başka bir zamanda göndermek için kullanmak istediğinizi varsayalım:

Birincisi, bir hazırlayacak NSInvocationile kullanılmak üzere nesne NSMutableArraybireyin addObject:selektör:

NSMethodSignature * mySignature = [NSMutableArray
    instanceMethodSignatureForSelector:@selector(addObject:)];
NSInvocation * myInvocation = [NSInvocation
    invocationWithMethodSignature:mySignature];

Ardından, iletiyi hangi nesneye göndereceğinizi belirlersiniz:

[myInvocation setTarget:myArray];

Bu nesneye göndermek istediğiniz mesajı belirtin:

[myInvocation setSelector:@selector(addObject:)];

Ve bu yöntem için herhangi bir argümanı doldurun:

[myInvocation setArgument:&myString atIndex:2];

Nesne bağımsız değişkenlerinin işaretçiyle iletilmesi gerektiğini unutmayın. Bunu belirttiği için Ryan McCuaig'e teşekkür ederiz ve daha fazla bilgi için lütfen Apple'ın belgelerine bakın.

Bu noktada, myInvocationgönderilebilecek bir mesajı tanımlayan eksiksiz bir nesnedir. Mesajı gerçekten göndermek için şunu arayabilirsiniz:

[myInvocation invoke];

Bu son adım, esasen yürütülerek mesajın gönderilmesine neden olacaktır [myArray addObject:myString];.

Bir e-posta göndermek gibi düşünün. Yeni bir e-posta ( NSInvocationnesne) açarsınız, göndermek istediğiniz kişinin (nesnenin) adresini girersiniz, alıcı için bir mesaj yazılır (a selectorve bağımsız değişkenler belirtir ) ve sonra "gönder" (çağrı) invoke).

Daha fazla bilgi için bkz . NSInvocation'ı Kullanma . Yukarıdakiler çalışmıyorsa NSInvocation'ı kullanma konusuna bakın .


NSUndoManagerNSInvocationnesneleri komutları tersine çevirebilmesi için kullanır . Aslında, yaptığınız şey NSInvocation"Hey, az önce yaptığım işi geri almak istiyorsanız, bu iletiyi bu argümanlarla o nesneye gönderin" demek için bir nesne yaratmaktır . Sen vermek NSInvocationnesneyi NSUndoManagerve geri alınamaz eylemlerin bir diziye nesneyi ekler. Kullanıcı "Geri al" ı NSUndoManagerçağırırsa, dizideki en son eylemi arar NSInvocationve gerekli eylemi gerçekleştirmek için saklanan nesneyi çağırır .

Daha fazla bilgi için Geri Alma İşlemlerini Kaydetme konusuna bakın .


10
Aksi halde mükemmel bir cevaba yapılan küçük bir düzeltme ... içindeki nesnelere bir işaretçi setArgument:atIndex:geçirmeniz gerekir, bu yüzden arg ödevi gerçekten okunmalıdır [myInvocation setArgument:&myString atIndex:2].
Ryan McCuaig

60
Sadece Ryan'ın notunu açıklığa kavuşturmak için, dizin 0 "öz" ve dizin 1 "_cmd" için ayrılmıştır (daha fazla ayrıntı için yayınlanan e.James bağlantısına bakın). Böylece ilk argümanınız dizin 2'ye, ikinci argüman dizin 3'e yerleştirilir, vb ...
Dave

4
@haroldcampbell: neyi çağırmalıyız?
e.James

6
Neden mySignature içinde seçiciyi belirlediğimiz için setSelector'ı çağırmamız gerektiğini anlamıyorum.
Gleno

6
@Gleno: NSInvocation oldukça esnektir. Aslında yöntem imzasıyla eşleşen herhangi bir seçiciyi ayarlayabilirsiniz, böylece yöntem imzasını oluşturmak için kullanılanla aynı seçiciyi kullanmanız gerekmez. Bu örnekte, aynı yöntem imzasını paylaştıkları için setSelector: @selector (removeObject :) gibi kolayca yapabilirsiniz.
e.James

48

İşte NSInvocation uygulamasının basit bir örneği:

- (void)hello:(NSString *)hello world:(NSString *)world
{
    NSLog(@"%@ %@!", hello, world);

    NSMethodSignature *signature  = [self methodSignatureForSelector:_cmd];
    NSInvocation      *invocation = [NSInvocation invocationWithMethodSignature:signature];

    [invocation setTarget:self];                    // index 0 (hidden)
    [invocation setSelector:_cmd];                  // index 1 (hidden)
    [invocation setArgument:&hello atIndex:2];      // index 2
    [invocation setArgument:&world atIndex:3];      // index 3

    // NSTimer's always retain invocation arguments due to their firing delay. Release will occur when the timer invalidates itself.
    [NSTimer scheduledTimerWithTimeInterval:1 invocation:invocation repeats:NO];
}

Arandığında - [self hello:@"Hello" world:@"world"]; - yöntem:

  • Yazdır "Merhaba dünya!"
  • Kendisi için bir NSMethodSignature oluşturun.
  • Kendini çağıran bir NSInvocation oluşturun ve doldurun.
  • NSInvocation'ı bir NSTimer'e aktarın
  • Zamanlayıcı (yaklaşık) 1 saniye içinde tetiklenerek yöntemin orijinal argümanlarıyla tekrar çağrılmasına neden olur.
  • Tekrar et.

Sonunda şöyle bir çıktı alırsınız:

2010-07-11 17:48:45.262 Your App[2523:a0f] Hello world!
2010-07-11 17:48:46.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:47.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:48.267 Your App[2523:a0f] Hello world!
2010-07-11 17:48:49.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:50.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:51.269 Your App[2523:a0f] Hello world!
...

Elbette self, NSTimer'in NSInvocation'ı ona göndermesi için hedef nesne var olmaya devam etmelidir. Örneğin, bir Singleton nesnesi veya uygulama süresince var olan bir AppDelegate.


GÜNCELLEME:

Yukarıda belirtildiği gibi, bir NSInvocation'ı bir NSTimer'e argüman olarak ilettiğinizde, NSTimer otomatik olarak tüm NSInvocation argümanlarını korur.

Bir NSInvocation'ı bir NSTimer'e argüman olarak geçirmiyorsanız ve bir süreliğine yapışmasını planlıyorsanız, -retainArgumentsyöntemini çağırmalısınız . Aksi takdirde, çağrılar çağrılmadan önce argümanları serbest bırakılabilir ve bu da sonunda kodunuzun çökmesine neden olabilir. Bunu nasıl yapacağınız aşağıda açıklanmıştır:

NSMethodSignature *signature  = ...;
NSInvocation      *invocation = [NSInvocation invocationWithMethodSignature:signature];
id                arg1        = ...;
id                arg2        = ...;

[invocation setTarget:...];
[invocation setSelector:...];
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];

[invocation retainArguments];  // If you do not call this, arg1 and arg2 might be deallocated.

[self someMethodThatInvokesYourInvocationEventually:invocation];

6
invocationWithMethodSignature:Başlatıcı kullanılsa bile , yine de aramanız gerekir setSelector:. Gereksiz görünüyor, ama sadece test ettim ve gerekli.
ThomasW

Bu sonsuz bir döngü içinde devam ediyor mu? ve _cmd nedir
j2emanue


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.