我正在尝试实现一个链表,所以我有一个带有头文件的Node类,如下所示:
@interface Node : NSObject
@property(nonatomic,assign)int data;
@property(nonatomic,strong) Node *right;
@property(nonatomic,strong) Node *left;
@end
然后在另一个类中,我正在分配它们然后调用一个方法来销毁给定值的所有出现:
Node *node0 = [[Node alloc]init];
Node *node1 = [[Node alloc]init];
Node *node2 = [[Node alloc]init];
Node *node3 = [[Node alloc]init];
Node *node4 = [[Node alloc]init];
node0.data = 1;
node1.data = 2;
node2.data = 5;
node3.data = 5;
node4.data = 3;
node0.right = node1;
node1.right = node2;
node2.right = node3;
node3.right = node4;
node4.right = NULL;
[self removeNodeWithValue:node0 value:5];
NSLog(@"node %d, %d, %d, %d, %d", node0.data, node1.data, node2.data, node3.data, node4.data);
这是方法本身:
-(void)removeNodeWithValue:(Node *)head value:(int)value
{
Node *toDelete;
while (head != NULL) {
if (head.data == value)
{
toDelete = head;
head = head.right;
toDelete = nil;
}
else
{
head = head.right;
}
}
}
==> 1, 2, 5, 5, 3
我知道我可以更改实例,因为如果我将toDelete = nil
更改为toDelete.data = 4
,则输出为==> 1, 2, 4, 4, 3
。我的问题是,我如何销毁这些实例?感谢。
答案 0 :(得分:3)
好像你还没有理解ARC是如何工作的。只要有一个指向该对象的强指针,该对象就不会被释放。在您的示例中,您的代码失败有两个原因:首先,您始终强烈引用node0
:
Node *node0 = [[Node alloc]init];
只要此指针未设置为nil
(请记住,按惯例NULL
用于常规指针,nil
用于对象指针),该节点将不会被释放。
其次,如果要释放的节点不是第一个节点,那么另一个节点会有一个指向它的强指针,这也是该节点不会被解除分配的另一个原因。保持指向node0
(在您的情况下为toDelete
)的另一个指针将增加节点的节点保留计数,当您将其设置为nil
时,它将返回到它的原始状态值。
要正确地执行此操作,您还必须避免链删除(如果第一个节点被解除分配,它将丢失对第二个节点的强引用,如果没有强指针,则可能会释放该第二个节点,并且还会导致第三个节点要解除分配,等等)。
最后,我建议不要只持有一堆指向每个节点的指针,而是实现一个链表类,它将完成添加/删除节点的工作:
@interface List : NSObject
@property (nonatomic, strong) Node* first;
@property (nonatomic, weak) Node* last;
@end
// Inside the class implementation
- (void) addNodeWithValue: (int) value
{
Node* node= [[Node alloc]init];
node.data= value;
if(!first)
{
last= first= node;
}
else
{
last.right= node;
node.left= last; // left should be a weak property
last= node;
}
}
- (void) removeNodeWithValue: (int) value // O(n) method
{
Node* ptr= first;
while(ptr)
{
if(ptr.data== value)
{
if(ptr== first)
{
first= last= nil;
}
else
{
ptr.left.right= ptr.right;
ptr.right.left= ptr.left;
}
break; // Remove the break if you want to remove all nodes with that value
}
ptr= ptr.right;
}
}
我没有测试过这段代码,我不能保证它能正常工作。
答案 1 :(得分:0)
因此,您希望清除具有指定值的所有节点。首先,您的测试无效,因为您明确引用了所有节点,因此即使从节点结构中清除它们,您的测试日志仍会将其打印出来。其次,当删除方法未发现被测节点具有指定值时,需要以递归方式调用节点结构。第三,节点不应该自己测试,它应该测试left
和right
节点值(节点不能自行删除,因为它不知道它的父节点。)
所以,比如:
-(void)removeNodeWithValue:(Node *)head value:(int)value
{
if (head.right.data == value)
{
head.right = nil;
}
else
{
[self removeNodeWithValue:head.right value:value];
}
if (head.left.data == value)
{
head.left = nil;
}
else
{
[self removeNodeWithValue:head.left value:value];
}
}
这不会测试根节点本身,因为应该在启动之前检查它,然后从控制器本身中删除头项目。
答案 2 :(得分:0)
您的问题不是删除对象,而是指针。
指针就像箭头指向带有内容的盒子(记忆位置)。当你这样做
toDelete = head;
head = head.right;
toDelete = nil;
您只是删除了某个框中的一个“箭头”,但没有删除该框本身。
Wain的答案应该给出正确的方法。