我在objective-c中遇到了一个奇怪的问题。这是代码:
STViewController.h
#import <UIKit/UIKit.h>
@interface STViewController : UIViewController <UIAlertViewDelegate>
+(void)myStaticMethod;
@end
STViewController.m
#import "STViewController.h"
@implementation STViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[STViewController myStaticMethod];
}
+ (void)myStaticMethod {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Foo bar"
message:@"baz bat"
//what does self even mean in this context? The class object STViewController?
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK", nil];
[alert show];
[alert release];
}
#pragma mark UIAlertViewDelegate
// TRICKY PART if it's static it works, if it's not, it doesn't.
// even though the protocol declares instance methods (with a minus).
+ (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(@"It works!");
}
@end
为什么会发生这种情况?这是正确的吗?我没有收到任何错误或警告。协议方法声明中的-/+有作用吗?
类内方法self
指类对象。类对象是一个普通的objc对象(从NSObject
派生(,它将接收发送到类的消息(类方法,带有"+"的方法(。
在您的案例中,您使用类对象作为UIAlertView
的委托(这很有效,因为UIAlertView的API不明确要求静态类型为id<UIAlertViewDelegate>
的对象(。现在,警报视图只会将其委托消息发送到类对象,这也很好,因为您将它们实现为类方法。
首先,您必须知道Class
(通常来自[MyClass class]
(对象也是有效的ObjC对象。这意味着您也可以向类对象发送消息。
例如
@interface MyClass : NSObject
+ (NSString *)name;
@end
@implementation MyClass
+ (NSString *)name {
return NSStringFromClass(self); // note in class method, self == [MyClass class]
}
@end
// ------- in some method
id cls = [MyClass class]; // the correct type should be Class, but since Class is an object, id will also work
NSLog(@"%@", [cls name]); // call like instance method - MyClass
NSLog(@"%@", [MyClass name]); // call like class method - MyClass
因此,您可以像使用其他对象一样使用类对象,并像调用实例方法一样调用类方法。
类方法实际上是实例方法!!不同之处在于类方法是元类的实例方法,后者是Class
的类。有关元类的更多信息:http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
此外,您的类接口是不正确的,因为不可能(至少在编译器时(向元类添加协议。因此,如果你这样做,[self conformsToProtocol:@protocol(UIAlertViewDelegate)]
将返回NO。但你实现了+ alertView:clickedButtonAtIndex:
,它将添加此方法作为元类的实例方法,因此委托代码有效,[self responseToSelector:@selector(alertView:clickedButtonAtIndex:)]
将返回YES。
您将self
设置为动态的警报委托,但方法调用是静态的,因此这是错误的,因为当您在未来某个时间调用+ (void)myStaticMethod
时,self
可能未初始化,而是nil
。这就是为什么可能会出现其他错误以及未定义的行为