UIView 作为字典键?

发布于 2024-09-04 19:29:38 字数 126 浏览 8 评论 0原文

我想要一个从 UIView 映射到其他内容的 NSDictionary

但是,由于 UIView 没有实现 NSCopying 协议,因此我无法直接将它们用作字典键。

I want to have a NSDictionary that maps from UIViews to something else.

However, since UIViews do not implement the NSCopying protocol, I can't use them directly as dictionary keys.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

情痴 2024-09-11 19:29:38

您可以使用 NSValue 保存指向 UIView 的指针,并将其用作键。 NSValues
是可复制的。但是,如果视图被销毁,NSValue 将保存一个
垃圾指针。

You can use an NSValue holding the pointer to the UIView and use this as key. NSValues
are copyable. but, if the view is destroyed, the NSValue will hold a
junk pointer.

诗酒趁年少 2024-09-11 19:29:38

这是实际的代码(基于 luvieere 的答案 和 Yar 的进一步建议):

// create dictionary
NSMutableDictionary* dict = [NSMutableDictionary new];
// set value
UIView* view = [UILabel new];
dict[[NSValue valueWithNonretainedObject:view]] = @"foo";
// get value
NSString* foo = dict[[NSValue valueWithNonretainedObject:view]];

Here is the actual code (based on the answer by luvieere and further suggestion by Yar):

// create dictionary
NSMutableDictionary* dict = [NSMutableDictionary new];
// set value
UIView* view = [UILabel new];
dict[[NSValue valueWithNonretainedObject:view]] = @"foo";
// get value
NSString* foo = dict[[NSValue valueWithNonretainedObject:view]];
生死何惧 2024-09-11 19:29:38

虽然这并不是它们真正的目的,但您可以使用 关联引用

static char associate_key;
void setValueForUIView(UIView * view, id val){
    objc_setAssociatedObject(view, &associate_key, val, OBJC_ASSOCIATION_RETAIN);
}

id valueForUIView(UIView * view){
    return objc_getAssociatedObject(view, &associate_key);
}

您甚至可以将其包装在类中 ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable*;在这种情况下,您可能希望保留用作键的视图。

像这样的东西(未经测试):

#import "ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable.h"
#import <objc/runtime.h>

static char associate_key;

@implementation ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable

- (void)setObject: (id)obj forKey: (id)key
{
    // Remove association and release key if obj is nil but something was
    // previously set
    if( !obj ){
        if( [self objectForKey:key] ){
            objc_setAssociatedObject(key, &associate_key, nil, OBJC_ASSOCIATION_RETAIN);
            [key release];

        }
        return;
    }

    [key retain];
    // retain/release for obj is handled by associated objects functions
    objc_setAssociatedObject(key, &associate_key, obj, OBJC_ASSOCIATION_RETAIN);
}

- (id)objectForKey: (id)key 
{
    return objc_getAssociatedObject(key, &associate_key);
}

@end

*名称可能需要一些修改。

Although this isn't really what they're intended for, you could whip up a functional dictionary-like interface using Associative References:

static char associate_key;
void setValueForUIView(UIView * view, id val){
    objc_setAssociatedObject(view, &associate_key, val, OBJC_ASSOCIATION_RETAIN);
}

id valueForUIView(UIView * view){
    return objc_getAssociatedObject(view, &associate_key);
}

You could even wrap this up in a class ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable*; in that case you might want to retain the views that you use as keys.

Something like this (untested):

#import "ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable.h"
#import <objc/runtime.h>

static char associate_key;

@implementation ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable

- (void)setObject: (id)obj forKey: (id)key
{
    // Remove association and release key if obj is nil but something was
    // previously set
    if( !obj ){
        if( [self objectForKey:key] ){
            objc_setAssociatedObject(key, &associate_key, nil, OBJC_ASSOCIATION_RETAIN);
            [key release];

        }
        return;
    }

    [key retain];
    // retain/release for obj is handled by associated objects functions
    objc_setAssociatedObject(key, &associate_key, obj, OBJC_ASSOCIATION_RETAIN);
}

- (id)objectForKey: (id)key 
{
    return objc_getAssociatedObject(key, &associate_key);
}

@end

*The name may need some work.

遇见了你 2024-09-11 19:29:38

如果您在 iOS 6 之前不需要支持,NSMapTable(由 neilsbot 建议)可以很好地工作,因为它可以提供枚举器在集合中的键上。这对于所有文本字段通用的代码来说非常方便,例如设置委托或将文本值与 NSUserDefaults 实例双向同步。

in viewDidLoad

self.userDefFromTextField = [NSMapTable weakToStrongObjectsMapTable];
[self.userDefFromTextField setObject:@"fooUserDefKey" forKey:self.textFieldFoo];
[self.userDefFromTextField setObject:@"barUserDefKey" forKey:self.textFieldBar];
// skipped for clarity: more text fields

NSEnumerator *textFieldEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [textFieldEnumerator nextObject]) {
    textField.delegate = self;
}

in viewWillAppear:

NSEnumerator *keyEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [keyEnumerator nextObject]) {
    textField.text = [self.userDefaults stringForKey:[self.textFields objectForKey:textField]];
}

in textField:shouldChangeCharactersInRange:replacementString:

NSString *resultingText = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(resultingText.length == 0) return YES;

NSString *preferenceKey = [self.textFields objectForKey:textField];
if(preferenceKey) [self.userDefaults setString:resultingText forKey:preferenceKey];
return YES;

现在我要哭了,因为我在意识到我的 iOS 5.1 目标应用程序无法使用它之前实现了所有这些。 NSMapTable 是在 iOS 6 中引入的。

Provided you don't need to support before iOS 6, NSMapTable (suggested by neilsbot) works well because it can provide an enumerator over the keys in the collection. That's handy for code common to all of the text fields, like setting the delegate or bi-directionally syncing the text values with an NSUserDefaults instance.

in viewDidLoad

self.userDefFromTextField = [NSMapTable weakToStrongObjectsMapTable];
[self.userDefFromTextField setObject:@"fooUserDefKey" forKey:self.textFieldFoo];
[self.userDefFromTextField setObject:@"barUserDefKey" forKey:self.textFieldBar];
// skipped for clarity: more text fields

NSEnumerator *textFieldEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [textFieldEnumerator nextObject]) {
    textField.delegate = self;
}

in viewWillAppear:

NSEnumerator *keyEnumerator = [self.userDefFromTextField keyEnumerator];
UITextField *textField;
while (textField = [keyEnumerator nextObject]) {
    textField.text = [self.userDefaults stringForKey:[self.textFields objectForKey:textField]];
}

in textField:shouldChangeCharactersInRange:replacementString:

NSString *resultingText = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(resultingText.length == 0) return YES;

NSString *preferenceKey = [self.textFields objectForKey:textField];
if(preferenceKey) [self.userDefaults setString:resultingText forKey:preferenceKey];
return YES;

And now I will go cry, because I implemented all of this before realizing that my iOS 5.1-targeted app can't use it. NSMapTable was introduced in iOS 6.

稀香 2024-09-11 19:29:38

与其存储指向视图的指针并冒垃圾问题的风险,不如为 UIView 提供一个标签并将标签的值存储在字典中。安全得多。

Rather than store a pointer to the view and risk the garbage issue, just give the UIView a tag and store the tag's value in the dictionary. Much safer.

仅冇旳回忆 2024-09-11 19:29:38

我正在使用 Objective-C++ 提供的 ARC 下的简单解决方案。

MyClass.mm:

#import <map>

@implementation MyClass
{
    std::map<UIView* __weak, UIColor* __strong> viewMap;
}

- (void) someMethod
{
    viewMap[self.someView] = [UIColor redColor];
}

在这个例子中,我通过使所有值都必须是 UIColor* 来获得更强的类型检查,这就是我所需要的。但是,如果您想允许任何对象作为值,您也可以使用 id 作为值类型,例如:std::mapviewMap; 对于键也是如此:id __weak, id __strong> viewMap;

您还可以根据需要更改 __strong__weak 属性。就我而言,视图已经由我在其中使用的视图控制器保留,因此我认为没有必要使用强指针指向它们。

I'm using a simple solution under ARC provided by Objective-C++.

MyClass.mm:

#import <map>

@implementation MyClass
{
    std::map<UIView* __weak, UIColor* __strong> viewMap;
}

- (void) someMethod
{
    viewMap[self.someView] = [UIColor redColor];
}

In this example I am getting stronger type checking by making all the values have to be a UIColor* which is all I needed this for. But you could also use id as the value type if you want to allow any object as the value, ex: std::map<UIView* __weak, id __strong> viewMap; Likewise for keys: id __weak, id __strong> viewMap;

You can also vary the __strong and __weak attributes as needed. In my case, the views are already retained by the view controller that I use this in, so I saw no need to take a strong pointer to them.

溺ぐ爱和你が 2024-09-11 19:29:38

当您偶尔只需要 UIView 作为键时,这是一个简单的解决方案,我用它来存储 UILabelUIColor

NSArray<UIView *> *views = @[viewA,viewB,viewC,viewD];
NSArray *values = @[valueA,valueB,valueC,valueD];

for(int i = 0;i < 4;i++) {
    UIView *key = views[i];
    id value = values[i]
    //do something
}

id value = values[[views indexOfObject:key]]

a simple solution when you just want UIView as key occasionally,I use it to store UILabel and UIColor

NSArray<UIView *> *views = @[viewA,viewB,viewC,viewD];
NSArray *values = @[valueA,valueB,valueC,valueD];

for(int i = 0;i < 4;i++) {
    UIView *key = views[i];
    id value = values[i]
    //do something
}

id value = values[[views indexOfObject:key]]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文