Obj-C, как использовать категорию для предоставления методов, которые я буду использовать в методах делегатов? - PullRequest
0 голосов
/ 07 декабря 2018

Я хочу предоставить методы, используемые в нескольких контроллерах представления, вызываемых в моих методах делегатов.

Например, у меня есть некоторые функции 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 file - мой вызывающий контроллер представления (например, My View Controller)

#import "UIViewController+CloudKitDelegates.h"

m file - метод делегата

-(NSString*)getDBPath
{
    [self.iCloudDBDelegate crashLog: @"testing"];

Fromпри этом вызове я получаю сообщение об ошибке ...

'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]: 
    unrecognized selector sent to instance

Ошибка показывает, что у моего вызывающего контроллера представления MyViewController нет метода crashLog, который есть в моей категории.

Есть идеи, где я иду не так?

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

(я добавил это к своей собственной структуре, но я не думаю, что это важно)

Да, это типичная проблема.Вы не смогли включить -ObjC во флаги ссылок.

См. Создание статических библиотек Objective-C с категориями .Это относится и к фреймворкам.

ObjC не создает символы компоновщика для методов.Это не может, они не разрешены до времени выполнения.Таким образом, методы категорий не рассматриваются компоновщиком как «отсутствующие», и это не мешает связать соответствующий модуль компиляции.Это важная оптимизация, которая не дает вам связать всю массивную библиотеку C только потому, что вы используете в ней одну функцию, но категории Objective C нарушают некоторые предположения компоновщика.Компилятор видел определение (через заголовок), но компоновщику было все равно, поэтому до времени выполнения ошибки не возникает.

Флаг -ObjC говорит: «этот C-выглядящий модуль компиляции на самом деле Objective-C; связать все это, даже если вы не думаете, что вам нужно. "

0 голосов
/ 08 декабря 2018

Проблема: идентичный метод 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
...