如何在没有首先超出范围的情况下将指针的地址传递给线程?

时间:2012-09-28 18:47:06

标签: objective-c xcode multithreading macos cocoa

我有一个Objective-C方法,它创建一个指针,然后使用performSelectorInBackground:将它传递给一个新线程。问题是,我需要将指针的地址传递给线程,因为我想从线程中释放与之关联的内存。我遇到的问题是,当新线程启动时,调用方法结束,导致指针超出范围。如何在线程可以接收指针地址的地方保持足够长的时间?

如果我只是在主线程上调用“pointerTest”方法,那么这一切都有效。

这是我的代码:

-(void)pointerTest:(NSValue*)pointer
{   
    void **p = (void**)[pointer pointerValue];
    fprintf(stderr,"p was %s\n",(char*)(*p)); //prints "Hello There!"
    free(*p);
    *p = NULL;
}

现在,在主线程上:

-(IBAction)doTest:(id)sender
{
    char *str = "Hello There!";

    void *p = malloc(strlen(str)+1);
    strcpy(p, str);

    //this works
    //[self pointerTest:[NSValue valueWithPointer:&p]];

    //this fails
    [self performSelectorInBackground:@selector(pointerTest:) withObject:[NSValue valueWithPointer:&p]];

    if(!p){
        fprintf(stderr,"p was NULL!\n");
    }else{
        fprintf(stderr,"p was NOT NULL: %s\n",p);
    }
}

更新

此代码显示如果我没有将指针传递给pointerTest:,我无法从pointerTest中释放p:

-(void)pointerTest:(NSValue*)pointer
{   
    void *p = (void*)[pointer pointerValue];
    fprintf(stderr,"p was %s\n",(char*)p); //prints "Hello There!"
    free(p);
    fprintf(stderr,"p was %s\n",(char*)p); //STILL prints "Hello There!"
    *p = NULL;
    fprintf(stderr,"p was %s\n",(char*)p); //prints "(null)"
}

现在,在调用方法中:

//I pass p, NOT a pointer to p
[self pointerTest:[NSValue valueWithPointer:p]];

if(p){
    //p is not NULL, and in fact I can print its value
    fprintf(stderr,"p was NOT NULL: %s\n",p);
}

但是,如果我将指针传递给p而不是p,我可以在pointerTest中从预期释放其值:并且在从pointerTest返回时,p在调用方法中为NULL。

1 个答案:

答案 0 :(得分:3)

[我不会问你为什么要把malloc C-strings与Objective-C混合,我猜你正在试验。这样做本身没有任何问题,在这种情况下这是不寻常的。]

您根本不需要将指针传递给指针

指针名称。有些框包含字符,一些员工记录等,还有一些框名称(其他框)。 malloc找到一个所需大小的框并返回其名称,您可以传递它,就像传递整数值一样 - 您不需要传递包含包含该框的名称的框的名称你的角色......

同样,当你malloc需要在C字符串上允许尾随NUL字符时,所以在字符串长度上加1。

尝试:

- (void) pointerTest:(NSValue *)pointer
{   
   void *p = (void *)[pointer pointerValue];
   fprintf(stderr,"p (%p) was %s\n", p, (char *)p);

   free(p);
}

- (IBAction) doTest:(id)sender
{
   char *str = "Hello There!";

   void *p = malloc(strlen(str)+1); // allow for NUL
   fprintf(stderr,"p is %p\n", p);
   strcpy(p, str);

   // this works
   // [self pointerTest:[NSValue valueWithPointer:p]];

   // and so does this
   [self performSelectorInBackground:@selector(pointerTest:) withObject:[NSValue valueWithPointer:p]];

   if(!p)
   {
      fprintf(stderr,"p was NULL!\n");
   }
}

请注意,您不需要在p中包装NSValue,将指针值传递给Objective-C方法也没有错。

HTH

评论后

您将指针的有效性与指针所指向的内存中可能出现的内容混淆。

指针引用的内存已被释放。调用free后,指针值不再有效 - 它是悬空指针

您现在悬挂的指针值所引用的内存可以随时重复使用。恰好在你的fprintf时,内存还没有被重用,所以你看到了旧的内容 - 释放内存并不能清除它。

你的NULL商店打算删除你的悬空指针,这样做可能是一般的好习惯,但是你已经发现了获取一个短期变量的地址并将其传递给另一个方法是创建悬空指针的好方法。所以你最终试图通过使用悬挂指针A框来清理从方框A到方框B的悬空指针,因为方框A已经被释放了......让我们尝试以不同的方式解释; - )

在这种情况下,您创建了一个指向属于p的局部变量(框)doTest的指针,只要doTest完成,系统就会返回用于{的p框。 {1}}到自由盒堆(或在这种情况下是堆栈),你的指针变成了悬空;当pointerTest的原始代码尝试通过该NULL存储到该框时,现在悬空,指针就会出现问题。