ios - parameter - 如何在Objective-C中創建委託?




objective c delegate教學 (13)

我知道代表如何工作,並且我知道我可以如何使用它們。

但是,我如何創建它們?


Swift版本

一個委託只是一個為另一個類做一些工作的類。 閱讀下面的代碼,看看有沒有什麼愚蠢的(但很有啟發性的)Playground示例,它顯示了這是如何在Swift中完成的。

// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
    // This protocol only defines one required method
    func getYourNiceOlderSiblingAGlassOfWater() -> String
}

class BossyBigBrother {

    // The delegate is the BossyBigBrother's slave. This position can 
    // be assigned later to whoever is available (and conforms to the 
    // protocol).
    weak var delegate: OlderSiblingDelegate?

    func tellSomebodyToGetMeSomeWater() -> String? {
        // The delegate is optional because there might not be anyone
        // nearby to boss around.
        return delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {

    // This method is repquired by the protocol, but the protocol said
    // nothing about how it needs to be implemented.
    func getYourNiceOlderSiblingAGlassOfWater() -> String {
        return "Go get it yourself!"
    }

}

// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()

// Set the delegate 
// bigBro could boss around anyone who conforms to the 
// OlderSiblingDelegate protocol, but since lilSis is here, 
// she is the unlucky choice.
bigBro.delegate = lilSis

// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
    print(replyFromLilSis) // "Go get it yourself!"
}

在實際操作中,代表經常用於以下情況

  1. 當一個班級需要將一些信息傳達給另一個班級時
  2. 當一個班級想要讓另一個班級來定制它時

除了委託類符合所需的協議之外,這些類不需要事先知道任何其他信息。

我強烈推薦閱讀以下兩篇文章。 他們幫助我更好地了解了代表們,甚至比documentation做得更好。


ViewController.h

@protocol NameDelegate <NSObject>

-(void)delegateMEthod: (ArgType) arg;

@end

@property id <NameDelegate> delegate;

ViewController.m

[self.delegate delegateMEthod: argument];

MainViewController.m

ViewController viewController = [ViewController new];
viewController.delegate = self;

方法:

-(void)delegateMEthod: (ArgType) arg{
}

Objective-C委託是已經分配給delegate屬性另一個對象的對象。 要創建一個,您只需定義一個實現您感興趣的委託方法的類,並將該類標記為實現委託協議。

例如,假設你有一個UIWebView 。 如果你想實現它的委託的webViewDidStartLoad:方法,你可以像這樣創建一個類:

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

然後,您可以創建一個MyClass的實例,並將其作為Web視圖的委託進行分配:

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

UIWebView方面,它可能具有類似於此的代碼,以查看委託是否響應使用respondsToSelector:webViewDidStartLoad:消息並在適當時發送它。

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

委託屬性本身通常被聲明為weak (在ARC中)或assign (在ARC之前)以避免保留循環,因為對象的委託通常持有對該對象的強引用。 (例如,視圖控制器通常是它包含的視圖的代表。)

為您的類製作代表

為了定義你自己的委託,你必須在某個地方聲明它們的方法,正如Apple Docs on protocols所討論的那樣。 你通常會聲明一個正式的協議。 聲明,從UIWebView.h轉述,看起來像這樣:

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

這與接口或抽象基類相似,因為它在這種情況下為您的委託UIWebViewDelegate創建了一個特殊類型。 代表實現者必須採用這個協議:

@interface MyClass <UIWebViewDelegate>
// ...
@end

然後在協議中實現這些方法。 對於在協議中聲明為@optional方法(如大多數委託方法),您需要在調用特定方法之前使用@optional進行檢查。

命名

委託方法通常以委派​​類名稱開始命名,並將委託對像作為第一個參數。 他們也經常使用遺囑,遺囑或遺囑。 因此, webViewDidStartLoad:webViewDidStartLoad:第一個參數是web視圖),而不是loadStarted (不帶參數)。

速度優化

每次我們要發送消息時,不要檢查代理是否響應選擇器,而是可以在代理設置時緩存該信息。 一個非常乾淨的方法是使用一個位域,如下所示:

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

然後,在主體中,我們可以通過訪問我們的delegateRespondsTo結構來檢查我們的委託是否處理消息,而不是通過發送-respondsToSelector:一遍又一遍。

非正式代表

在協議存在之前,通常在NSObject上使用一個category來聲明委託可以實現的方法。 例如, CALayer仍然這樣做:

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

這基本上告訴編譯器,任何對像都可能實現displayLayer:

然後,您將使用與上述相同的-respondsToSelector:方法來調用此方法。 代表只需實現此方法並分配delegate屬性,就是這樣(沒有聲明符合協議)。 這種方法在Apple的庫中很常見,但新代碼應該使用上面更現代的協議方法,因為這種方法會污染NSObject (這會使自動補全的用處變得不那麼有用),並且使編譯器很難警告您輸入錯誤和類似的錯誤。


也許這更符合你所缺少的內容:

如果你是來自C ++的觀點,代表們需要一點習慣 - 但基本上“他們只是工作”。

它的工作方式是設置一些作為NSWindow委託編寫的對象,但對像只有一個或多個可能委託方法中的一些或多個的實現(方法)。 所以發生了一些事情, NSWindow想調用你的對象 - 它只是使用Objective-c的respondsToSelector方法來確定你的對像是否需要調用該方法,然後調用它。 這就是objective-c的工作方式 - 按需查找方法。

這對你自己的對象來說是微不足道的,沒有什麼特別的,例如你可以有一個27個對象的NSArray ,所有不同類型的對象,只有18個有一些方法-(void)setToBue; 其他9不。 所以要在所有18個需要完成的調用setToBlue時調用setToBlue ,如下所示:

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

關於委託的另一件事是它們不被保留,所以你總是必須在你的MyClass dealloc方法中將委託設置nil


以我的觀點為該委託方法創建單獨的類,並且可以使用您想要的位置。

在我的Custom DropDownClass.h中

typedef enum
{
 DDSTATE,
 DDCITY
}DropDownType;

@protocol DropDownListDelegate <NSObject>
@required
- (void)dropDownDidSelectItemWithString:(NSString*)itemString     DropDownType:(DropDownType)dropDownType;
@end
@interface DropDownViewController : UIViewController
{
 BOOL isFiltered;
}
@property (nonatomic, assign) DropDownType dropDownType;
@property (weak) id <DropDownListDelegate> delegate;
@property (strong, nonatomic) NSMutableArray *array1DropDown;
@property (strong, nonatomic) NSMutableArray *array2DropDown;

之後,in.m文件用對象創建數組,

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight = 44.0f;
return rowHeight;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return isFiltered?[self.array1DropDown count]:[self.array2DropDown count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

if (self.delegate) {
    if (self.dropDownType == DDCITY) {
        cell.textLabel.text = [self.array1DropDown objectAtIndex:indexPath.row];
    }
    else if (self.dropDownType == DDSTATE) {
        cell.textLabel.text = [self.array2DropDown objectAtIndex:indexPath.row];
    }
}
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 [self dismissViewControllerAnimated:YES completion:^{
    if(self.delegate){
        if(self.dropDownType == DDCITY){
            [self.delegate dropDownDidSelectItemWithString:[self.array1DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
        else if (self.dropDownType == DDSTATE) {
            [self.delegate dropDownDidSelectItemWithString:[self.array2DropDown objectAtIndex:indexPath.row] DropDownType:self.dropDownType];
        }
    }
}];
}

Here all are set for Custom delegate class.after that you can use this delegate method where you want.for example...

in my another viewcontroller import after that

create action for calling delegate method like this

- (IBAction)dropDownBtn1Action:(id)sender {
DropDownViewController *vehicleModelDropView = [[DropDownViewController alloc]init];
vehicleModelDropView.dropDownType = DDCITY;
vehicleModelDropView.delegate = self;
[self presentViewController:vehicleModelDropView animated:YES completion:nil];
}

after that call delegate method like this

- (void)dropDownDidSelectItemWithString:(NSString *)itemString DropDownType:(DropDownType)dropDownType {
switch (dropDownType) {
    case DDCITY:{
        if(itemString.length > 0){
            //Here i am printing the selected row
            [self.dropDownBtn1 setTitle:itemString forState:UIControlStateNormal];
        }
    }
        break;
    case DDSTATE: {
        //Here i am printing the selected row
        [self.dropDownBtn2 setTitle:itemString forState:UIControlStateNormal];
    }

    default:
        break;
}
}

作為Apple推薦的一種良好做法,它對委託人(根據定義它是一種協議)很有用,符合NSObject協議。

@protocol MyDelegate <NSObject>
    ...
@end

&在委託中創建可選方法(即不需要實現的方法),可以使用@optional註釋,如下所示:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

因此,當使用您已指定為可選方法的方法時,如果視圖(與您的委託符合)已實際實現了您的可選方法,則需要(在您的類中)檢查respondsToSelector


可以說你有一個你開發的類,並且想要聲明一個委託屬性,以便能夠在發生某些事件時通知它:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

所以你在MyClass頭文件(或單獨的頭文件)中聲明了一個協議,並聲明你的委託必須/應該實現的必需/可選事件處理程序,然後在MyClass類型( id< MyClassDelegate> )中聲明一個屬性,這意味著任何目標c類符合協議MyClassDelegate ,您會注意到委託屬性被聲明為弱,這對於防止保留週期非常重要(大多數情況下,委託保留MyClass實例,因此如果您將委託聲明為保留,他們之間將保持對方,他們都不會被釋放)。

您還會注意到協議方法將MyClass實例傳遞給委託作為參數,這是在MyClassDelegate想要調用MyClass實例上的某些方法時的最佳實踐,並且還有助於委託將自身聲明為MyClassDelegate到多個MyClass實例,如當你在你的ViewController有多個UITableView's實例,並將它們自己聲明為一個UITableViewDelegate

並在您的MyClass聲明事件如下:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

你首先檢查你的委託是否響應你將要調用的協議方法,以防委託沒有實現它,並且應用程序會崩潰(即使協議方法是必需的)。


好吧,這不是真正的問題的答案,但如果您正在查找如何製作您自己的代表,也許更簡單的事情可能是更好的答案。

我很少執行我的代表,因為我很少需要。 我只能有一個委託對象的委託。 所以如果你希望你的委託進行單向通信/傳遞數據比你更擅長通知。

NSNotification可以將對像傳遞給多個收件人,並且它非常易於使用。 它是這樣工作的:

MyClass.m文件應該看起來像這樣

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

在其他課程中使用您的通知:以觀察者身份添加課程:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

實現選擇器:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

如果是的話,不要忘記以觀察者身份移除你的課程

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

獲得批准的答案非常棒,但如果您正在尋找1分鐘的答案,請嘗試以下操作:

MyClass.h文件應該看起來像這樣(添加帶有註釋的代表行!)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

MyClass.m文件應該看起來像這樣

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

要在另一個類中使用委託(本例中稱為MyVC的UIViewController)MyVC.h:

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MyVC.m:

myClass.delegate = self;          //set its delegate to self somewhere

實現委託方法

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}

當使用正式協議方法創建委託支持時,我發現可以通過添加如下內容來確保正確的類型檢查(儘管是運行時,而不是編譯時):

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

在你的委託訪問器(setDelegate)代碼中。 這有助於減少錯誤。


要創建自己的委託,首先需要創建一個協議並聲明必要的方法,而無需執行。 然後將此協議實現到您想要實現委託或委託方法的頭類中。

協議必須聲明如下:

@protocol ServiceResponceDelegate <NSObject>

- (void) serviceDidFailWithRequestType:(NSString*)error;
- (void) serviceDidFinishedSucessfully:(NSString*)success;

@end

這是應該完成一些任務的服務類。 它顯示瞭如何定義委託以及如何設置委託。 在任務完成後的實現類中調用委託的方法。

@interface ServiceClass : NSObject
{
id <ServiceResponceDelegate> _delegate;
}

- (void) setDelegate:(id)delegate;
- (void) someTask;

@end

@implementation ServiceClass

- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}

- (void) someTask
{
/*

   perform task

*/
if (!success)
{
[_delegate serviceDidFailWithRequestType:@”task failed”];
}
else
{
[_delegate serviceDidFinishedSucessfully:@”task success”];
}
}
@end

這是通過將委託設置為自身來調用服務類的主視圖類。 協議也在頭類中實現。

@interface viewController: UIViewController <ServiceResponceDelegate>
{
ServiceClass* _service;
}

- (void) go;

@end

@implementation viewController

//
//some methods
//

- (void) go
{
_service = [[ServiceClass alloc] init];
[_service setDelegate:self];
[_service someTask];
}

就是這樣,通過在這個類中實現委託方法,一旦操作/任務完成,控制就會回來。


請! 請查看下面簡單的一步一步的教程,以了解委託人如何在iOS中工作。

在iOS中委派

我創建了兩個ViewController(用於將數據從一個發送到另一個)

  1. FirstViewController實現委託(提供數據)。
  2. SecondViewController聲明委託(它將接收數據)。

//1.
//Custom delegate 
@protocol TB_RemovedUserCellTag <NSObject>

-(void)didRemoveCellWithTag:(NSInteger)tag;

@end

//2.
//Create a weak reference in a class where you declared the delegate
@property(weak,nonatomic)id <TB_RemovedUserCellTag> removedCellTagDelegate;

//3. 
// use it in the class
  [self.removedCellTagDelegate didRemoveCellWithTag:self.tag];

//4. import the header file in the class where you want to conform to the protocol
@interface MyClassUsesDelegate ()<TB_RemovedUserCellTag>

@end

// 5。 在類.m中實現該方法 - (void)didRemoveCellWithTag:(NSInteger)標記{NSLog @(“Tag%d”,tag);

}





delegates