[ios] Objective-C 블록을 특성으로 사용할 수 있습니까?

표준 속성 구문을 사용하여 블록을 속성으로 가질 수 있습니까?

ARC에 대한 변경 사항이 있습니까?



답변

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

여러 곳에서 같은 블록을 반복하려면 def 유형을 사용하십시오

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


답변

이러한 작업을 수행하는 방법의 예는 다음과 같습니다.

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

이제 비교 유형을 변경해야하는 경우 변경해야하는 유일한 것은입니다 typedef int (^IntBlock)(). 두 개의 객체를 전달해야하는 경우 다음 typedef int (^IntBlock)(id, id)과 같이 변경하고 블록을 다음과 같이 변경하십시오.

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

이게 도움이 되길 바란다.

2012 년 3 월 12 일 수정 :

ARC의 경우 ARC가 블록으로 정의 된 블록을 관리하기 때문에 특정 변경이 필요하지 않습니다. 소멸자에서도 속성을 nil로 설정할 필요가 없습니다.

자세한 내용은 다음 문서를 확인하십시오.
http://clang.llvm.org/docs/AutomaticReferenceCounting.html


답변

Swift의 경우 클로저를 사용하십시오 .


Objective-C에서 :

@property (사본) 무효

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

그렇게 간단합니다.

다음은 실제 사용 가능한 Apple 설명서입니다.

애플 도코.

.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".

.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();
    }

오래된 예제 코드에주의하십시오.

최신 (2014+) 시스템으로 여기에 표시된 것을 수행하십시오. 그렇게 간단합니다.


답변

후손 / 완전성을 위해… 다음은이 엄청나게 다재다능한 “일을하는 방식”을 구현하는 방법에 대한 두 가지 예입니다. @Robert의 대답은 행복하고 간결하고 정확하지만 여기서는 실제로 블록을 “정의”하는 방법을 보여주고 싶습니다.

@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

바보? 예. 유능한? 그래요 여기에 속성을 설정하는 다른 “더 원자적인”방법과 엄청나게 유용한 클래스가 있습니다.

@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

이것은 첫 번째 예제의 “비 원자” “게터”메커니즘에 대해 접근자를 통해 블록 속성을 설정하는 방법을 보여줍니다 (init 내부에 있지만, 명백한 습득 사례 ..). 어느 경우 든… “하드 코딩 된”구현은 인스턴스별로 항상 덮어 쓸 수 있습니다 .

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

또한 .. 카테고리에 블록 속성을 추가하려면 … 구식 대상 / 액션 “조치”대신 블록을 사용한다고 가정하십시오. 관련 값을 사용할 수도 있습니다. 블록을 연결하십시오.

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

이제 버튼을 만들 때 IBAction드라마 를 설정할 필요가 없습니다 . 제작시 수행 할 작업을 연결하기 만하면됩니다.

_button.actionBlock = ^(NSControl*thisButton){ 

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

이 패턴은 Cocoa API 에 OVER 및 OVER를 적용 할 수 있습니다 . 속성을 사용하여 코드의 관련 부분을 더 가깝게 만들고 복잡한 위임 패러다임을 제거 하며 단순한 “컨테이너”역할을하는 것 이상의 객체의 힘을 활용하십시오.


답변

물론 블록을 속성으로 사용할 수 있습니다. 그러나 @property (copy) 로 선언되어 있는지 확인하십시오 . 예를 들면 다음과 같습니다.

typedef void(^TestBlock)(void);

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

MRC에서는 컨텍스트 변수를 캡처하는 블록이 스택에 할당됩니다 . 스택 프레임이 파괴되면 해제됩니다. 이들이 복사되면 새로운 블록이 heap에 할당되며 , 스택 프레임이 팝된 후에 나중에 실행될 수 있습니다.


답변

Disclamer

이 질문은 ObjectiveC를 명시 적으로 요구하기 때문에 “좋은 답변”이 아닙니다. Apple이 WWDC14에서 Swift를 소개함에 따라 Swift에서 블록 (또는 클로저)을 사용하는 다른 방법을 공유하고 싶습니다.

안녕 스위프트

Swift의 기능과 동등한 블록을 전달하는 방법은 여러 가지가 있습니다.

세 개를 찾았습니다.

이것을 이해하기 위해 놀이터 에서이 작은 코드를 테스트하는 것이 좋습니다.

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는 비동기 개발에 최적화되어 있기 때문에 Apple은 클로저에 더 많은 노력을 기울였습니다. 첫 번째는 함수 서명을 유추하여 다시 작성할 필요가 없다는 것입니다.

숫자로 액세스 매개 변수

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

명명을 통한 매개 변수 추론

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

후행 폐쇄

이 특별한 경우는 블록이 마지막 인수 인 경우에만 작동하며 후행 마감 이라고합니다.

다음은 예입니다 (Swift 전력을 표시하기 위해 유추 된 서명과 병합 됨)

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

드디어:

이 모든 힘을 사용하는 것은 후행 클로저와 형식 유추를 혼합하는 것입니다 (가독성을 위해 명명)

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!")
    }
}


답변

안녕 스위프트

@Francescu가 답변 한 내용을 보완합니다.

추가 매개 변수 추가 :

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)