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


321

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

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


1
아주 편리하기 때문입니다. 구문이 맞고 NSObject처럼 동작하는 한 그것이 무엇인지 알 필요는 없습니다.
gurghet

5
그것이 무엇인지 모른다면, 그것이 매우 편리하다는 것을 어떻게 알 수 있습니까?
Stephen Canon

5
당신은 그들이 무엇인지 모르는 경우에는 사용해서는 안됩니다 :)
Richard J. Ross III

5
@Moshe는 여기에 몇 가지 이유가 있습니다. 블록은 전체 델리게이트 클래스보다 구현하기 쉽고 블록은 가벼우 며 해당 블록의 컨텍스트에있는 변수에 액세스 할 수 있습니다. 이벤트 콜백은 블록을 사용하여 효과적으로 수행 할 수 있습니다 (cocos2d는 거의 독점적으로 사용합니다).
Richard J. Ross III

2
완전히 관련되지는 않았지만 일부 의견은 "추악한"블록 구문에 대해 불평하기 때문에 다음은 첫 번째 원칙에서 구문을 파생시키는 훌륭한 기사입니다. nilsou.com/blog/2013/08/21/objective-c-blocks-syntax
paulrehkugler

답변:


305
@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;

3
xCode 4.4 이상에서는 합성 할 필요가 없습니다. 더 간결 해집니다. Apple Doc
Eric

와우, 몰랐어요, 고마워요! ... 자주하는데@synthesize myProp = _myProp
Robert

7
@Robert : @synthesize기본값 을 두지 않으면 서 다시 한 번 운이 좋았습니다. @synthesize name = _name; stackoverflow.com/a/12119360/1052616
Eric

1
@CharlieMonroe-그렇습니다.하지만 ARC없이 블록 속성을 무효화하거나 해제하려면 dealloc 구현이 필요하지 않습니까? (비 ARC를 사용한 이후로 오랜 시간이 지났습니다)
Robert

1
@imcaptor : 예, 다른 변수와 마찬가지로 할당 해제에서 메모리를 해제하지 않으면 메모리 누수가 발생할 수 있습니다.
Charlie Monroe

210

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

#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


158

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+) 시스템으로 여기에 표시된 것을 수행하십시오. 그렇게 간단합니다.


어쩌면 지금 (2016) strong대신에 사용 하는 것이 copy좋을까요?
Nik Kov

속성이 속성을 nonatomic사용하는 다른 대부분의 경우 모범 사례와 다르지 않은 이유를 설명 할 수 있습니까 ?
Alex Pretzlav

Apple의 WorkingwithBlocks.html "왜냐하면 사본을 속성 속성으로 지정해야합니다."
Fattie

20

후손 / 완전성을 위해… 다음은이 엄청나게 다재다능한 "일을하는 방식"을 구현하는 방법에 대한 두 가지 예입니다. @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를 적용 할 수 있습니다 . 속성을 사용하여 코드의 관련 부분을 더 가깝게 만들고 복잡한 위임 패러다임을 제거 하며 단순한 "컨테이너"역할을하는 것 이상의 객체의 힘을 활용하십시오.


Alex, 훌륭한 관련 사례. 비 원자에 대해 궁금합니다. 생각?
Fattie

2
"원자"가 속성에 대해 올바른 일을하는 경우는 매우 드 '니다. 한 스레드에서 블록 속성을 설정 하고 동시에 다른 스레드 에서 읽 거나 여러 스레드에서 동시에 블록 속성 을 설정하는 것은 매우 이상한 일 입니다. 따라서 "원자"대 "비 원자"의 비용은 실제 이점을 제공하지 않습니다.
gnasher729

8

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

typedef void(^TestBlock)(void);

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

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


바로 그거죠. 실제 Apple Doco는 왜 당신이 사본을 사용 해야하는지에 대한 정확한 이유입니다. developer.apple.com/library/ios/documentation/cocoa/conceptual/…
Fattie

7

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

0

안녕 스위프트

@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)

-3

아래 형식을 따르고 testingObjectiveCBlock클래스 의 속성을 사용할 수 있습니다 .

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

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

자세한 내용은 여기 를 참조 하십시오


2
이 답변은 이미 제공된 다른 답변에 더 많은 것을 추가합니까?
Richard J. Ross III
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.