क्या मैं गुण के रूप में ऑब्जेक्टिव-सी ब्लॉक का उपयोग कर सकता हूं?


321

क्या मानक संपत्ति सिंटैक्स का उपयोग करके गुणों के रूप में ब्लॉक करना संभव है?

क्या ARC के लिए कोई बदलाव हैं ?


1
खैर, क्योंकि यह बहुत काम आएगा। मुझे यह जानने की जरूरत नहीं है कि यह तब तक है जब तक कि मेरे पास वाक्यविन्यास अधिकार है और यह एक NSObject की तरह व्यवहार करता है।
गुरुघाट

5
यदि आप नहीं जानते कि यह क्या है, तो आप कैसे जानते हैं कि यह बहुत आसान होगा?
स्टीफन कैनन

5
आपको उनका उपयोग नहीं करना चाहिए यदि आप नहीं जानते कि वे क्या हैं :)
रिचर्ड जे। रॉस III

5
@ यहाँ कुछ कारण हैं जो दिमाग में आते हैं। पूर्ण प्रतिनिधि वर्ग की तुलना में ब्लॉक को लागू करना आसान है, ब्लॉक हल्के होते हैं, और आपके पास उस ब्लॉक के संदर्भ में चर तक पहुंच होती है। इवेंट कॉलबैक को प्रभावी ढंग से ब्लॉकों का उपयोग करके किया जा सकता है (cocos2d उनका उपयोग लगभग विशेष रूप से करता है)।
रिचर्ड जे। रॉस III

2
पूरी तरह से संबंधित नहीं है, लेकिन चूंकि कुछ टिप्पणियां "बदसूरत" ब्लॉक सिंटैक्स के बारे में शिकायत करती हैं, यहां एक शानदार लेख है जो पहले सिद्धांतों से सिंटैक्स प्राप्त करता है: nilsou.com/blog/2013/08/21/objective-c-blocks-sctax
पॉलिरेकुगलर

जवाबों:


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

यदि आप कई स्थानों पर एक ही ब्लॉक को दोहराते जा रहे हैं तो एक प्रकार का उपयोग करें

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

3
XCode 4.4 या नए के साथ आपको संश्लेषित करने की आवश्यकता नहीं है। जो इसे और भी संक्षिप्त बना देगा। Apple डॉक्टर
एरिक

वाह, मुझे नहीं पता था कि, धन्यवाद! ... हालाँकि मैं अक्सर करता हूँ@synthesize myProp = _myProp
रॉबर्ट

7
@Robert: आप क्योंकि डाले बिना फिर से भाग्य में हैं, @synthesizeडिफ़ॉल्ट तुम क्या कर रहे है @synthesize name = _name; stackoverflow.com/a/12119360/1052616
एरिक

1
@CharlieMonroe - हाँ आप शायद सही हैं, लेकिन क्या आपको एआरसी के बिना ब्लॉक संपत्ति को नीलाम करने या जारी करने के लिए एक कार्यान्वयन कार्यान्वयन की आवश्यकता नहीं है? (इसके कुछ समय बाद मैंने गैर-एआरसी का उपयोग किया)
रॉबर्ट

1
@imcaptor: हां, यह मेमोरी लीक का कारण बन सकता है अगर आप इसे डीललोक में जारी नहीं करते हैं - जैसे किसी अन्य चर के साथ।
चार्ली मुनरो

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

आशा है कि ये आपकी मदद करेगा।

EDIT 12 मार्च, 2012:

एआरसी के लिए, कोई विशेष परिवर्तन की आवश्यकता नहीं है, क्योंकि एआरसी आपके लिए ब्लॉकों का प्रबंधन करेगा जब तक कि वे कॉपी के रूप में परिभाषित न हों। आपको अपने विध्वंसक में या तो संपत्ति को सेट करने की आवश्यकता नहीं है।

अधिक पढ़ने के लिए, कृपया इस दस्तावेज़ की जाँच करें: http://clang.llvm.org/docs/AutomaticReferenceTount.html


158

स्विफ्ट के लिए, बस क्लोजर का उपयोग करें: उदाहरण।


उद्देश्य-सी में:

@property (कॉपी) शून्य

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

यह इत्ना आसान है।

यहाँ वास्तविक Apple प्रलेखन है, जो बताता है कि क्या उपयोग करना है:

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?
निक कॉव

क्या आप बता सकते हैं कि संपत्ति nonatomicका उपयोग करने वाले अधिकांश अन्य मामलों के लिए संपत्ति सर्वोत्तम प्रथाओं के विपरीत क्यों नहीं होनी चाहिए ?
एलेक्स प्रेट्ज़लाव 22

WorkwithBlocks.html Apple से "आपको संपत्ति की विशेषता के रूप में प्रतिलिपि निर्दिष्ट करनी चाहिए, क्योंकि ..."
Fattie

20

पश्चाताप / पूर्णता के लिए ... यहां दो पूर्ण उदाहरण दिए गए हैं कि इस हास्यास्पद "चीजों को करने के तरीके" को कैसे लागू किया जाए। @ रॉबर्ट का जवाब आनंदपूर्वक संक्षिप्त और सही है, लेकिन यहां मैं वास्तव में ब्लॉक को "परिभाषित" करने के तरीके भी दिखाना चाहता हूं।

@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]; 
};

इस पैटर्न को कोको एपीआई में OVER और OVER लागू किया जा सकता है । अपने कोड के संबंधित हिस्सों को एक साथ पास लाने के लिए गुणों का उपयोग करें , दृढ़ डेलिगेशन प्रतिमानों को समाप्त करें , और केवल "डंबल" के रूप में अभिनय करने से परे वस्तुओं की शक्ति का लाभ उठाएं।


एलेक्स, महान एसोसिएटेड उदाहरण। तुम्हें पता है, मैं नॉनटॉमिक के बारे में सोच रहा हूं। विचार?
फेटी

2
यह बहुत दुर्लभ है कि "परमाणु" एक संपत्ति के लिए सही चीज होगी। एक प्रॉपर्टी को एक थ्रेड में सेट करना और एक ही समय में इसे दूसरे थ्रेड में पढ़ना या एक साथ कई थ्रेड से ब्लॉक प्रॉपर्टी को सेट करना एक बहुत ही अजीब बात होगी । तो "परमाणु" बनाम "गैर-परमाणु" की लागत आपको कोई वास्तविक लाभ नहीं देती है।
gnasher729

8

बेशक आप संपत्तियों के रूप में ब्लॉक का उपयोग कर सकते हैं। लेकिन सुनिश्चित करें कि उन्हें @property (कॉपी) घोषित किया गया है । उदाहरण के लिए:

typedef void(^TestBlock)(void);

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

MRC में, संदर्भ चर पर कब्जा करने वाले ब्लॉक स्टैक में आवंटित किए जाते हैं ; स्टैक फ्रेम नष्ट हो जाने पर उन्हें छोड़ दिया जाएगा। यदि उन्हें कॉपी किया जाता है, तो ढेर में एक नया ब्लॉक आवंटित किया जाएगा , जिसे स्टैक फ्रेम के पॉप होने के बाद बाद में निष्पादित किया जा सकता है।


बिल्कुल सही। यहां वास्तविक Apple डोको है कि आपको कॉपी का उपयोग क्यों करना चाहिए और कुछ नहीं। developer.apple.com/library/ios/documentation/cocoa/conceptual/…
फेटी

7

Disclamer

यह "अच्छा जवाब" होने का इरादा नहीं है, क्योंकि यह प्रश्न ऑब्जेक्टिव के लिए स्पष्ट रूप से पूछते हैं। जैसा कि Apple ने WWDC14 में स्विफ्ट पेश किया, मैं स्विफ्ट में ब्लॉक (या क्लोजर) का उपयोग करने के विभिन्न तरीकों को साझा करना चाहूंगा।

हैलो, स्विफ्ट

आपके पास स्विफ्ट में फ़ंक्शन के बराबर ब्लॉक पास करने के लिए कई तरीके हैं।

मुझे तीन मिले।

यह समझने के लिए मैं आपको खेल के मैदान में इस छोटे से कोड का परीक्षण करने का सुझाव देता हूं।

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)

स्विफ्ट, क्लोजर के लिए अनुकूलित

जैसा कि स्विफ्ट अतुल्यकालिक विकास के लिए अनुकूलित है, Apple ने क्लोजर पर अधिक काम किया। पहला यह है कि फ़ंक्शन सिग्नेचर का अनुमान लगाया जा सकता है ताकि आपको इसे फिर से लिखना न पड़े।

संख्या के आधार पर पैरामेट्स तक पहुँचें

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

नामकरण के साथ परमानस

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

ट्रेलिंग क्लोजर

यह विशेष मामला तभी काम करता है जब ब्लॉक अंतिम तर्क है, इसे अनुगामी समापन कहा जाता है

यहाँ एक उदाहरण है (स्विफ्ट पावर दिखाने के लिए अनुमानित हस्ताक्षर के साथ विलय)

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
क्या यह उत्तर वास्तव में पहले से उपलब्ध कराए गए अन्य उत्तरों से कुछ अधिक जोड़ता है?
रिचर्ड जे। रॉस III
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.