gpt4 book ai didi

ios - NSMutableDictionary 的子类化有哪些陷阱和磨难?

转载 作者:塔克拉玛干 更新时间:2023-11-02 21:12:07 24 4
gpt4 key购买 nike

苹果说

There should typically be little need to subclass NSMutableDictionary. If you do need to customize behavior, it is often better to consider composition rather than subclassing.

(参见 https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSMutableDictionary_Class/)

他们可能应该加强一点,并说风险自负

但是,在某些情况下,子类化 NSMutableDictionary 可能很重要。就我而言,在符号上,它确实与我的代码相关。有很多障碍需要克服。关于此还有其他网络和 SO 条目,但我在旅途中遇到了一些看似新的问题,所以想写下来以纪念我并帮助其他人。所以,我会发布我的答案。请随意贡献您自己的其他发现。

最佳答案

1) 没有代理对象。一开始,出于某种原因,Apple 似乎使 NSMutableDictionary 在一些不寻常的方面不同于 NSMutableSet。我对子类化 NSMutableDictionary 的潜在需求实际上源于需要了解对 NSMutableDictionary 实例的突变更改。 NSMutableSets,例如,使这更容易一些。 NSMutableSets 让您可以访问“代理”对象:mutableSetValueForKey。这为您提供了一种了解设置内容何时发生变化的机制。参见 https://www.objc.io/issues/7-foundation/key-value-coding-and-observing/一些细节。您希望看到的是类似 mutableDictValueForKey 的东西,但它似乎不存在。

2) 在你的子类方法中实现 init!Apple 告诉你你需要覆盖方法:

In a subclass, you must override both of its primitive methods:

setObject:forKey:

removeObjectForKey:

You must also override the primitive methods of the NSDictionary class.

NSDictionary 原始方法是:

initWithObjects:forKeys:count:

@property count

objectForKey:

keyEnumerator:

但是,您还必须覆盖 init 方法!

3) 在 Swift 中执行此操作还行不通! 至少在我尝试此操作的日期(大约 2015 年 10 月 8 日和 Xcode 7),你必须让你的NSMutableDictionary 是 Objective-C 中的子类,而不是 Swift。参见 Cannot override initializer of NSDictionary in Swift

4) NSCoding 不适用于 NSMutableDictionary 子类! 在我的 NSMutableDictionary 子类中,我尝试实现 NSCoding 协议(protocol),但无法让它在上下文中工作键控归档器。键控存档器会生成一个空的 NSMutableDictionary(解码时),而不是我自己的子类,我不知道为什么。一些特殊的 NSMutableDictionary 魔法?

5) Swift 中的下标可能不会削减它。 我尝试只为 Swift 实现下标方法(参见 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html),但在符号上这还有很多不足之处。我真的想要一个与 NSDictionary/NSMutableDictionary 完全互操作的类型,这似乎需要一个子类。

6) 不要只是实现方法;您需要自己的数据! 如果您只是尝试覆盖上面的方法,并调用“super”,您的代码将无法工作。您需要使用“组合”在内部实现 NSMutableDictionary 属性。或者您想要实现字典的任何其他机制。再次,一些类(class)集群魔术正在进行。在下面的 .m 文件中查看我的 dict 属性。

以下是我在 Objective-C 代码方面的最新情况:

//
// SMMutableDictionary.h
// Dictionary
//
// Created by Christopher Prince on 10/6/15.
// Copyright © 2015 Spastic Muffin, LLC. All rights reserved.
//

/* I subclassed NSMutableDictionary because:
1) because I needed a way to know when a key was set or removed. With other mutable objects you can use proxy objects (e.g., see https://www.objc.io/issues/7-foundation/key-value-coding-and-observing/), but a proxy object doesn't seem to be provided by Apple for NSMutableDictionary's.
2) for notational convenience in some other code that I was writing.
*/

// QUESTION: Can I set up an observer to detect any changes to the value of the key's within the dictionary? We'd have to remove this KVO observer if the object was removed. Presumably, with this interface, the way that the object would be removed would be (a) setting with nil, and (b) deallocation of this SMMutableDictionary itself.

#import <Foundation/Foundation.h>

@class SMMutableDictionary;

@protocol SMMutableDictionaryDelegate <NSObject>

@required

// Reports on the assignment to a keyed value for this dictionary and the removal of a key: setObject:forKey: and removeObjectForKey:
- (void) dictionaryWasChanged: (SMMutableDictionary * _Nonnull) dict;

@end

@interface SMMutableDictionary : NSMutableDictionary

// For some reason (more of the ugliness associated with having an NSMutableDictionary subclass), when you unarchive a keyed archive of an SMMutableDictionary, it doesn't give you back the SMMutableDictionary, it gives you an NSMutableDictionary. So, this method is for your convenience. AND, almost even better, when you use a keyed archiver to archive, it uses our encoder method, but doesn't actually generate an archive containing our dictionary!! SO, don't use keyed archiver methods directly, use the following two methods:
- (NSData * _Nullable) archive;
+ (instancetype _Nullable) unarchiveFromData: (NSData * _Nonnull) keyedArchiverData;

// Optional delegate
@property (nonatomic, weak, nullable) id<SMMutableDictionaryDelegate> delegate;

@end

这是 .m 文件:

//
// SMMutableDictionary.m
// Dictionary
//
// Created by Christopher Prince on 10/6/15.
// Copyright © 2015 Spastic Muffin, LLC. All rights reserved.
//

// I wanted to make this a Swift NSMutableDictionary subclass, but run into issues...
// See https://stackoverflow.com/questions/28636598/cannot-override-initializer-of-nsdictionary-in-swift
// http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html
// See also https://stackoverflow.com/questions/10799444/nsdictionary-method-only-defined-for-abstract-class-my-app-crashed
// I tried only implementing the subscript method for Swift (see https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html), but notationally this left much to be desired. I really wanted a type that was fully interoperable with NSDictionary/NSMutableDictionary, which seems to require a subclass.

// See also http://www.smackie.org/notes/2007/07/11/subclassing-nsmutabledictionary/

#import "SMMutableDictionary.h"

@interface SMMutableDictionary()
@property (nonatomic, strong) NSMutableDictionary *dict;
@end

// See this for methods you have to implement to subclass: https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMutableDictionary_Class/index.html
// HOWEVER, while they didn't say you have to subclass the init method, it did't work for me without doing that. i.e., I needed to have [1] below.

@implementation SMMutableDictionary

- (instancetype) initWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt;
{
self = [super init];
if (self) {
self.dict = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys count:cnt];
}
return self;
}

// [1].
- (instancetype) init;
{
self = [super init];
if (self) {
self.dict = [NSMutableDictionary new];
}
return self;
}

// Both of these are useless. See the keyed archiver/unarchiver methods on the .h interface.
/*
- (void)encodeWithCoder:(NSCoder *)aCoder;
{
//[aCoder encodeObject:self.dict];
[aCoder encodeObject:self.dict forKey:@"dict"];
}
*/

/*
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;
{
self = [super initWithCoder:aDecoder];
if (self) {
//self.dict = [aDecoder decodeObject];
self.dict = [aDecoder decodeObjectForKey:@"dict"];
}
return self;
}
*/

- (NSData * _Nullable) archive;
{
return [NSKeyedArchiver archivedDataWithRootObject:self.dict];
}

+ (instancetype _Nullable) unarchiveFromData: (NSData * _Nonnull) keyedArchiverData;
{
NSMutableDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:keyedArchiverData];
if (nil == dict) return nil;

return [[SMMutableDictionary alloc] initWithDictionary:dict];
}

- (NSUInteger) count;
{
return self.dict.count;
}

- (id) objectForKey:(id)aKey;
{
return [self.dict objectForKey:aKey];
}

- (NSEnumerator *)keyEnumerator;
{
return [self.dict keyEnumerator];
}

- (void) setObject:(id)anObject forKey:(id<NSCopying>)aKey;
{
[self.dict setObject:anObject forKey:aKey];
if (self.delegate) {
[self.delegate dictionaryWasChanged:self];
}
}

- (void) removeObjectForKey:(id)aKey;
{
[self.dict removeObjectForKey:aKey];
if (self.delegate) {
[self.delegate dictionaryWasChanged:self];
}
}

@end

更新于 2015 年 10 月 9 日

为了阐明我所说的“突变变化”的意思(回应下面的@quelish),这里有一个带有 NSMutableDictionary 的 KVO 示例。请注意,此输出反射(reflect)下面的测试 1。即,KVO 不指示对 key 的更改。此示例改编自 https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-XID_5

如果您确实知道字典的所有键,您也许可以使用 KVO。参见 Observing NSMutableDictionary changes

//
// ViewController.swift
// Dictionary2
//
// Created by Christopher Prince on 10/9/15.
// Copyright © 2015 Spastic Muffin, LLC. All rights reserved.
//

import UIKit

private var myContext = 0

class ViewController: UIViewController {
var obj = MyObserver()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

print("Test 1")
obj.objectToObserve.myDict["key1"] = "value1"

print("Test 2")
obj.objectToObserve.myDict = NSMutableDictionary()
}
}

class MyObjectToObserve: NSObject {
dynamic var myDict = NSMutableDictionary()
override var description : String {
return "\(myDict)"
}
}

class MyObserver: NSObject {
var objectToObserve = MyObjectToObserve()

override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: "myDict", options: NSKeyValueObservingOptions(rawValue: 0), context: &myContext)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &myContext {
//let newValue = change?[NSKeyValueChangeNewKey]
print("change: \(change)")
print("object: \(object)")
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}

deinit {
objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
}
}

关于ios - NSMutableDictionary 的子类化有哪些陷阱和磨难?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33028236/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com