Czy Mogę używać bloków Objective-C jako właściwości?

Czy możliwe jest posiadanie bloków jako właściwości przy użyciu standardowej składni właściwości?

Czy są jakieś zmiany dla ARC ?

Author: EI Captain v2.0, 2010-10-14

8 answers

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

Jeśli zamierzasz powtarzać ten sam blok w kilku miejscach użyj type def

typedef void(^MyCompletionBlock)(BOOL success, NSError *error);
@property (nonatomic) MyCompletionBlock completion;
 312
Author: Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 10:47:10

Oto przykład jak wykonałbyś takie zadanie:

#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;
}

Teraz, jedyną rzeczą, która musiałaby się zmienić, jeśli trzeba zmienić rodzaj porównania, byłoby typedef int (^IntBlock)(). Jeśli chcesz przekazać do niego dwa obiekty, zmień to na to: typedef int (^IntBlock)(id, id), a następnie zmień swój blok na:

^ (id obj1, id obj2)
{
    return rand();
};
Mam nadzieję, że to pomoże.

Edycja 12 marca 2012:

W przypadku ARC nie są wymagane żadne konkretne zmiany, ponieważ ARC będzie zarządzać blokami tak długo, jak są one zdefiniowane jako kopia. Ty tak nie trzeba też ustawiać właściwości na zero w destruktorze.

Aby dowiedzieć się więcej, zapoznaj się z tym dokumentem: http://clang.llvm.org/docs/AutomaticReferenceCounting.html

 210
Author: Richard J. Ross III,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-28 12:23:56

Dla Swift, wystarczy użyć zamknięcia: przykład.


W Objective-C:

@ property (copy)void

@property (copy)void (^doStuff)(void);
To takie proste. [10]}Oto prawdziwa dokumentacja Apple, która dokładnie określa, czego używać:

Apple doco.

W Twoim .plik h:
// 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".
Oto Twoja .plik m:
 -(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();
    }

Uważaj na Nieaktualny przykładowy kod.

Z nowoczesnymi (2014+) systemami, rób to, co pokazano tutaj. To takie proste.

 157
Author: Fattie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-25 17:42:53

Ze względu na potomność/ kompletność ... oto dwa pełne przykłady, jak wdrożyć ten absurdalnie wszechstronny "sposób robienia rzeczy". @ Robert odpowiedź jest banalnie zwięzła i poprawna, ale tutaj chcę również pokazać sposoby na faktycznie "zdefiniowanie" bloków.

@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
Głupie? Tak. przydatne? jasne. oto inny, "bardziej atomowy" sposób ustawienia właściwości.. i klasa, która jest śmiesznie przydatna ...
@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

To ilustruje ustawienie właściwość blokowa za pośrednictwem accesora (choć wewnątrz init, dyskusyjna ryzykowna praktyka..) kontra pierwszy przykładowy "nieatomowy" mechanizm "getter". W obu przypadkach... "zakodowane na twardo" implementacje mogą być zawsze nadpisane, na instancję.. a lá..

CALayoutDelegator *littleHelper = CALayoutDelegator.new;
littleHelper.layoutBlock = ^(CALayer*layer){
  [layer.sublayers do:^(id sub){ [sub somethingElseEntirely]; }];
};
someLayer.layoutManager = littleHelper;
Również.. jeśli chcesz dodać właściwość block do kategorii... powiedzmy, że chcesz użyć bloku zamiast jakiegoś starego celu / akcji "Akcja"... Możesz po prostu użyć powiązanych wartości, dobrze.. / align = "left" / bloki.
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
Teraz, kiedy robisz guzik, nie musisz urządzać jakiegoś dramatu.. Wystarczy powiązać dzieło do wykonania przy stworzeniu...
_button.actionBlock = ^(NSControl*thisButton){ 

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

Ten wzorzec może być stosowany w kółko do API Cocoa. użyj właściwości, aby zbliżyć odpowiednie części kodu do siebie , wyeliminować zawiłe paradygmaty delegacji i wykorzystać moc obiektów wykraczającą poza działanie jako głupie "kontenery".

 20
Author: Alex Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-07 13:20:44

Oczywiście można użyć bloków jako właściwości. Ale upewnij się, że są zadeklarowane jako @ property (copy) . Na przykład:

typedef void(^TestBlock)(void);

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

W MRC bloki przechwytywające zmienne kontekstowe są przydzielane w stosie ; zostaną one zwolnione, gdy ramka stosu zostanie zniszczona. Jeśli zostaną skopiowane, nowy blok zostanie przydzielony w stercie , która może być wykonana Później Po wypełnieniu ramki stosu.

 8
Author: Mindy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-16 09:08:50

[7]} nie ma to być "dobra odpowiedź", ponieważ to pytanie zadaje się wprost dla ObjectiveC. Ponieważ Apple wprowadziło Swift na WWDC14, chciałbym podzielić się różnymi sposobami korzystania z bloków (lub zamknięć) w Swift.

Hello, Swift

Masz wiele sposobów na przekazanie bloku odpowiadającego funkcji w języku Swift.

Znalazłem trzy.

Aby to zrozumieć proponuję przetestować na placu zabaw ten mały kawałek kodu.

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)

Swift, Optymalizacja dla zamknięć

Ponieważ Swift jest zoptymalizowany pod kątem asynchronicznego rozwoju, Apple więcej pracowało nad zamknięciami. Po pierwsze, można wywnioskować podpis funkcji, dzięki czemu nie trzeba jej przepisywać.

Access params by numbers

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

Params wnioskowanie z nazewnictwa

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

Zamknięcie Końcowe

Ten specjalny przypadek działa tylko wtedy, gdy blok jest ostatnim argumentem, nazywa się zamykaniem końcowym

Oto przykład (połączony z wywnioskowanym podpis, aby pokazać moc Swift)

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

Wreszcie:

Używając całej tej mocy, to co bym zrobił, to mieszanie zamknięcia końcowego i wnioskowania typu (z nazwami dla czytelności)

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!")
    }
}
 7
Author: Francescu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-17 13:46:08

Witaj, Swift

Completing what @Francescu answer.

Dodanie dodatkowych parametrów:

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)
 0
Author: Gil Beyruth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-21 17:56:53

Możesz postępować zgodnie z poniższym formatem i używać właściwości testingObjectiveCBlock w klasie.

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

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

Po Więcej informacji zajrzyj tutaj

 -3
Author: Sujith Thankachan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-24 06:41:20