苹果说
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.
他们可能应该加强一点,并说风险自负。
但是,在某些情况下,子类化 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/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
您认为可以作为插件很好地存在于您的Rails应用程序中必须实现的哪些行为?您过去曾搜索过哪些插件功能但找不到?哪些现有的Rails插件可以改进或扩展,如何改进或扩展? 最佳答案 我希望在管理界面中看到一个引擎插件,它提供了应用程序中所有模型的仪表板摘要,以及可配置的事件图表。 关于ruby-on-rails-您希望看到哪些Rails插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questio
假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项
我可以访问ruby的异常层次结构(它在镐和蜂鸟中都提到过),但我不确定使用哪个异常,因为我没有找到关于每个术语含义的任何信息。使用正确的异常类重要吗? 最佳答案 创建您自己的异常时很重要。一个重要的警告是,继承自Exception而不是StandardError(常见错误)的异常不会被rescue捕获(没有任何参数)。 关于ruby-在ruby中使用正确的异常子类,我们在StackOverflow上找到一个类似的问题: https://stackove