Должно быть возможно достаточно просто перенести это поведение на произвольные классы.Я с уверенностью, но без гарантии представляю следующий код, который вы сможете использовать для добавления реализации без исключения valueForUndefinedKey:
в любой класс с одной централизованной строкой кода на класс во время запуска приложения.Если вы хотите сохранить еще больше кода, вы можете сделать так, чтобы все классы, которые вы хотите, чтобы это поведение наследовало от общего подкласса NSManagedObject, а затем применить это к этому общему классу, и все ваши подклассы будут наследовать поведение.Более подробно после, но вот код:
Заголовок (NSObject+ValueForUndefinedKeyAdding.h
):
@interface NSObject (ValueForUndefinedKeyAdding)
+ (void)addCustomValueForUndefinedKeyImplementation: (IMP)handler;
@end
Реализация (NSObject+ValueForUndefinedKeyAdding.m
):
#import "NSObject+ValueForUndefinedKeyAdding.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation NSObject (ValueForUndefinedKeyAdding)
+ (void)addCustomValueForUndefinedKeyImplementation: (IMP)handler
{
Class clazz = self;
if (clazz == nil)
return;
if (clazz == [NSObject class] || clazz == [NSManagedObject class])
{
NSLog(@"Don't try to do this to %@; Really.", NSStringFromClass(clazz));
return;
}
SEL vfuk = @selector(valueForUndefinedKey:);
@synchronized([NSObject class])
{
Method nsoMethod = class_getInstanceMethod([NSObject class], vfuk);
Method nsmoMethod = class_getInstanceMethod([NSManagedObject class], vfuk);
Method origMethod = class_getInstanceMethod(clazz, vfuk);
if (origMethod != nsoMethod && origMethod != nsmoMethod)
{
NSLog(@"%@ already has a custom %@ implementation. Replacing that would likely break stuff.",
NSStringFromClass(clazz), NSStringFromSelector(vfuk));
return;
}
if(!class_addMethod(clazz, vfuk, handler, method_getTypeEncoding(nsoMethod)))
{
NSLog(@"Could not add valueForUndefinedKey: method to class: %@", NSStringFromClass(clazz));
}
}
}
@end
Затем, вваш класс AppDelegate (или действительно где угодно, но, вероятно, имеет смысл поместить его куда-то в центр, чтобы вы знали, где его найти, когда вы хотите добавить или удалить классы из списка), поместите этот код, который добавляет эту функциональность к выбранным вами классамво время запуска:
#import "MyAppDelegate.h"
#import "NSObject+ValueForUndefinedKeyAdding.h"
#import "MyOtherClass1.h"
#import "MyOtherClass2.h"
#import "MyOtherClass3.h"
static id ExceptionlessVFUKIMP(id self, SEL cmd, NSString* inKey)
{
NSLog(@"Not throwing an exception for undefined key: %@ on instance of %@", inKey, [self class]);
return nil;
}
@implementation MyAppDelegate
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[MyOtherClass1 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
[MyOtherClass2 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
[MyOtherClass3 addCustomValueForUndefinedKeyImplementation: (IMP)ExceptionlessVFUKIMP];
});
}
// ... rest of app delegate class ...
@end
Здесь я добавляю пользовательскую реализацию для valueForUndefinedKey:
к классам MyOtherClass1, 2 и 3. В качестве примера реализации я предоставил только NSLog
s ивозвращает ноль, но вы можете изменить реализацию так, как вам хочется, изменив код в ExceptionlessVFUKIMP
.Если вы удалите NSLog
и просто вернете ноль, я подозреваю, что вы получите то, что вы хотите, основываясь на вашем вопросе.
Этот код НИКОГДА не бросает вызов методам, он только добавляет одинесли его там нетЯ поставил проверки, чтобы предотвратить использование этого в классах, которые уже имеют свои собственные пользовательские реализации valueForUndefinedKey:
, потому что, если кто-то поместит этот метод в свой класс, будет ожидание, что он будет продолжать вызываться.Также обратите внимание, что может существовать код AppKit, который ОЖИДАЕТ исключения из реализаций NSObject / NSManagedObject.(Я точно не знаю, но это можно рассмотреть.)
Несколько замечаний:
NSManagedObject
предоставляет пользовательскую реализацию для valueForUndefinedKey:
Пошаговая сборка вотладчик, все, что , по-видимому, должен сделать, это выдать примерно то же исключение с немного другим сообщением.Основываясь на этом 5-минутном исследовании отладчика, я чувствую, что было бы безопасно использовать это с NSManagedObject
подклассами, но я не уверен на 100% - там могло быть какое-то поведение, которое я не уловил.Остерегайтесь.
Кроме того, если вы используете этот подход, у вас нет хорошего способа узнать, возвращает ли valueForKey:
nil
, потому что keyPath действителен и состояние оказалосьnil
, или если он возвращает nil
, потому что keyPath недействителен, и обработчик привитого объекта возвратил nil.Чтобы сделать это, вам нужно сделать что-то другое и конкретную реализацию.(Возможно, верните [NSNull null]
или какое-либо другое значение часового типа или установите какой-либо флаг в локальном хранилище потока, которое вы могли бы проверить, но на этом этапе это действительно намного проще, чем @try/@catch
?) Просто кое-что, о чем следует знать.
Это, кажется, работает очень хорошо для меня;Надеюсь, это полезно для вас.