অবজেক্টিভ-সি দিয়ে আমি কি একজন নির্বাচক হিসাবে কোনও ব্লক পাস করতে পারি?


90

একটিতে @selectorযুক্তির জন্য একটি উদ্দেশ্য-সি ব্লকটি পাস করা কি সম্ভব UIButton? অর্থাত, নিম্নলিখিত কাজ করার উপায় আছে কি?

    [closeOverlayButton addTarget:self 
                           action:^ {[anotherIvarLocalToThisMethod removeFromSuperview];} 
                 forControlEvents:UIControlEventTouchUpInside];

ধন্যবাদ

উত্তর:


69

হ্যাঁ, তবে আপনাকে একটি বিভাগ ব্যবহার করতে হবে।

কিছুটা এইরকম:

@interface UIControl (DDBlockActions)

- (void) addEventHandler:(void(^)(void))handler 
        forControlEvents:(UIControlEvents)controlEvents;

@end

বাস্তবায়নটি কিছুটা কৌশলযুক্ত হবে:

#import <objc/runtime.h>

@interface DDBlockActionWrapper : NSObject
@property (nonatomic, copy) void (^blockAction)(void);
- (void) invokeBlock:(id)sender;
@end

@implementation DDBlockActionWrapper
@synthesize blockAction;
- (void) dealloc {
  [self setBlockAction:nil];
  [super dealloc];
}

- (void) invokeBlock:(id)sender {
  [self blockAction]();
}
@end

@implementation UIControl (DDBlockActions)

static const char * UIControlDDBlockActions = "unique";

- (void) addEventHandler:(void(^)(void))handler 
        forControlEvents:(UIControlEvents)controlEvents {

  NSMutableArray * blockActions = 
                 objc_getAssociatedObject(self, &UIControlDDBlockActions);

  if (blockActions == nil) {
    blockActions = [NSMutableArray array];
    objc_setAssociatedObject(self, &UIControlDDBlockActions, 
                                        blockActions, OBJC_ASSOCIATION_RETAIN);
  }

  DDBlockActionWrapper * target = [[DDBlockActionWrapper alloc] init];
  [target setBlockAction:handler];
  [blockActions addObject:target];

  [self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents];
  [target release];

}

@end

কিছু ব্যাখ্যা:

  1. আমরা একটি কাস্টম "অভ্যন্তরীণ কেবল" ক্লাস ব্যবহার করছি DDBlockActionWrapper । এটি একটি সাধারণ শ্রেণীর রয়েছে যার একটি ব্লক সম্পত্তি রয়েছে (আমরা যে ব্লকটি আমন্ত্রণ জানাতে চাই), এবং এমন একটি পদ্ধতি যা কেবল block ব্লকটিকে ডাকে।
  2. UIControlবিভাগ কেবল এই চাদরে এক instantiates, এটা ব্লক দেয় প্রার্থনা করা, এবং তারপর নিজেই বলে যে মোড়কের এবং তার ব্যবহার করতেinvokeBlock: লক্ষ্য এবং কর্ম হিসাবে পদ্ধতি (স্বাভাবিক হিসাবে)।
  3. UIControlবিষয়শ্রেণীতে একটি অ্যারের সঞ্চয় করতে এই ক্ষেত্রে সংশ্লিষ্ট বস্তু ব্যবহার DDBlockActionWrappers, কারণUIControl তার লক্ষ্যমাত্রা ধরে রাখা যায় না। এই অ্যারেটি নিশ্চিত করা হয় যে ব্লকগুলি যখন তাদের আহ্বান করার কথা রয়েছে তখন উপস্থিত রয়েছে।
  4. আমাদের নিশ্চিত করতে হবে যে DDBlockActionWrappersঅবজেক্টটি ধ্বংস হয়ে গেলে তা পরিষ্কার হয়ে যায়, তাই আমরা সম্পর্কিত -[UIControl dealloc]নতুন বস্তুর সাথে নতুন করে সুইজলিংয়ের একটি বাজে হ্যাক করছি , এবং তারপরে মূল deallocকোডটি আহ্বান করব । চতুর, কৌতুকময়। প্রকৃতপক্ষে, সম্পর্কিত বিষয়গুলি হ্রাসের সময় স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যায়

অবশেষে, এই কোডটি ব্রাউজারে টাইপ করা হয়েছিল এবং সংকলিত হয়নি। এটিতে সম্ভবত কিছু জিনিস ভুল রয়েছে। আপনার মাইলেজ পরিবর্তিত হতে পারে.


4
মনে রাখবেন যে আপনি এখন সম্পর্কিত সমস্যাগুলি ব্যবহার করার চেয়ে কিছুটা বেশি দক্ষ ফ্যাশনে এই সমস্যাটি সমাধান করতে objc_implementationWithBlock()এবং class_addMethod()সমাধান করতে পারেন (যা বোঝায় হ্যাশ লুক যা পদ্ধতিটির মতো দেখার মতো দক্ষ নয়)। সম্ভবত একটি অপ্রাসঙ্গিক পারফরম্যান্স পার্থক্য, তবে এটি একটি বিকল্প।
বুবব

@ বুবাম মানে imp_implementationWithBlock?
ভাইকিংগুন্ডো

হ্যাঁ - যে এক। এটি একবার নামকরণ করা হয়েছিল objc_implementationWithBlock()। :)
বুবব

কাস্টমগুলির বোতামগুলির জন্য এটি ব্যবহারের ফলে UITableViewCellকাঙ্ক্ষিত লক্ষ্যগুলি-ক্রিয়াগুলির সদৃশ হবে কারণ প্রতিটি নতুন লক্ষ্য একটি নতুন উদাহরণ এবং পূর্ববর্তীগুলি একই ইভেন্টগুলির জন্য পরিষ্কার হয় না। আপনি লক্ষ্যগুলি প্রথমে পরিষ্কার করতে হবে for (id t in self.allTargets) { [self removeTarget:t action:@selector(invokeBlock:) forControlEvents:controlEvents]; } [self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents];
ইউজিন

আমি মনে করি যে একটি জিনিস যা উপরের কোডটিকে আরও স্পষ্ট করে তোলে তা জেনে রাখা হচ্ছে যে কোনও ইউআইসিআন্ট্রোল অনেকগুলি লক্ষ্য গ্রহণ করতে পারে: অ্যাকশন
জোড়

41

ব্লক বস্তু হয়। হিসাবে আপনার ব্লক পাশ targetদিয়ে যুক্তি, @selector(invoke)যেমন actionযুক্তি, এভাবে:

id block = [^{NSLog(@"Hello, world");} copy];// Don't forget to -release.

[button addTarget:block
           action:@selector(invoke)
 forControlEvents:UIControlEventTouchUpInside];

ইহা আকর্ষণীয়. আমি আজ রাতে এই জাতীয় কিছু করতে পারি কিনা তা আমি সন্ধান করব। একটি নতুন প্রশ্ন শুরু করতে পারেন।
তাদ ডোনাগে

31
এটি কাকতালীয়ভাবে "কাজ করে"। এটি বেসরকারী এপিআই উপর নির্ভর করে; invokeব্লক বস্তুর উপর পদ্ধতি সরকারি ও এই ফ্যাশন ব্যবহার করা উদ্দেশ্যে নয়।
বুব্বু

4
বুবুম: ঠিক বলেছেন। আমি ভেবেছিলাম -আনভোকটি সর্বজনীন, তবে আমি আমার উত্তরটি আপডেট করার এবং একটি বাগ ফাইল করার অর্থ করছি।
লেমনার

4
এটি একটি দুর্দান্ত সমাধান বলে মনে হচ্ছে, তবে আমি ভাবছি যে এটি অ্যাপল কোনও প্রাইভেট এপিআই ব্যবহার করে তা গ্রহণযোগ্য কিনা whether
ব্রায়ান

4
nilপরিবর্তে পাস যখন কাজ করে @selector(invoke)
k06a

17

না, নির্বাচক এবং ব্লকগুলি উদ্দেশ্য-সি-তে সামঞ্জস্যপূর্ণ ধরণের নয় (আসলে, তারা খুব আলাদা জিনিস different আপনাকে নিজের পদ্ধতি লিখতে হবে এবং পরিবর্তে এর নির্বাচকটি পাস করতে হবে।


11
বিশেষত, একটি নির্বাচক আপনি চালিত এমন কিছু নয়; এটি কোনও বস্তুর কাছে আপনি যে বার্তা প্রেরণ করেছেন তার নাম (অথবা এই ক্ষেত্রে যেমনটি তৃতীয় বস্তুতে অন্য কোনও বস্তু প্রেরণ করুন: আপনি লক্ষ্যতে একটি [নির্বাচক এখানে যান] বার্তা প্রেরণের জন্য নিয়ন্ত্রণকে বলছেন)। অন্যদিকে, একটি ব্লক, আপনি চালিত এমন কিছু যা: আপনি কোনও অবজেক্টকে স্বাধীনভাবে, সরাসরি ব্লকটি কল করেন।
পিটার হোসি

7

কোনও ইউআইবুটনে @ নির্বাচক যুক্তির জন্য একটি উদ্দেশ্য-সি ব্লকটি পাস করা কি সম্ভব?

ইতিমধ্যে সরবরাহিত সমস্ত উত্তর গ্রহণ করে উত্তরটি হ্যাঁ তবে কয়েকটি বিভাগ সেটআপ করার জন্য একটি সামান্য কাজ প্রয়োজন।

আমি এনএসআইভোকেশনটি ব্যবহার করার পরামর্শ দিচ্ছি কারণ আপনি এটির সাহায্যে টাইমারের সাহায্যে, কোনও বস্তু হিসাবে সঞ্চিত এবং অনুরোধ করা ... ইত্যাদি ...

এখানে আমি যা করেছি তা এখানে রয়েছে তবে নোট করুন যে আমি এআরসি ব্যবহার করছি।

এনএসবজেক্টে প্রথমটি একটি সহজ বিভাগ:

.হ

@interface NSObject (CategoryNSObject)

- (void) associateValue:(id)value withKey:(NSString *)aKey;
- (id) associatedValueForKey:(NSString *)aKey;

@end

.ম

#import "Categories.h"
#import <objc/runtime.h>

@implementation NSObject (CategoryNSObject)

#pragma mark Associated Methods:

- (void) associateValue:(id)value withKey:(NSString *)aKey {

    objc_setAssociatedObject( self, (__bridge void *)aKey, value, OBJC_ASSOCIATION_RETAIN );
}

- (id) associatedValueForKey:(NSString *)aKey {

    return objc_getAssociatedObject( self, (__bridge void *)aKey );
}

@end

এর পরে একটি ব্লকে সংরক্ষণ করার জন্য এনএসআইভোকেশন এ একটি বিভাগ:

.হ

@interface NSInvocation (CategoryNSInvocation)

+ (NSInvocation *) invocationWithTarget:(id)aTarget block:(void (^)(id target))block;
+ (NSInvocation *) invocationWithSelector:(SEL)aSelector forTarget:(id)aTarget;
+ (NSInvocation *) invocationWithSelector:(SEL)aSelector andObject:(__autoreleasing id)anObject forTarget:(id)aTarget;

@end

.ম

#import "Categories.h"

typedef void (^BlockInvocationBlock)(id target);

#pragma mark - Private Interface:

@interface BlockInvocation : NSObject
@property (readwrite, nonatomic, copy) BlockInvocationBlock block;
@end

#pragma mark - Invocation Container:

@implementation BlockInvocation

@synthesize block;

- (id) initWithBlock:(BlockInvocationBlock)aBlock {

    if ( (self = [super init]) ) {

        self.block = aBlock;

    } return self;
}

+ (BlockInvocation *) invocationWithBlock:(BlockInvocationBlock)aBlock {
    return [[self alloc] initWithBlock:aBlock];
}

- (void) performWithTarget:(id)aTarget {
    self.block(aTarget);
}

@end

#pragma mark Implementation:

@implementation NSInvocation (CategoryNSInvocation)

#pragma mark - Class Methods:

+ (NSInvocation *) invocationWithTarget:(id)aTarget block:(void (^)(id target))block {

    BlockInvocation *blockInvocation = [BlockInvocation invocationWithBlock:block];
    NSInvocation *invocation = [NSInvocation invocationWithSelector:@selector(performWithTarget:) andObject:aTarget forTarget:blockInvocation];
    [invocation associateValue:blockInvocation withKey:@"BlockInvocation"];
    return invocation;
}

+ (NSInvocation *) invocationWithSelector:(SEL)aSelector forTarget:(id)aTarget {

    NSMethodSignature   *aSignature  = [aTarget methodSignatureForSelector:aSelector];
    NSInvocation        *aInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
    [aInvocation setTarget:aTarget];
    [aInvocation setSelector:aSelector];
    return aInvocation;
}

+ (NSInvocation *) invocationWithSelector:(SEL)aSelector andObject:(__autoreleasing id)anObject forTarget:(id)aTarget {

    NSInvocation *aInvocation = [NSInvocation invocationWithSelector:aSelector 
                                                           forTarget:aTarget];
    [aInvocation setArgument:&anObject atIndex:2];
    return aInvocation;
}

@end

এটি কীভাবে ব্যবহার করবেন তা এখানে:

NSInvocation *invocation = [NSInvocation invocationWithTarget:self block:^(id target) {
            NSLog(@"TEST");
        }];
[invocation invoke];

আপনি আমন্ত্রণ এবং মানক উদ্দেশ্য-সি পদ্ধতিগুলি সহ অনেক কিছু করতে পারেন। উদাহরণস্বরূপ, আপনি এনএসআইভোকেশনওপেশন (initWithInvocation :), NSTimer (সময়সূচী টাইমারউইথটাইমইন্টারভাল: অনুরোধ: পুনরাবৃত্তি :) ব্যবহার করতে পারেন

বিন্দুটি আপনার ব্লকটিকে একটি এনএসআইভোকেশনে পরিণত করছে আরও বহুমুখী এবং এটি ব্যবহার করা যেতে পারে:

NSInvocation *invocation = [NSInvocation invocationWithTarget:self block:^(id target) {
                NSLog(@"My Block code here");
            }];
[button addTarget:invocation
           action:@selector(invoke)
 forControlEvents:UIControlEventTouchUpInside];

আবার এটি কেবল একটি পরামর্শ।


আরও একটি জিনিস, এখানে অনুরোধ একটি পাবলিক পদ্ধতি। বিকাশকারী.অ্যাপল.
অরভিন

5

দুর্ভাগ্যক্রমে, এর মতো সহজ নয়।

তত্ত্ব অনুসারে, কোনও ফাংশনটি সংজ্ঞায়িত করা সম্ভব হবে যা গতিশীলভাবে শ্রেণীর একটি পদ্ধতি যুক্ত করে target, সেই পদ্ধতিতে কোনও ব্লকের বিষয়বস্তু কার্যকর করতে এবং actionযুক্তি দ্বারা প্রয়োজনীয় হিসাবে নির্বাচককে ফিরিয়ে দেওয়া যায় । এই ফাংশনটি এমএব্লকক্লোজার দ্বারা ব্যবহৃত কৌশলটি ব্যবহার করতে পারে , যা আইওএসের ক্ষেত্রে, লিবিফির একটি কাস্টম প্রয়োগের উপর নির্ভর করে, যা এখনও পরীক্ষামূলক।

আপনি পদ্ধতি হিসাবে ক্রিয়াটি প্রয়োগ করা ভাল।


4

লাইব্রেরি ব্লকসকিট এ (কোকোপড হিসাবে উপলভ্য) এই বিল্ট-ইন বৈশিষ্ট্য রয়েছে।

ইউআইসিন্ট্রোল + ব্লকসকিট। হিরের জন্য শিরোনাম ফাইলটি একবার দেখুন। তারা ডেভ দেলংয়ের ধারণাটি বাস্তবায়ন করেছে যাতে আপনার প্রয়োজন হয় না। কিছু ডকুমেন্টেশন এখানে


1

কেউ আমাকে বলছেন যে এটি কেন ভুল, সম্ভবত, বা কোনও ভাগ্য সহ, সম্ভবত না, তাই আমি হয় কিছু শিখব, অথবা আমি সহায়ক হব।

আমি এই একসাথে ছুড়ে ফেলেছি। এটি সত্যই মৌলিক, কিছুটা কাস্টিংয়ের সাথে একটি পাতলা মোড়কযুক্ত। সতর্কতার একটি শব্দ, এটি ধরে নিয়েছে যে আপনি যে ব্লকটি ব্যবহার করছেন তাতে আপনার নির্বাচিতের (যেমন যুক্তি এবং ধরণের সংখ্যা) মেলে সঠিক স্বাক্ষর রয়েছে।

//
//  BlockInvocation.h
//  BlockInvocation
//
//  Created by Chris Corbyn on 3/01/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>


@interface BlockInvocation : NSObject {
    void *block;
}

-(id)initWithBlock:(void *)aBlock;
+(BlockInvocation *)invocationWithBlock:(void *)aBlock;

-(void)perform;
-(void)performWithObject:(id)anObject;
-(void)performWithObject:(id)anObject object:(id)anotherObject;

@end

এবং

//
//  BlockInvocation.m
//  BlockInvocation
//
//  Created by Chris Corbyn on 3/01/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "BlockInvocation.h"


@implementation BlockInvocation

-(id)initWithBlock:(void *)aBlock {
    if (self = [self init]) {
        block = (void *)[(void (^)(void))aBlock copy];
    }

    return self;
}

+(BlockInvocation *)invocationWithBlock:(void *)aBlock {
    return [[[self alloc] initWithBlock:aBlock] autorelease];
}

-(void)perform {
    ((void (^)(void))block)();
}

-(void)performWithObject:(id)anObject {
    ((void (^)(id arg1))block)(anObject);
}

-(void)performWithObject:(id)anObject object:(id)anotherObject {
    ((void (^)(id arg1, id arg2))block)(anObject, anotherObject);
}

-(void)dealloc {
    [(void (^)(void))block release];
    [super dealloc];
}

@end

সত্যিই জাদুকর কিছুই হচ্ছে না। void *পদ্ধতিটি চালুর আগে কেবলমাত্র প্রচুর ডাউনকাস্টিং এবং ব্যবহারযোগ্য ব্লক স্বাক্ষরে টাইপকাস্টিং। স্পষ্টতই (ঠিক এর মতোই)performSelector: এবং এর সাথে সম্পর্কিত পদ্ধতির মতো, ইনপুটগুলির সম্ভাব্য সংমিশ্রণগুলি সীমাবদ্ধ, তবে আপনি কোডটি সংশোধন করেন তবে প্রসারণযোগ্য।

এর মতো ব্যবহৃত:

BlockInvocation *invocation = [BlockInvocation invocationWithBlock:^(NSString *str) {
    NSLog(@"Block was invoked with str = %@", str);
}];
[invocation performWithObject:@"Test"];

এটি ফলাফল:

2011-01-03 16: 11: 16.020 ব্লক ইনভোকেশন [37096: a0f] ব্লকটি স্ট্র = টেস্ট সহ আহ্বান করা হয়েছিল

একটি লক্ষ্য-অ্যাকশন দৃশ্যে ব্যবহৃত আপনার ঠিক এর মতো কিছু করতে হবে:

BlockInvocation *invocation = [[BlockInvocation alloc] initWithBlock:^(id sender) {
  NSLog(@"Button with title %@ was clicked", [(NSButton *)sender title]);
}];
[myButton setTarget:invocation];
[myButton setAction:@selector(performWithObject:)];

যেহেতু একটি লক্ষ্য-অ্যাকশন সিস্টেমের লক্ষ্য ধরে রাখা হয় না, তাই আপনাকে নিয়ন্ত্রণের ব্যবস্থা যতক্ষণ না চলবে ততক্ষণ আপনি নিশ্চিত করতে হবে যে অনুরোধের অবজেক্টটি বেঁচে থাকবে।

আমি আমার চেয়ে আরও বিশেষজ্ঞের কাছ থেকে কিছু শুনতে আগ্রহী।


লক্ষ্য-অ্যাকশন invocation
দৃশ্যে

1

আমার কোনও ইউআইটিবেলভিউসিলের মধ্যে একটি ইউআইবাটনের সাথে সম্পর্কিত একটি ক্রিয়া থাকা দরকার। আমি প্রতিটি আলাদা আলাদা ঘরে প্রতিটি বোতাম ট্র্যাক করতে ট্যাগ ব্যবহার এড়াতে চেয়েছিলাম। আমি ভেবেছিলাম এটি অর্জনের সর্বাধিক প্রত্যক্ষ উপায় হ'ল এই জাতীয় বোতামটির সাথে একটি ব্লক "অ্যাকশন" সংযুক্ত করা:

[cell.trashButton addTarget:self withActionBlock:^{
        NSLog(@"Will remove item #%d from cart!", indexPath.row);
        ...
    }
    forControlEvent:UIControlEventTouchUpInside];

আমার বাস্তবায়নটি কিছুটা আরও সরল করা হয়েছে, উল্লেখ করার জন্য @ বুবামকে ধন্যবাদ imp_implementationWithBlockএবং এবং class_addMethod(যদিও এটি ব্যাপকভাবে পরীক্ষা করা হয়নি):

#import <objc/runtime.h>

@implementation UIButton (ActionBlock)

static int _methodIndex = 0;

- (void)addTarget:(id)target withActionBlock:(ActionBlock)block forControlEvent:(UIControlEvents)controlEvents{
    if (!target) return;

    NSString *methodName = [NSString stringWithFormat:@"_blockMethod%d", _methodIndex];
    SEL newMethodName = sel_registerName([methodName UTF8String]);
    IMP implementedMethod = imp_implementationWithBlock(block);
    BOOL success = class_addMethod([target class], newMethodName, implementedMethod, "v@:");
    NSLog(@"Method with block was %@", success ? @"added." : @"not added." );

    if (!success) return;


    [self addTarget:target action:newMethodName forControlEvents:controlEvents];

    // On to the next method name...
    ++_methodIndex;
}


@end

0

এটি কোনও এনএসব্লকঅপরেশন (আইওএস এসডিকে +5) রাখার জন্য কাজ করে না? এই কোডটি এআরসি ব্যবহার করে এবং এটি এমন একটি অ্যাপের সরলকরণ যা আমি এটি দিয়ে পরীক্ষা করছি (মনে হচ্ছে এটি কাজ করছে, কমপক্ষে দৃশ্যত, নিশ্চিত নয় যে আমি স্মৃতি ফাঁস করছি কিনা)।

NSBlockOperation *blockOp;
UIView *testView; 

-(void) createTestView{
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake(0, 60, 1024, 688)];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];            

    UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btnBack setFrame:CGRectMake(200, 200, 200, 70)];
    [btnBack.titleLabel setText:@"Back"];
    [testView addSubview:btnBack];

    blockOp = [NSBlockOperation blockOperationWithBlock:^{
        [testView removeFromSuperview];
    }];

    [btnBack addTarget:blockOp action:@selector(start) forControlEvents:UIControlEventTouchUpInside];
}

অবশ্যই, আমি নিশ্চিত না যে এটি বাস্তব ব্যবহারের জন্য কতটা ভাল। আপনার এনএসব্লকওপরেশনের একটি রেফারেন্সটি জীবিত রাখা দরকার বা আমার মনে হয় এআরসি এটি মেরে ফেলবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.