Objective-C bloklarını özellik olarak kullanabilir miyim?


321

Standart özellik sözdizimini kullanarak bloklar özellik olarak kullanılabilir mi?

ARC için herhangi bir değişiklik var mı?


1
Çünkü çok kullanışlıdır. Sözdizimi doğru olduğu ve bir NSObject gibi davrandığı sürece bunun ne olduğunu bilmem gerekmeyecekti.
gurghet

5
Bunun ne olduğunu bilmiyorsanız, çok kullanışlı olacağını nereden biliyorsunuz?
Stephen Canon

5
Bunları kullanmamalısınız Eğer ne olduklarını bilmiyorsanız :)
Richard J. Ross III

5
@Moshe, akla gelen bazı nedenler. Blokları uygulamak, tam delege sınıfından daha kolaydır, bloklar hafiftir ve bu blok bağlamındaki değişkenlere erişebilirsiniz. Olay Geri Aramaları bloklar kullanılarak etkin bir şekilde yapılabilir (cocos2d bunları neredeyse tamamen kullanır).
Richard J. Ross III

2
Tamamen ilgili değil, ancak bazı yorumlar "çirkin" blok sözdiziminden şikayet ettiğinden, sözdizimini ilk ilkelerden türeyen harika bir makale: nilsou.com/blog/2013/08/21/objective-c-blocks-syntax
paulrehkugler

Yanıtlar:


305
@property (nonatomic, copy) void (^simpleBlock)(void);
@property (nonatomic, copy) BOOL (^blockWithParamter)(NSString *input);

Aynı bloğu birkaç yerde tekrarlayacaksanız def yazın

typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;

3
XCode 4.4 veya daha yenisi ile sentezlemenize gerek yoktur. Bu onu daha da özlü hale getirecektir. Apple Doc
Eric

vay, bunu bilmiyordum, teşekkürler! ... Sık sık yapmam @synthesize myProp = _myProp
Robert

7
@Robert: Yine şanslısınız, çünkü @synthesizevarsayılanı koymadan yaptığınız şey @synthesize name = _name; stackoverflow.com/a/12119360/1052616
Eric

1
@CharlieMonroe - Evet, muhtemelen haklısınız, ancak blok özelliğini ARC olmadan nil veya serbest bırakmak için bir dealloc uygulamasına ihtiyacınız yok mu? (ARC dışı kullandığımdan beri bir süredir)
Robert

1
@imcaptor: Evet, diğer tüm değişkenlerde olduğu gibi, onu dealloc'da bırakmamanız durumunda bellek sızıntılarına neden olabilir.
Charlie Monroe

210

İşte böyle bir görevi nasıl başaracağınıza dair bir örnek:

#import <Foundation/Foundation.h>
typedef int (^IntBlock)();

@interface myobj : NSObject
{
    IntBlock compare;
}

@property(readwrite, copy) IntBlock compare;

@end

@implementation myobj

@synthesize compare;

- (void)dealloc 
{
   // need to release the block since the property was declared copy. (for heap
   // allocated blocks this prevents a potential leak, for compiler-optimized 
   // stack blocks it is a no-op)
   // Note that for ARC, this is unnecessary, as with all properties, the memory management is handled for you.
   [compare release];
   [super dealloc];
}
@end

int main () {
    @autoreleasepool {
        myobj *ob = [[myobj alloc] init];
        ob.compare = ^
        {
            return rand();
        };
        NSLog(@"%i", ob.compare());
        // if not ARC
        [ob release];
    }

    return 0;
}

Şimdi, karşılaştırma türünü değiştirmeniz gerekiyorsa değiştirmeniz gereken tek şey typedef int (^IntBlock)(). İki nesne geçirmeniz gerekiyorsa, bunu şu şekilde değiştirin: typedef int (^IntBlock)(id, id)ve bloğunuzu şu şekilde değiştirin:

^ (id obj1, id obj2)
{
    return rand();
};

Umarım bu yardımcı olur.

EDIT 12 Mart 2012:

ARC için belirli bir değişiklik gerekmez, çünkü ARC blokları kopya olarak tanımlandığı sürece sizin için yönetir. Özelliği, yıkıcıda sıfır olarak ayarlamanıza gerek yoktur.

Daha fazla okuma için lütfen şu belgeye göz atın : http://clang.llvm.org/docs/AutomaticReferenceCounting.html


158

Swift için sadece kapaklar kullanın: örnek.


Hedef-C'de:

@property (kopya) geçersiz

@property (copy)void (^doStuff)(void);

Bu kadar basit.

İşte tam olarak ne kullanacağınızı belirten gerçek Apple belgeleri:

Apple doco.

.H dosyanızda:

// Here is a block as a property:
//
// Someone passes you a block. You "hold on to it",
// while you do other stuff. Later, you use the block.
//
// The property 'doStuff' will hold the incoming block.

@property (copy)void (^doStuff)(void);

// Here's a method in your class.
// When someone CALLS this method, they PASS IN a block of code,
// which they want to be performed after the method is finished.

-(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater;

// We will hold on to that block of code in "doStuff".

İşte .m dosyanız:

 -(void)doSomethingAndThenDoThis:(void(^)(void))pleaseDoMeLater
    {
    // Regarding the incoming block of code, save it for later:
    self.doStuff = pleaseDoMeLater;

    // Now do other processing, which could follow various paths,
    // involve delays, and so on. Then after everything:
    [self _alldone];
    }

-(void)_alldone
    {
    NSLog(@"Processing finished, running the completion block.");
    // Here's how to run the block:
    if ( self.doStuff != nil )
       self.doStuff();
    }

Güncel olmayan örnek kodlara dikkat edin.

Modern (2014+) sistemlerle burada gösterilenleri yapın. Bu kadar basit.


Belki de şunu söylemelisiniz, şimdi (2016) strongyerine kullanmakta sorun değil copymi?
Nik Kov

Mülkün, mülk nonatomickullanan diğer çoğu durum için neden en iyi uygulamalardan farklı olmaması gerektiğini açıklayabilir misiniz ?
Alex Pretzlav

Apple'dan WorkingwithBlocks.html "Kopyayı özellik özelliği olarak belirtmelisiniz, çünkü ..."
Fattie

20

Gelecek kuşak / bütünlük uğruna… İşte bu gülünç derecede çok yönlü “bir şeyler yapmanın” nasıl uygulanacağına dair iki TAM örnek. @ Robert'ın cevabı mutlulukla özlü ve doğrudur, ama burada aynı zamanda blokları gerçekten "tanımlamak" için yollar göstermek istiyorum.

@interface       ReusableClass : NSObject
@property (nonatomic,copy) CALayer*(^layerFromArray)(NSArray*);
@end

@implementation  ResusableClass
static  NSString const * privateScope = @"Touch my monkey.";

- (CALayer*(^)(NSArray*)) layerFromArray { 
     return ^CALayer*(NSArray* array){
        CALayer *returnLayer = CALayer.layer
        for (id thing in array) {
            [returnLayer doSomethingCrazy];
            [returnLayer setValue:privateScope
                         forKey:@"anticsAndShenanigans"];
        }
        return list;
    };
}
@end

Aptal? Evet. Kullanışlı? İşte bu. İşte özelliği ayarlamak için farklı, "daha atomik" bir yol .. ve gülünç yararlı bir sınıf ...

@interface      CALayoutDelegator : NSObject
@property (nonatomic,strong) void(^layoutBlock)(CALayer*);
@end

@implementation CALayoutDelegator
- (id) init { 
   return self = super.init ? 
         [self setLayoutBlock: ^(CALayer*layer){
          for (CALayer* sub in layer.sublayers)
            [sub someDefaultLayoutRoutine];
         }], self : nil;
}
- (void) layoutSublayersOfLayer:(CALayer*)layer {
   self.layoutBlock ? self.layoutBlock(layer) : nil;
}   
@end

Bu, blok özelliğinin, ilk örneğin "anatomik olmayan" "alıcı" mekanizmasına karşı (init içinde olsa da, tartışmalı bir uygulama olarak) erişimci aracılığıyla ayarlanmasını gösterir. Her iki durumda da… "sabit kodlu" uygulamaların üzerine her zaman yazılabilir, örneğin .. a ..

CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
  [layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;

Ayrıca .. bir kategoriye bir blok özelliği eklemek istiyorsanız ... bazı eski okul hedef / eylem "eylemi" yerine bir Blok kullanmak istediğinizi varsayalım ... blokları ilişkilendirir.

typedef    void(^NSControlActionBlock)(NSControl*); 
@interface       NSControl            (ActionBlocks)
@property (copy) NSControlActionBlock  actionBlock;    @end
@implementation  NSControl            (ActionBlocks)

- (NSControlActionBlock) actionBlock { 
    // use the "getter" method's selector to store/retrieve the block!
    return  objc_getAssociatedObject(self, _cmd); 
} 
- (void) setActionBlock:(NSControlActionBlock)ab {

    objc_setAssociatedObject( // save (copy) the block associatively, as categories can't synthesize Ivars.
    self, @selector(actionBlock),ab ,OBJC_ASSOCIATION_COPY);
    self.target = self;                  // set self as target (where you call the block)
    self.action = @selector(doItYourself); // this is where it's called.
}
- (void) doItYourself {

    if (self.actionBlock && self.target == self) self.actionBlock(self);
}
@end

Şimdi, bir düğme yaptığınızda, bir IBActiondrama kurmanıza gerek yok .. Sadece yaratılışta yapılacak işi ilişkilendirin ...

_button.actionBlock = ^(NSControl*thisButton){ 

     [doc open]; [thisButton setEnabled:NO]; 
};

Bu desen Kakao API'larına OVER ve OVER uygulanabilir . Kodunuzun ilgili kısımlarını bir araya getirmek , kıvrık temsilci seçme paradigmalarını ortadan kaldırmak ve nesnelerin gücünü sadece aptal "kaplar" olarak davranmanın ötesinde kullanmak için özellikleri kullanın .


Alex, büyük İlişkili örnek. Atomik olmayanı merak ediyorum. Düşünceler?
Fattie

2
"Atomik" bir mülk için yapılacak doğru şey olması çok nadirdir. Bir iş parçacığında bir block özelliği ayarlamak ve aynı anda başka bir iş parçacığında okumak veya block özelliğini aynı anda birden fazla iş parçacığından ayarlamak çok garip bir şey olacaktır . Yani "atomik" ve "atomik olmayan" ın maliyeti size gerçek bir avantaj sağlamaz.
gnasher729

8

Elbette blokları özellik olarak kullanabilirsiniz. Ancak @property (copy) olarak bildirildiklerinden emin olun . Örneğin:

typedef void(^TestBlock)(void);

@interface SecondViewController : UIViewController
@property (nonatomic, copy) TestBlock block;
@end

MRC'de bağlam değişkenlerini yakalayan bloklar yığına ayrılır ; yığın çerçevesi yok edildiğinde serbest bırakılırlar. Kopyalanırlarsa, yığın halinde yeni bir blok atanır ve bu blok , yığın çerçevesi açıldıktan sonra daha sonra çalıştırılabilir.


Kesinlikle. İşte tam olarak neden kopyayı kullanmanız gerektiğine dair gerçek Apple doco'su ve başka bir şey yok. developer.apple.com/library/ios/documentation/cocoa/conceptual/…
Fattie

7

Disclamer

Bu soru açıkça ObjectiveC için sorduğu için bu "iyi cevap" olarak tasarlanmamıştır. Apple Swift'i WWDC14'te tanıtırken, Swift'te blok (veya kapatma) kullanmanın farklı yollarını paylaşmak istiyorum.

Merhaba Swift

Swift'teki işleve eşit bir blok geçirmeyi öneren birçok yol var.

Üç tane buldum.

Bunu anlamak için oyun alanında bu küçük kod parçasını test etmenizi öneririm.

func test(function:String -> String) -> String
{
    return function("test")
}

func funcStyle(s:String) -> String
{
    return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle)

let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle)

let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" })


println(resultFunc)
println(resultBlock)
println(resultAnon)

Hızlı, kapaklar için optimize edilmiş

Swift, eşzamansız geliştirme için optimize edildiğinden Apple, kapaklar üzerinde daha çok çalıştı. Birincisi, işlev imzası çıkarılabilir, böylece yeniden yazmak zorunda kalmazsınız.

Parametrelere numaralarla erişim

let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })

Adlandırma ile parametre çıkarımı

let resultShortAnon2 = test({myParam in return "ANON_" + myParam + "__ANON" })

Sondaki Kapanış

Bu özel durum yalnızca blok son argüman ise işe yarar, buna sondaki kapatma adı verilir

İşte bir örnek (Swift gücünü göstermek için çıkarılan imzayla birleştirilmiştir)

let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }

En sonunda:

Tüm bu gücü kullanarak yaptığım sondaki kapanış ve tip çıkarımını karıştırmak (okunabilirlik için adlandırma ile)

PFFacebookUtils.logInWithPermissions(permissions) {
    user, error in
    if (!user) {
        println("Uh oh. The user cancelled the Facebook login.")
    } else if (user.isNew) {
        println("User signed up and logged in through Facebook!")
    } else {
        println("User logged in through Facebook!")
    }
}

0

Merhaba Swift

@Francescu'nun cevaplarını tamamlıyor.

Ek parametreler ekleme:

func test(function:String -> String, param1:String, param2:String) -> String
{
    return function("test"+param1 + param2)
}

func funcStyle(s:String) -> String
{
    return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle, "parameter 1", "parameter 2")

let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle, "parameter 1", "parameter 2")

let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" }, "parameter 1", "parameter 2")


println(resultFunc)
println(resultBlock)
println(resultAnon)

-3

Aşağıdaki biçimi izleyebilir testingObjectiveCBlockve sınıftaki özelliği kullanabilirsiniz .

typedef void (^testingObjectiveCBlock)(NSString *errorMsg);

@interface MyClass : NSObject
@property (nonatomic, strong) testingObjectiveCBlock testingObjectiveCBlock;
@end

Daha fazla bilgi için buraya bir göz atın


2
Bu cevap, daha önce verilen diğer cevaplara gerçekten bir şey daha ekliyor mu?
Richard J. Ross III
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.