“NSCFArray突变而被枚举”枚举副本
def leopardRemoveWireless(networkName):
plistPath = '/Library/Preferences/SystemConfiguration/preferences.plist'
# Sanity checks for the plist
if os.path.exists(plistPath):
try:
pl = NSMutableDictionary.dictionaryWithContentsOfFile_(plistPath)
except:
print 'Unable to parse file at path: %s' % plistPath
sys.exit(1)
else:
print 'File does not exist at path: %s' % plistPath
sys.exit(1)
# Create a copy of the dictionary due to emuration
copy = NSDictionary.dictionaryWithDictionary_(pl)
# Iterate through network sets
for Set in copy['Sets']:
UserDefinedName = copy['Sets'][Set]['UserDefinedName']
print 'Processing location: %s' % UserDefinedName
for enX in copy['Sets'][Set]['Network']['Interface']:
print 'Processing interface: %s' % enX
# I think this will always be a single key but this works either way
for key in copy['Sets'][Set]['Network']['Interface'][enX]:
print 'Processing Service: %s' % key
# Try to grab the PreferredNetworks key if any
try:
# Iterate through preferred network sets
index = 0
for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']:
SSID_STR = PreferredNetwork['SSID_STR']
print 'Processing SSID: %s' % SSID_STR
# If the preferred network matches our removal SSID
if SSID_STR == networkName:
print 'Found SSID %s to remove' % SSID_STR
# Delete our in ram copy
print 'Processing Set: %s' % Set
print 'Processing enX: %s' % enX
print 'Processing key: %s' % key
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
index += 1
except KeyError:
print 'Skipping interface without PreferredNetworks'
当我在编辑一个相当复杂的(字典)的plist,然后写更改回原来的文件后,我找到一个特定的键值对错误。问题是,即使我做物业的副本列表的字典:“NSCFArray突变而被枚举”枚举副本
copy = NSDictionary.dictionaryWithDictionary_(pl)
这是给我的标准“的突变,同时列举了”错误,当我编辑原始的,只是循环键作为独立插件(注意缺乏引号)。
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
是我的语法在某种程度上导致它尝试和编辑pl
的字典那么copy
?下面是输出:
... Processing Set: D5C0A0F4-613A-4121-B6AE-4CBA6E2635FF Processing enX: en1 Processing key: AirPort Traceback (most recent call last): File "/Users/tester/Desktop/wifiutil", line 1164, in sys.exit(main()) File "/Users/tester/Desktop/wifiutil", line 1135, in main removeWireless(osVersion,network) File "/Users/tester/Desktop/wifiutil", line 1051, in removeWireless leopardRemoveWireless(network)
File "/Users/tester/Desktop/wifiutil", line 528, in leopardRemoveWireless for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']: File "/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC/objc/_convenience.py", line 431, in enumeratorGenerator yield container_unwrap(anEnumerator.nextObject(), StopIteration) objc.error: NSGenericException - * Collection was mutated while being enumerated.
我认为这个问题是字典的嵌套性质。 dictionaryWithDictionary_()
不会执行任何操作,如深度复制;它所做的就是创建一个新的NSDictionary
并复制值的指针(它自己复制密钥,因为这是NSDictionary
的性质)。
这意味着,虽然你有一个新的顶层,你可以使用枚举,内部字典和数组是完全一样的对象为在原。
你的最后一个循环:
for PreferredNetwork in copy['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks']:
被列举出这些内部数组,然后尝试用del
声明变异之一:
del pl['Sets'][Set]['Network']['Interface'][enX][key]['PreferredNetworks'][index]
这是不可复制的;它与for
中使用的数组对象相同,这会导致异常。您可以通过将这两个表达式传递给id()
来测试。
你必须要么是原始字典的全深拷贝,或(可能更好),你列举的前几天最后一级的副本。
我尝试切换它来复制= NSMutableDictionary.alloc()。initWithDictionary_copyItems_(PL,真) 但仍然有同样的错误。也许我应该制作NSArray的副本? – acidprime 2012-02-08 19:29:38
是的,这仍然不会做一个完整的副本 - 如果你看[文档](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class /Reference/Reference.html#//apple_ref/doc/uid/20000140-BABECHBH)你会看到它只复制第一层;这是你必须自己编码的东西。幸运的是,在Python中这比ObjC容易得多,但我仍然认为最好的方法是在枚举它之前复制最后一个元素。 – 2012-02-08 19:37:17
copy = NSMutableDictionary.dictionaryWithContentsOfFile_(plistPath)虽然对内存管理来说不是最好的。 – acidprime 2012-02-08 19:58:06