- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
ARC 已启用。
我有一个具有 SEL 类型属性的类:@property SEL mySelector;
合成:@synthesize mySelector;
然后我尝试使用 KVC 为其分配一个值:
SEL someSelector = @selector(doSomething:)
NSValue* someSelectorValue = [NSValue value:someSelector withObjCType:@encode(SEL)];
[target setValue:someSelectorValue forKey:@"mySelector"];
我收到错误信息:
[<LACMyClass 0x101b04bc0> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key mySelector.
这显然不是真的——这个类是 KVC 兼容的,它只是不喜欢我传入的值。当我定义 void*
类型的属性时它似乎起作用了SEL
,但这不符合我的要求。
除了使用value:withObjCType:
,我还尝试了valueWithBytes:objCType:
和valueWithPointer:
谁能给我解释一下
最佳答案
默认情况下,自动装箱/拆箱似乎只支持原始类型的特定子集setValue:forKey:
执行。请参阅 "Scalar and Structure Support" chapter 中的表 1 和表 2 “键值编码编程指南”。这里的言外之意就是只有BOOL
, char
, double
, float
, int
, long
, long long
, short
, 以及它们未签名的副本,以及 struct
都得到了完全支持。通过 NSValue
.其他类型,例如 SEL
和其他指针值,appear to be unsupported .
考虑以下程序:
#import <Foundation/Foundation.h>
@interface MyObject : NSObject
@property (nonatomic) SEL mySelector;
@property (nonatomic) void *myVoid;
@property (nonatomic) int myInt;
@property (nonatomic,unsafe_unretained) id myObject;
@end
@implementation MyObject
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
SEL selector = @selector(description);
NSValue *selectorValue = [NSValue valueWithPointer:selector];
NSValue *voidValue = [NSValue valueWithPointer:selector];
NSValue *intValue = @1;
__unsafe_unretained id obj = (__bridge id)(const void *)selector;
MyObject *object = [[MyObject alloc] init];
// The following two calls succeed:
[object setValue:intValue forKey:@"myInt"];
[object setValue:obj forKey:@"myObject"];
// These two throw an exception:
[object setValue:voidValue forUndefinedKey:@"myVoid"];
[object setValue:selectorValue forKey:@"mySelector"];
}
}
我们可以设置int
和 id
属性很好 - 即使使用 __unsafe_unretained
和桥接转换让我们传递选择器值。但是,不支持尝试设置这两种指针类型中的任何一种。
我们如何从这里开始?例如,我们可以覆盖 valueForKey:
和 setValueForKey:
在MyObject
支持拆箱 SEL
类型或拦截特定的键。后一种方法的示例:
@implementation MyObject
- (id)valueForKey:(NSString *)key
{
if ([key isEqualToString:@"mySelector"]) {
return [NSValue valueWithPointer:self.mySelector];
}
return [super valueForKey:key];
}
- (void)setValue:(id)value forKey:(NSString *)key
{
if ([key isEqualToString:@"mySelector"]) {
SEL toSet;
[(NSValue *)value getValue:&toSet];
self.mySelector = toSet;
}
else {
[super setValue:value forUndefinedKey:key];
}
}
@end
在使用中,我们发现它按预期工作:
[object setValue:selectorValue forKey:@"mySelector"];
NSString *string = NSStringFromSelector(object.mySelector);
NSLog(@"selector string = %@", string);
这会将“selector string = description”记录到控制台。
当然,这有可维护性问题,因为您现在必须在每个需要使用 KVC 设置选择器的类中实现这些方法,并且还必须与硬编码键进行比较。一种有风险的解决方法是使用方法调配并替换替换 NSObject
使用我们自己的 KVC 方法的实现,它处理 SEL
的装箱和拆箱类型。
以下程序建立在第一个示例的基础上,很大程度上源自 Mike Ash 的精彩 "let's build KVC"文章,还使用了 Swizzle()
来自 this answer on SO 的函数.请注意,我为了演示目的偷工减料,并且此代码仅适用于 SEL
。具有适当命名的 getter 和 setter 的属性,并且不会直接检查实例变量,这与默认的 KVC 实现不同。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyObject : NSObject
@property (nonatomic) SEL mySelector;
@property (nonatomic) int myInt;
@end
@implementation MyObject
@end
@interface NSObject (ShadyCategory)
@end
@implementation NSObject (ShadyCategory)
// Implementations of shadyValueForKey: and shadySetValue:forKey: Adapted from Mike Ash's "Let's Build KVC" article
// http://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html
// Original MAObject implementation on github at https://github.com/mikeash/MAObject
- (id)shadyValueForKey:(NSString *)key
{
SEL getterSEL = NSSelectorFromString(key);
if ([self respondsToSelector: getterSEL]) {
NSMethodSignature *sig = [self methodSignatureForSelector: getterSEL];
char type = [sig methodReturnType][0];
IMP imp = [self methodForSelector: getterSEL];
if (type == @encode(SEL)[0]) {
return [NSValue valueWithPointer:((SEL (*)(id, SEL))imp)(self, getterSEL)];
}
}
// We will have swapped implementations here, so this call's NSObject's valueForKey: method
return [self shadyValueForKey:key];
}
- (void)shadySetValue:(id)value forKey:(NSString *)key
{
NSString *capitalizedKey = [[[key substringToIndex:1] uppercaseString] stringByAppendingString:[key substringFromIndex:1]];
NSString *setterName = [NSString stringWithFormat: @"set%@:", capitalizedKey];
SEL setterSEL = NSSelectorFromString(setterName);
if ([self respondsToSelector: setterSEL]) {
NSMethodSignature *sig = [self methodSignatureForSelector: setterSEL];
char type = [sig getArgumentTypeAtIndex: 2][0];
IMP imp = [self methodForSelector: setterSEL];
if (type == @encode(SEL)[0]) {
SEL toSet;
[(NSValue *)value getValue:&toSet];
((void (*)(id, SEL, SEL))imp)(self, setterSEL, toSet);
return;
}
}
[self shadySetValue:value forKey:key];
}
@end
// Copied from: https://stackoverflow.com/a/1638940/475052
void Swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
int main(int argc, char *argv[]) {
@autoreleasepool {
Swizzle([NSObject class], @selector(valueForKey:), @selector(shadyValueForKey:));
Swizzle([NSObject class], @selector(setValue:forKey:), @selector(shadySetValue:forKey:));
SEL selector = @selector(description);
MyObject *object = [[MyObject alloc] init];
object.mySelector = selector;
SEL fromProperty = object.mySelector;
NSString *fromPropertyString = NSStringFromSelector(fromProperty);
NSValue *fromKVCValue = [object valueForKey:@"mySelector"];
SEL fromKVC;
[fromKVCValue getValue:&fromKVC];
NSString *fromKVCString = NSStringFromSelector(fromKVC);
NSLog(@"fromProperty = %@ fromKVC = %@", fromPropertyString, fromKVCString);
object.myInt = 1;
NSNumber *myIntFromKVCNumber = [object valueForKey:@"myInt"];
int myIntFromKVC = [myIntFromKVCNumber intValue];
int myIntFromProperty = object.myInt;
NSLog(@"int from kvc = %d from propety = %d", myIntFromKVC, myIntFromProperty);
selector = @selector(class);
NSValue *selectorValue = [NSValue valueWithPointer:selector];
[object setValue:selectorValue forKey:@"mySelector"];
SEL afterSettingWithKVC = object.mySelector;
NSLog(@"after setting the selector with KVC: %@", NSStringFromSelector(afterSettingWithKVC));
[object setValue:@42 forKey:@"myInt"];
int myIntAfterSettingWithKVC = object.myInt;
NSLog(@"after setting the int with KVC: %d", myIntAfterSettingWithKVC);
}
}
这个程序的输出展示了它的装箱和拆箱能力:
2013-08-30 19:37:14.287 KVCSelector[69452:303] fromProperty = description fromKVC = description
2013-08-30 19:37:14.288 KVCSelector[69452:303] int from kvc = 1 from propety = 1
2013-08-30 19:37:14.289 KVCSelector[69452:303] after setting the selector with KVC: class
2013-08-30 19:37:14.289 KVCSelector[69452:303] after setting the int with KVC: 42
调配当然有风险,所以要小心!
关于objective-c - 使用 KVC 分配给 SEL 类型的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18542664/
我在我的代码中使用了 kvc。 [self addObserver:self forKeyPath:@"type" options:NSKeyValueObservingOptionNew |
@interface MyClass: NSObject @property NSArray *arr; @end @inplementation MyClass - (instancetype) i
作为 Cocoa 和 Objective-C 的新手,我对 KVC 和 KVO 有初步的了解。然而,关于 Cocoa 绑定(bind)(如标题为“Cocoa 绑定(bind)编程主题”的 Apple
我有一组从核心数据中选择的对象。我需要从这组对象子集中选择与条件相对应的对象。怎么做? 最佳答案 如果我没理解错的话,您想根据数组中某些对象满足的条件来过滤数组吗? 您可以通过使用 NSPredica
我创建类: @interface KVOGame : NSObject @property (nonatomic, strong) NSString *name; @property (nonatom
我有一个可绑定(bind)协议(protocol) protocol Bindable: class { associatedtype ObjectType: Any associat
以下哪个更快,为什么? CGFloat sum = 0; for (UIView *v in self.subviews) sum += v.frame.size.height; 或 CGFl
如果接收器类符合 NSKeyValueProtocol,则可以选择走很远的路: [myInstance setValue:[NSNumber numberWithInt:2] forKey:@"int
我经常需要检索属于 Set 的第一个对象。 (使用该对象作为该集合的代表。) 我设想了一个集合对象运算符,类似于 @unionOfObjects 但是很明显 @firstObject 是否可以创建这样
我正在尝试让我的头脑专注于“键值编码”。 Apple's docs say: This document describes the NSKeyValueCoding informal protoco
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我正在尝试向我的可重用单元添加一个观察者,问题是它添加了多个观察者。所以我想知道是否有什么办法可以解决这个问题,因为我真的需要这个观察者。 var player: AVPlayer? var post
我想解析 XML 以填充符合 KVC 的对象,但是,我的解析器非常笨,它只是从 XML 属性/标签组装 NSStrings 并尝试通过 KVC 设置它们。 这适用于实际的字符串和数字(我相信),但
我正在尝试观察收藏笔记。这是代码: -(void) registerNotesHeaderViewChangeNotification { [self.selectedVegetableGar
我使用此代码通过我的可变数组订阅事件: [RACObserve(self, marray) subscribeNext:^.... 我是 RAC 的新手,我不明白为什么,例如,如果我使用: NSMut
我有例如我的 NSArray 中有 5 个 MyFile 对象。这 5 个对象中的每一个都具有属性 path。 MyFile *myFile ...; NSString path = myMyFile
这是代码: NSNumber *taskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:@"identity"]; N
有一个例子如下: enum Gender{ case male case female } class Person:NSObject{ var name: String?
我正在尝试了解 Swift 4 的最新添加 - better KVC .在 Playground 上放这个简单的东西什么也打印不出来 class Foo { var name: String
我读过Marcus S. Zarra的iOS相关章节Core Data: Data Storage and Management for iOS, OS X, and iCloud (第 2 版)受益
我是一名优秀的程序员,十分优秀!