Objective-C NSThread可以访问全局变量吗?

时间:2022-06-01 22:18:23

Ok, basically I have a run loop going in my application every second or two, while at the same time I have another thread going that is looping through the listenForPackets method; broadcastMessage is only initiated when another action method takes place. The important part of this question is that when the listener thread is running seperately from the main thread, it never print out any of the print commands and it seems to not allow access to a global variable that I have defined called recvMessage which lies outside of the interface and implementation sections.

好的,基本上我的应用程序每隔一两秒运行一次,而同时我有另一个循环通过listenForPackets方法循环; broadcastMessage仅在发生另一个操作方法时启动。这个问题的重要部分是,当侦听器线程单独从主线程运行时,它永远不会打印出任何打印命令,并且它似乎不允许访问我定义的名为recvMessage的全局变量,该变量位于接口和实现部分。

In my code, I have it set up so that every time it runs through the main run loop, it updates a UILabel in my GUI. When the app is running, my label stays blank the whole time and never changes. I've double-checked the GUI and everything is linked up there correctly and my label is instantiated correctly too (I use the name "label" as an instance of UILabel in the code below). Does anyone have any ideas why my label is updating? The networking aspect of things is fine I believe, because I just got that done and everything is "talking" ok. Maybe it's a variable scope issue that I don't know about, or are separate threads allowed to access global variables, such as the one I have used below (rcvMessage)? I'm fairly new to multi-threaded applications but I don't believe it's really that hard to implement, using NSThread (only one line of code).

在我的代码中,我进行了设置,以便每次运行主运行循环时,它都会更新GUI中的UILabel。当应用程序运行时,我的标签始终保持空白并且永远不会更改。我已经仔细检查了GUI,所有内容都正确地链接在那里,我的标签也正确实例化(我在下面的代码中使用名称“label”作为UILabel的实例)。有没有人有任何想法为什么我的标签正在更新?事情的网络方面很好,我相信,因为我完成了这一切,一切都“说”好。也许这是一个我不知道的变量范围问题,或者是允许访问全局变量的单独线程,例如我在下面使用的那个(rcvMessage)?我对多线程应用程序相当新,但我不相信使用NSThread(只有一行代码)实现起来真的很难。

Global Variable

NSString *recvMessage;

Main Runloop - the section that updates the label every time it goes through the runloop

Main Runloop - 每次通过runloop时更新标签的部分

if (label.text != recvMessage)
    label.text = recvMessage

Talker Method

-(void)broadcastMessage { // (NSString*)msg {
    msg = @"From_Master";

    NSLog(@"broadcastMessage - Stage 1");
    mc_ttl = 15; // number of node hops the message is allowed to travel across the network
    // define the port we will be using
    mc_port = MYPORT;

    // create a socket for sending to the multicast address
    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        NSLog(@"ERROR: broadcastMessage - socket() failed");
        return;
    }

    memset(&mc_addr, 0, sizeof(mc_addr));
    mc_addr.sin_family      = AF_INET;
    mc_addr.sin_addr.s_addr = inet_addr(GROUP_ADDRESS);
    mc_addr.sin_port        = htons(MYPORT);

    NSLog(@"broadcastMessage - Stage 2");

// set the TTL (time to live/hop count) for the send
    if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &mc_ttl, sizeof(mc_ttl))) < 0) {
        NSLog(@"ERROR: broadcastMessage - setsockopt() failed");
        return;
    }   

    NSLog(@"broadcastMessage - Stage 3");

    // clear send buffer
    memset(send_str, 0, sizeof(send_str));

    // convert the message to a C string to send
    [msg getCString:send_str maxLength:MAX_LEN encoding:NSASCIIStringEncoding];

    //while (fgets(send_str, MAX_LEN, stdin)) {
    NSLog(@"broadcastMessage - Stage 4");
    NSLog(@"Message =");
    printf(send_str);

    // send string to multicast address
    if ((sendto(sock, send_str, sizeof(send_str), 0, (struct sockaddr *)&mc_addr, sizeof(mc_addr))) < 0) {
        NSLog(@"ERROR: broadcastMessage - sendto() sent incorrect number of bytes");
        //return;
    }
    NSLog(@"Sent Message -");
    printf(send_str);
    NSLog(@"broadcastMessage - Stage 5");

    // clear send buffer
    memset(send_str, 0, sizeof(send_str));
    NSLog(@"broadcastMessage - Stage 6 - Complete");
    close(sock);
}

Listener Method

-(void)listenForPackets {
    listeningFlag_on = 1; // allows reuse of the same socket

    NSLog(@"listenForPackets - Stage 1");
    if ((listeningSock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP)) < 0) {
        NSLog(@"ERROR: listenForPackets - socket() failed");
        return;
            // make the method return an int instead of void and use this statement to check for errors
    }

    NSLog(@"listenForPackets - Stage 2");

    // set reuse port to on to allow multiple binds per host
    if ((setsockopt(listeningSock, SOL_SOCKET, SO_REUSEADDR, &listeningFlag_on, sizeof(listeningFlag_on))) < 0) {
        NSLog(@"ERROR: listenForPackets - setsockopt() Reuse failed");
        return;
            // make the method return an int instead of void and use this statement to check for errors
    }

    // construct a multicast address structure after erasing anything in the listeningmc_addr structure
    memset(&listeningmc_addr, 0, sizeof(listeningmc_addr));
    listeningmc_addr.sin_family      = AF_INET;
    listeningmc_addr.sin_addr.s_addr = htonl(INADDR_ANY); // different from sender
    listeningmc_addr.sin_port        = htons(MYPORT);

    // bind multicast address to socket
    if ((bind(listeningSock, (struct sockaddr *)&listeningmc_addr, sizeof(listeningmc_addr))) < 0) {
        NSLog(@"ERROR: listenForPackets - bind() failed");
        perror("Bind() -");
        return;                         // make the method return an int instead of void and use this statement to check for errors
    }

    //*********************************************************************************

    NSString *ipAddress = [[NSString alloc] initWithString:self.getIPAddress];
    const char *tmp = [ipAddress UTF8String];
    listeningMc_addr_str = tmp;

    printf("%s\n", listeningMc_addr_str);

    listeningMc_req.imr_multiaddr.s_addr = inet_addr(GROUP_ADDRESS);
    listeningMc_req.imr_interface.s_addr = htonl(INADDR_ANY);
    // send an ADD MEMBERSHIP message via setsockopt
    if ((setsockopt(listeningSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &listeningMc_req, sizeof(listeningMc_req))) < 0) {
        NSLog(@"ERROR: listenForPackets - setsockopt() failed");
        int err = errno;
        NSLog(@"errno - %i", err);
        NSLog(@"Error = %s", strerror(err));
        perror("ERROR");
        return;                         // make the method return an int instead of void and use this statement to check for errors
    }

    NSLog(@"listenForPackets - Stage 3");
    for (;;) {          // loop forever

        // clear the receive buffers & structs
        memset(listeningRecv_str, 0, sizeof(listeningRecv_str));
        listeningFrom_len = sizeof(listeningFrom_addr);
        memset(&listeningFrom_addr, 0, listeningFrom_len);

        NSLog(@"Test #1 Complete");
        //msgStatus.text = @"Waiting...";

        // block waiting to receive a packet
        listeningFrom_len = sizeof(listeningmc_addr);
        if ((listeningRecv_len = recvfrom(listeningSock, listeningRecv_str, MAX_LEN, 0, (struct sockaddr*)&listeningmc_addr, &listeningFrom_len)) < 0) {
            NSLog(@"ERROR: listenForPackets - recvfrom() failed");
            return;                     // make the method return an int instead of void and use this statement to check for errors
        }
        NSLog(@"Test #2 Complete - Received a Message =");
        NSLog(@"listenForPackets - Stage 4");

        // listeningRecv_str
    **tmpString = [[NSString alloc] initWithCString:listeningRecv_str encoding:NSASCIIStringEncoding];
            NSLog(@"Message Received =");
            NSLog(tmpString);
            recvMessage = tmpString;**
        //}
        // received string
        printf("Received %d bytes from %s: ", listeningRecv_len, inet_ntoa(listeningFrom_addr.sin_addr));
        printf("%s", listeningRecv_str);
        //}
    }
    // send a DROP MEMBERSHIP message via setsockopt
    if ((setsockopt(listeningSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &listeningMc_req, sizeof(listeningMc_req))) < 0) {
        NSLog(@"ERROR: listenForPackets - setsockopt() drop membership failed");
        //return 1;                         // make the method return an int instead of void and use this statement to check for errors
    }
    close(listeningSock);
    NSLog(@"listenForPackets - Stage 5 - Complete");
}

2 个解决方案

#1


Yes, all threads can access global variables. There are certainly problems with how you are using the global--you leak the NSString you create every time the variable is updated, and you are accessing the same memory from two threads without any access control--but there's nothing that would prevent the variable from being updated.

是的,所有线程都可以访问全局变量。您使用全局的方式肯定存在问题 - 每次更新变量时都会泄漏您创建的NSString,并且您从两个线程访问相同的内存而没有任何访问控制 - 但没有什么可以阻止变量从更新。

If none of your log messages are being printed, the problem is that the code is never being run, which is why the variable isn't changing. You should take a look at the code that is supposed to kick off this new thread.

如果没有打印任何日志消息,问题是代码永远不会运行,这就是变量没有改变的原因。你应该看看应该启动这个新线程的代码。

#2


Also note that updating any UI components, you need to use the method "performSelectorOnMainThread" to do any value setting of label text or any other GUI elements. The value will not update from background threads.

另请注意,更新任何UI组件,您需要使用方法“performSelectorOnMainThread”来执行标签文本或任何其他GUI元素的任何值设置。该值不会从后台线程更新。

#1


Yes, all threads can access global variables. There are certainly problems with how you are using the global--you leak the NSString you create every time the variable is updated, and you are accessing the same memory from two threads without any access control--but there's nothing that would prevent the variable from being updated.

是的,所有线程都可以访问全局变量。您使用全局的方式肯定存在问题 - 每次更新变量时都会泄漏您创建的NSString,并且您从两个线程访问相同的内存而没有任何访问控制 - 但没有什么可以阻止变量从更新。

If none of your log messages are being printed, the problem is that the code is never being run, which is why the variable isn't changing. You should take a look at the code that is supposed to kick off this new thread.

如果没有打印任何日志消息,问题是代码永远不会运行,这就是变量没有改变的原因。你应该看看应该启动这个新线程的代码。

#2


Also note that updating any UI components, you need to use the method "performSelectorOnMainThread" to do any value setting of label text or any other GUI elements. The value will not update from background threads.

另请注意,更新任何UI组件,您需要使用方法“performSelectorOnMainThread”来执行标签文本或任何其他GUI元素的任何值设置。该值不会从后台线程更新。