ios 如何在 Objective-C 中创建委托?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/626898/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:44:06  来源:igfitidea点击:

How do I create delegates in Objective-C?

iosobjective-ccocoacallbackdelegates

提问by Andy Jacobs

I know how delegates work, and I know how I can use them.

我知道委托是如何工作的,我知道如何使用它们。

But how do I create them?

但是我如何创建它们?

回答by Jesse Rusak

An Objective-C delegate is an object that has been assigned to the delegateproperty another object. To create one, you define a class that implements the delegate methods you're interested in, and mark that class as implementing the delegate protocol.

Objective-C 委托是已分配给delegate另一个对象的属性的对象。要创建一个,您需要定义一个实现您感兴趣的委托方法的类,并将该类标记为实现委托协议。

For example, suppose you have a UIWebView. If you'd like to implement its delegate's webViewDidStartLoad:method, you could create a class like this:

例如,假设您有一个UIWebView. 如果你想实现它的委托webViewDidStartLoad:方法,你可以创建一个这样的类:

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

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

Then you could create an instance of MyClass and assign it as the web view's delegate:

然后,您可以创建 MyClass 的实例并将其分配为 Web 视图的委托:

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

On the UIWebViewside, it probably has code similar to this to see if the delegate responds to the webViewDidStartLoad:message using respondsToSelector:and send it if appropriate.

另一方面UIWebView,它可能有与此类似的代码,以查看委托是否webViewDidStartLoad:使用respondsToSelector:并在适当时发送消息来响应消息。

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

The delegate property itself is typically declared weak(in ARC) or assign(pre-ARC) to avoid retain loops, since the delegate of an object often holds a strong reference to that object. (For example, a view controller is often the delegate of a view it contains.)

委托属性本身通常被声明weak(在 ARC 中)或(在 ARCassign之前)以避免保留循环,因为对象的委托通常持有对该对象的强引用。(例如,视图控制器通常是它包含的视图的委托。)

Making Delegates for Your Classes

为你的班级做代表

To define your own delegates, you'll have to declare their methods somewhere, as discussed in the Apple Docs on protocols. You usually declare a formal protocol. The declaration, paraphrased from UIWebView.h, would look like this:

要定义自己的委托,您必须在某处声明他们的方法,如Apple Docs on protocols 中所述。你通常会声明一个正式的协议。从 UIWebView.h 转述的声明将如下所示:

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

This is analogous to an interface or abstract base class, as it creates a special type for your delegate, UIWebViewDelegatein this case. Delegate implementors would have to adopt this protocol:

这类似于接口或抽象基类,因为UIWebViewDelegate在这种情况下,它为您的委托创建了一个特殊类型。委托实现者必须采用此协议:

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

And then implement the methods in the protocol. For methods declared in the protocol as @optional(like most delegate methods), you need to check with -respondsToSelector:before calling a particular method on it.

然后实现协议中的方法。对于在协议中声明为的方法@optional(与大多数委托方法一样),您需要-respondsToSelector:在调用特定方法之前进行检查。

Naming

命名

Delegate methods are typically named starting with the delegating class name, and take the delegating object as the first parameter. They also often use a will-, should-, or did- form. So, webViewDidStartLoad:(first parameter is the web view) rather than loadStarted(taking no parameters) for example.

委托方法通常以委托类名开头命名,并以委托对象作为第一个参数。他们还经常使用 will-、should- 或 did- 形式。因此,例如webViewDidStartLoad:(第一个参数是 Web 视图)而不是loadStarted(不带参数)。

Speed Optimizations

速度优化

Instead of checking whether a delegate responds to a selector every time we want to message it, you can cache that information when delegates are set. One very clean way to do this is to use a bitfield, as follows:

不必在每次我们想要向选择器发送消息时检查委托是否响应选择器,您可以在设置委托时缓存该信息。一种非常简洁的方法是使用位域,如下所示:

@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

Then, in the body, we can check that our delegate handles messages by accessing our delegateRespondsTostruct, rather than by sending -respondsToSelector:over and over again.

然后,在正文中,我们可以通过访问我们的delegateRespondsTo结构来检查我们的委托是否处理消息,而不是通过-respondsToSelector:一遍又一遍地发送。

Informal Delegates

非正式代表

Before protocols existed, it was common to use a categoryon NSObjectto declare the methods a delegate could implement. For example, CALayerstill does this:

协议出现之前,它是共同使用类别NSObject宣布委托可以实现的方法。例如,CALayer仍然这样做:

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

This tells the compiler that any object might implement displayLayer:.

这告诉编译器任何对象都可能实现displayLayer:.

You would then use the same -respondsToSelector:approach as described above to call this method. Delegates implement this method and assign the delegateproperty, and that's it (there's no declaring you conform to a protocol). This method is common in Apple's libraries, but new code should use the more modern protocol approach above, since this approach pollutes NSObject(which makes autocomplete less useful) and makes it hard for the compiler to warn you about typos and similar errors.

然后,您将使用与上述相同的-respondsToSelector:方法来调用此方法。委托实现这个方法并分配delegate属性,就是这样(没有声明你遵守协议)。这种方法在 Apple 的库中很常见,但新代码应该使用上面更现代的协议方法,因为这种方法会造成污染NSObject(这使得自动完成不太有用)并且编译器很难警告你拼写错误和类似错误。

回答by Tibidabo

The approved answer is great, but if you're looking for a 1 minute answer try this:

批准的答案很好,但如果您正在寻找 1 分钟的答案,请尝试以下操作:

MyClass.h file should look like this (add delegate lines with comments!)

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 file should look like this

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

To use your delegate in another class (UIViewController called MyVC in this case) MyVC.h:

在另一个类中使用你的委托(在这种情况下 UIViewController 称为 MyVC)MyVC.h:

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

MyVC.m:

我的VC.m:

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

Implement delegate method

实现委托方法

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

回答by swiftBoy

Please! check below simple step by step tutorial to understand how Delegates works in iOS.

请!查看以下简单的分步教程,了解 Delegates 在 iOS 中的工作原理。

Delegate in iOS

iOS 中的委托

I have created two ViewControllers (for sending data from one to another)

我创建了两个 ViewControllers(用于将数据从一个发送到另一个)

  1. FirstViewController implement delegate (which provides data).
  2. SecondViewController declare the delegate (which will receive data).
  1. FirstViewController 实现委托(提供数据)。
  2. SecondViewController 声明委托(它将接收数据)。

回答by umop

When using the formal protocol method for creating delegate support, I've found that you can ensure proper type checking (albeit, runtime, not compile time) by adding something like:

当使用正式协议方法创建委托支持时,我发现您可以通过添加以下内容来确保正确的类型检查(尽管是运行时,而不是编译时):

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

in your delegate accessor (setDelegate) code. This helps minimize mistakes.

在您的委托访问器 (setDelegate) 代码中。这有助于减少错误。

回答by Tom Andersen

Maybe this is more along the lines of what you are missing:

也许这更符合您所缺少的内容:

If you are coming from a C++ like viewpoint, delegates takes a little getting used to - but basically 'they just work'.

如果您来自类似 C++ 的观点,则委托需要一点时间来适应 - 但基本上“他们只是工作”。

The way it works is that you set some object that you wrote as the delegate to NSWindow, but your object only has implementations (methods) for one or a few of the many possible delegate methods. So something happens, and NSWindowwants to call your object - it just uses Objective-c's respondsToSelectormethod to determine if your object wants that method called, and then calls it. This is how objective-c works - methods are looked up on demand.

它的工作方式是将您编写的某个对象设置为 NSWindow 的委托,但您的对象只有许多可能的委托方法中的一种或几种的实现(方法)。所以发生了一些事情,并且NSWindow想要调用您的对象 - 它只是使用 Objective-c 的respondsToSelector方法来确定您的对象是否想要调用该方法,然后调用它。这就是objective-c 的工作原理——方法是按需查找的。

It is totally trivial to do this with your own objects, there is nothing special going on, you could for instance have an NSArrayof 27 objects, all different kinds of objects, only 18 some of them having the method -(void)setToBue;The other 9 don't. So to call setToBlueon all of 18 that need it done, something like this:

用你自己的对象来做这件事是完全微不足道的,没有什么特别的,例如你可以有NSArray27 个对象,所有不同类型的对象,只有 18 个有方法-(void)setToBue;,其他 9 个没有。因此,要调用setToBlue所有需要完成的 18 个任务,如下所示:

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

The other thing about delegates is that they are not retained, so you always have to set the delegate to nilin your MyClass deallocmethod.

关于委托的另一件事是它们不会被保留,所以你总是必须nil在你的MyClass dealloc方法中设置委托。

回答by Jean

As a good practice recommended by Apple, it's good for the delegate (which is a protocol, by definition), to conform to NSObjectprotocol.

作为 Apple 推荐的一种良好做法,委托(根据定义,这是一种协议)遵守NSObject协议是有好处的。

@protocol MyDelegate <NSObject>
    ...
@end

& to create optional methods within your delegate (i.e. methods which need not necessarily be implemented), you can use the @optionalannotation like this :

& 要在您的委托中创建可选方法(即不一定要实现的方法),您可以使用这样的@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

So when using methods that you have specified as optional, you need to (in your class) check with respondsToSelectorif the view (that is conforming to your delegate) has actually implemented your optional method(s) or not.

因此,当使用您指定为可选的方法时,您需要(在您的类中)检查respondsToSelector视图(符合您的委托)是否实际实现了您的可选方法。

回答by DrBug

I think all these answers make a lot of sense once you understand delegates. Personally I came from the land of C/C++ and before that procedural languages like Fortran etc so here is my 2 min take on finding similar analogues in C++ paradigm.

我认为一旦你理解了代表,所有这些答案都很有意义。就我个人而言,我来自 C/C++ 领域,在 Fortran 等过程语言之前,这里是我在 C++ 范式中寻找类似类似物的 2 分钟时间。

If I were to explain delegates to a C++/Java programmer I would say

如果我要向 C++/Java 程序员解释委托,我会说

What are delegates ? These are static pointers to classes within another class. Once you assign a pointer, you can call functions/methods in that class. Hence some functions of your class are "delegated" (In C++ world - pointer to by a class object pointer) to another class.

什么是代表?这些是指向另一个类中的类的静态指针。一旦你分配了一个指针,你就可以调用该类中的函数/方法。因此,您的类的某些功能被“委托”(在 C++ 世界中 - 由类对象指针指向)到另一个类。

What are protocols ? Conceptually it serves as similar purpose as to the header file of the class you are assigning as a delegate class. A protocol is a explicit way of defining what methods needs to be implemented in the class who's pointer was set as a delegate within a class.

什么是协议?从概念上讲,它的用途与您分配为委托类的类的头文件类似。协议是定义需要在类中实现哪些方法的明确方式,该类的指针被设置为类中的委托。

How can I do something similar in C++? If you tried to do this in C++, you would by defining pointers to classes (objects) in the class definition and then wiring them up to other classes that will provide additional functions as delegates to your base class. But this wiring needs to be maitained within the code and will be clumsy and error prone. Objective C just assumes that programmers are not best at maintaining this decipline and provides compiler restrictions to enforce a clean implementation.

我如何在 C++ 中做类似的事情?如果您尝试在 C++ 中执行此操作,您将通过在类定义中定义指向类(对象)的指针,然后将它们连接到其他类,这些类将提供附加功能作为您的基类的委托。但是这种接线需要在代码中维护,并且会很笨拙且容易出错。Objective C 只是假设程序员不擅长维护这个规则,并提供编译器限制来强制执行干净的实现。

回答by Suragch

Swift version

迅捷版

A delegate is just a class that does some work for another class. Read the following code for a somewhat silly (but hopefully enlightening) Playground example that shows how this is done in 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!"
}

In actual practice, delegates are often used in the following situations

在实际实践中,delegate 常用于以下几种情况

  1. When a class needs to communicate some information to another class
  2. When a class wants to allow another class to customize it
  1. 当一个班级需要向另一个班级传达一些信息时
  2. 当一个类想要允许另一个类对其进行自定义时

The classes don't need to know anything about each other beforehand except that the delegate class conforms to the required protocol.

除了委托类符合所需的协议外,这些类不需要事先了解彼此的任何信息。

I highly recommend reading the following two articles. They helped me understand delegates even better than the documentationdid.

我强烈推荐阅读以下两篇文章。他们帮助我比文档更好地理解代表。

回答by m.eldehairy

lets say you have a class that you developed and want to declare a delegate property to be able to notify it when some event happens :

假设您开发了一个类,并希望声明一个委托属性,以便能够在发生某些事件时通知它:

@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

so you declare a protocol in MyClassheader file (or a separate header file) , and declare the required/optional event handlers that your delegate must/should implement , then declare a property in MyClassof type (id< MyClassDelegate>) which means any objective c class that conforms to the protocol MyClassDelegate, you'll notice that the delegate property is declared as weak , this is very important to prevent retain cycle (most often the delegate retains the MyClassinstance so if you declared the delegate as retain, both of them will retain each other and neither of them will ever be released).

所以你在MyClass头文件(或单独的头文件)中声明一个协议,并声明你的委托必须/应该实现的必需/​​可选事件处理程序,然后声明一个MyClass类型为 ( id< MyClassDelegate>)的属性,这意味着任何符合协议MyClassDelegate,您会注意到委托属性被声明为弱,这对于防止保留循环非常重要(大多数情况下委托会保留MyClass实例,因此如果您将委托声明为保留,它们将相互保留,并且两者都不会保留)他们中的一个将永远被释放)。

you will notice also that the protocol methods passes the MyClassinstance to the delegate as parameter , this is best practice in case the delegate want to call some methods on MyClassinstance and also helps when the delegate declares itself as MyClassDelegateto multiple MyClassinstances , like when you have multiple UITableView'sinstances in your ViewControllerand declares itself as a UITableViewDelegateto all of them.

您还会注意到协议方法将MyClass实例作为参数传递给委托,这是最好的做法,以防委托想要在MyClass实例上调用某些方法,并且在委托将自己声明为MyClassDelegate多个MyClass实例时也有帮助,例如当您有多个实例时UITableView's在您的实例中,ViewController并将自己声明为UITableViewDelegate所有这些实例的a 。

and inside your MyClassyou notify the delegate with declared events as follows :

在你的内部,你MyClass用声明的事件通知委托,如下所示:

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

you first check if your delegate responds to the protocol method that you are about to call in case the delegate doesn't implement it and the app will crash then (even if the protocol method is required).

您首先检查您的委托是否响应您将要调用的协议方法,以防委托没有实现它并且应用程序将崩溃(即使需要协议方法)。

回答by Tibidabo

Ok, this is not really an answer to the question, but if you are looking up how to make your own delegate maybe something far simpler could be a better answer for you.

好的,这不是问题的真正答案,但是如果您正在寻找如何制作自己的委托,那么更简单的东西可能对您来说是更好的答案。

I hardly implement my delegates because I rarely need. I can have ONLY ONE delegate for a delegate object. So if you want your delegate for one way communication/passing data than you are much better of with notifications.

我几乎不执行我的委托,因为我很少需要。对于委托对象,我只能有一个委托。因此,如果您希望您的代表以一种方式进行通信/传递数据,那么您最好使用通知。

NSNotification can pass objects to more than one recipients and it is very easy to use. It works like this:

NSNotification 可以将对象传递给多个接收者,并且非常易于使用。它是这样工作的:

MyClass.m file should look like this

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

To use your notification in another classes: Add class as an observer:

要在其他类中使用您的通知:将类添加为观察者:

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

Implement the selector:

实现选择器:

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

Don't forget to remove your class as an observer if

如果出现以下情况,请不要忘记以观察者身份删除您的班级

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