如何在给定的索引处用新对象替换NSMutableArray中的对象

36

我有一个NSMutableArray对象(保留,全部合成),它被很好地初始化了,我可以使用addObject:方法轻松地向其中添加对象。但是,如果我想在特定索引处用新对象替换该NSMutableArray中的一个对象,则无法实现。

例如:

ClassA.h

@interface ClassA : NSObject {
    NSMutableArray *list;
}

@property (nonatomic, copy, readwrite) NSMutableArray *list;

@end

ClassA.m:

#import "ClassA.h"

@implementation ClassA

@synthesize list;

- (id)init 
{
    [super init];
    NSMutableArray *localList = [[NSMutableArray alloc] init];
    self.list = localList;
    [localList release];
    //Add initial data
    [list addObject:@"Hello "];
    [list addObject:@"World"];
}


// Custom set accessor to ensure the new list is mutable
- (void)setList:(NSMutableArray *)newList 
{
    if (list != newList) 
    {
        [list release];
        list = [newList mutableCopy];
    }
}

-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
    int i = [theIndex intValue]-1;
    [self.list replaceObjectAtIndex:i withObject:newTitle];
    NSLog((NSString *)[self.list objectAtIndex:i]);  // gives the correct output
}

然而,这种改变仅在该方法内部有效。从其他任何方法访问时,

NSLog((NSString *)[self.list objectAtIndex:i]);

返回相同的旧值。

我该如何在特定索引处实际替换旧对象为新对象,以便可以从任何其他方法中注意到更改。

我甚至修改了这个方法,但结果仍然是一样的:

-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
    int i = [theIndex intValue]-1;

    NSMutableArray *localList = [[NSMutableArray alloc] init];
    localList = [localList mutableCopy];
    for(int j = 0; j < [list count]; j++)
    {
        if(j == i)
        {
            [localList addObject:newTitle];
            NSLog(@"j == 1");
            NSLog([NSString stringWithFormat:@"%d", j]);
        }
        else
        {
            [localList addObject:(NSString *)[self.list objectAtIndex:j]];
        }
    }
    [self.list release];
    //self.list = [localList mutableCopy];
    [self setList:localList];
    [localList release];
}

请大家帮个忙 :)


1
抱歉,但你真的需要仔细阅读Objective-C文档、内存管理论文(非常好)和属性章节。这段代码真的存在很多漏洞... - Eiko
6个回答

131

这就是解决问题的方法:

[myMutableArray replaceObjectAtIndex:index withObject:newObject];

你能告诉我关于 replaceObjectAtIndex 方法的信息吗?如果我调用 list[i] = newTitle; 会编译通过而没有警告或错误,这种用法有什么问题吗?谢谢 :) - hqt
9
@Mario,你甚至没有读问题,是吗?但是嘿,其他52个人也没有读... - walkytalky

8

这里有几个混淆的地方。

你不需要对新创建的NSMutableArray进行使其可变。它已经是可变的--线索在名称中。只有在想要属性具有copy语义(当然,您可以设置并且可能有充分理由)时才需要在setter中执行此操作。但是,在更新的updateTitle代码中显示这样做肯定是不必要的,并且这样做会泄漏localList

此外,你将属性访问通过self.list和直接使用list混合在同一个方法中。这并不无效,但这是不好的实践,因为这意味着绕过了访问器方法所做的任何其他事情。像这样的属性通常会通过self做所有事情,除了在访问器本身或dealloc以及可能在init(意见似乎有所不同)中的访问器之外,那里您将直接访问ivar。

此外,永远不要调用[self.list 释放]--属性访问器不给其调用者所有权。这样做会导致悲伤,记住我的话。

所有这些都没有回答真正的问题,即为什么您的更改会消失。根据我所看到的原始updateTitle代码并不解释这个问题--它应该起作用。因此,我怀疑在其他地方调用了self.list = theOriginalList,从而撤消了您的更改。

更新:

只是为了争辩,我将发布我认为您发布的代码可能是如何看起来的。我保留了您使用字符串传递索引以进行updateTitle的方式,但我想指出,这样做是错误的。它是一个数字,您应该将其作为数字传递。即使数字来自文本字段或类似的东西,那也是调用者的问题;类接口应该指定一个数字。同样的是从基于1的索引更改为基于0的索引。请不要隐式地执行此类操作,它是哭泣和咬牙切齿的配方。

ClassA.h:

#import <Cocoa/Cocoa.h>
@interface ClassA : NSObject
{
    NSMutableArray* list;
}
- (void) setList:(NSMutableArray*)newList;
- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex;
@property (nonatomic, copy, readwrite) NSMutableArray* list;
@end

ClassA.m:

#import "ClassA.h"

@implementation ClassA
@synthesize list;

- (id) init
{
    if ( self = [super init] )
    {
        list = [[NSMutableArray alloc] init];
        [list addObject:@"Hello "];
        [list addObject:@"World"];
    }
    return self;
}

- (void) setList:(NSMutableArray*) newList
{
    if ( list != newList )
    {
        [list release];
        list = [newList mutableCopy];
    }
}

- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex
{
    int i = [theIndex intValue] - 1;
    [self.list replaceObjectAtIndex:i withObject:newTitle];
}

- (void) dealloc
{
    [list release];
    [super dealloc];
}

@end

这将清除各种问题,但请注意updateTitle基本上是相同的。如果您将所有内容放入其中,更改仍无法生效,则肯定在某处重置了list


感谢walkytalky的建议。我已经相应地修改了我的代码。现在,我已经仔细查看了我的代码,但是我找不到任何一个self.list = theOriginalList的调用。但是我想知道当使用'replaceObjectAtIndex'时是否会调用'setList:'方法。有什么想法吗? 再次感谢 :) - user339076
1
replaceObjectAtIndex 绝对不应该调用 setList -- 它是数组本身的一个方法,既不知道也不关心它所属的对象的属性访问器。 - walkytalky
你能告诉我关于 replaceObjectAtIndex 方法的信息吗?如果我调用 list[i] = newTitle; 会编译通过而没有警告或错误,这种用法有什么问题吗?谢谢 :) - hqt
@hqt 使用是正确的,只是在回答这个问题的时候它还不存在——相关的ObjC语法用于Cocoa数组和字典大约是在2012年添加的。 - walkytalky

8
更直接的答案是:
self.list[i] = newTitle;

这就像是“它可以工作”。

[self.list replaceObjectAtIndex:i withObject:newTitle];

你没有注意到属性中的“复制”,这导致每个变体都表现不良。 - gnasher729

0
看看这行代码:
@property (nonatomic, copy, readwrite) NSMutableArray *list;
copy 的意思是,每当你访问 self.list 时,你得到的不是你对象的 "_list" 实例变量,而是该列表的一个副本。如果你写 [self.list replaceObjectAtIndex... ],你将替换副本列表中的一个对象;原始的 _list 不会改变。只需使用即可。
@property (nonatomic, strong, readwrite) NSMutableArray *list;

为避免混淆,请移除“list”实例变量和@synthesize语句,然后使用_list访问实例变量。

如果我没记错的话,“copy”属性意味着只有在“设置”实例时才会创建副本。但是像你提到的那样,在“获取”时不会发生这种情况。请参阅https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html。 - Oded Regev
@OdedRegev 对的,正如文档所说,copy 属性仅适用于新创建的属性。据我理解,getter 方法将返回原始对象,但不允许您在所有者类中修改它(它将保持在原始未修改的属性中)。 - Alejandro Iván

0

终于得到了完美的代码,

let DuplicateArray: NSArray = array
let DuplicateMutableArray: NSMutableArray = []
DuplicateMutableArray.addObjectsFromArray(DuplicateArray as [AnyObject])
var dic = (DuplicateMutableArray[0] as! [NSObject : AnyObject])
dic["is_married"] = "false"
DuplicateMutableArray[self.SelectedIndexPath] = dic
array = []
array = (DuplicateMutableArray.copy() as? NSArray)!

//输出将会是这样的

array =  [
  {
    "name": "Kavin",
    "Age": 25,
    "is_married": "false"
  },
  {
    "name": "Kumar",
    "Age": 25,
    "is_married": "false"
  }
]

0

对于Swift,您可以尝试:

 //if you have indexPath
   self.readArray.removeAtIndex((indexPath?.row)!)
   self.readArray.insert(tempDict, atIndex: (indexPath?.row)!)
 //tempDict is NSDictionary object.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接