我想提供在我的委托方法中调用的多个视图控制器中使用的方法。
例如,我有一些CloudKit功能(已将其添加到我自己的框架中,但我认为这并不重要),我想在其中提供一些崩溃日志记录。 以前,我在每个视图控制器中都有一个crashLog函数,可以正常工作,但是我有很多重复的代码。
因此,我想用这些方法来产生一个类别。
但是我很难让我的委托方法来查看这些类别方法。
这是我的代码。
UIViewController + CloudKitDelegates.h
@interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
@property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
@end
UIViewController + CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
@implementation UIViewController (CloudKitDelegates)
@dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(@"%@", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
@end
h文件-我的调用视图控制器(例如“我的视图控制器”)
#import "UIViewController+CloudKitDelegates.h"
m文件-委托方法
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: @"testing"];
从这次通话中我遇到了一个错误...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
该错误表明我的调用视图控制器MyViewController没有属于我的类别的crashLog方法。
有什么想法我要去哪里吗?
答案 0 :(得分:1)
问题:例如,多个类中的方法crashLog:
相同
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
解决方案A:将crashLog:
移到一个公共超类(或超类UIViewController
上的类别)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
解决方案B:将crashLog:
移至代理和协议
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
现在我们遇到了新问题:多个类中的属性iCloudDBDelegate
解决方案B + A:将crashLog
移至委托,将iCloudDBDelegate
属性移至超类
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
解决方案C:
另一种方法是像NSUserDefaults.standardUserDefaults
或NSFontManager.sharedFontManager
:CloudDBManager.sharedCloudDBManager
这样的单例对象。无需类别或协议,只需包含CloudDBManager.h并在任何地方使用CloudDBManager.sharedCloudDBManager
。
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
答案 1 :(得分:1)
(我已将其添加到自己的框架中,但我认为这并不重要)
是的,这是典型的问题。您没有在链接标志中包含-ObjC
。
请参见Building Objective-C static libraries with categories。这也适用于框架。
ObjC不会为方法创建链接器符号。不能,直到运行时才解决。因此,链接器不会将类别方法视为“丢失”,并且不会麻烦链接相关的编译单元。这是一项重要的优化,它使您无法链接所有大型C库,只是因为您在其中使用了一个函数,但是Objective-C类别破坏了链接程序的某些假设。编译器(通过标头)看到了定义,但是链接器不在乎,因此直到运行时才出现错误。
-ObjC
标志说:“这个看起来像C的编译单元实际上是Objective-C;即使您不需要,也可以链接所有这些单元。”