开发者

objC/C- combined: letting a C function wait till an objC-Delegate is finished

开发者 https://www.devze.com 2022-12-08 12:01 出处:网络
I\'m having a problem here: I have a C function: int my_connect(int sockfd, const struct sockaddr *serv_addr,

I'm having a problem here:

I have a C function:

int my_connect(int sockfd, const struct sockaddr *serv_addr,
      socklen_t addrlen) {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 alertViewDelegate = [FirewallRest alloc];
 [alertViewDelegate retain];
 //ALog(@"1");

 int error;
 //ALog(@"2");
 char hostname[NI_MAXHOST] = "";
 //ALog(@"3");

 error = getnameinfo(serv_addr, addrlen, hostname, NI_MAXHOST, NULL, 0, 0);
 //ALog(@"4");
 if (error !=0) {
  ALog(@"coudldn't resolve hostname or internal connect");
  [pool release];
  return orig__connect(sockfd, serv_addr, addrlen);
 }
 if (error == 0) {
  ALog(@"hostname: %s", hostname);
  NSString *hostFirst = [NSString stringWithCString:hostname];
  NSString *host = [hostFirst stringByReplacingOccurrencesOfString:@"www." withString:@""];

  NSString *msg = [@"tries to contact: " stringByAppendingString:host];
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"asdf"
              message:msg
                delegate:alertViewDelegate cancelButtonTitle:@"Never allow!"
             otherButtonTitles:@"1", @"2",@"3", nil];
  [alert show];
  [alert release];



  waitingForResponse = YES;

  while (waitingForResponse == YES) {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   ALog(@"running Loop1?");
   [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
   ALog(@"running Loop2?");
   [pool drain];
  }


  ALog(@"continues");

  return orig__connect(sockfd, serv_addr, addrlen);
...

It should wait till the UIAlertViewDelegate method (in it's own class) sets waitingForResponse == NO.

extern BOOL waitingForResponse;

@implementation FirewallRest

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
 //NSLog(@"buttonIndex: %@", buttonIndex);

 if (buttonIndex == 0){
  NSLog(@"0");
  waitingForResponse = NO;
 }

 if (buttonIndex == 1){
  NSLog(@"1");
  waitingForResponse = N开发者_运维技巧O;
 }
 if (buttonIndex == 2){
  NSLog(@"2");
  waitingForResponse = NO;
 }
 if (buttonIndex == 3){
  NSLog(@"3");
  waitingForResponse = NO;
 }


}

but somehow that doesn't work :/

does anyone have an idea? or a better way to do this? (I'm not very used to C and objC in one app ;))

Thanks in advance for any help


You should not do IO on the main thread, your need to try to handle a run-loop is evident. Run-loops are tricky, especially since many events do not make the runloop call exit as expected (Anything run on a timer for one, performing selectors just happens to be run on a timer). You could probably solve your problem by not waiting for distantFuture, but just for a few hundred milliseconds at a time.

What you really want to to do is top run my_connect() in a background thread, and just wait for a condition. This code will be much easier to handle, look like less of a hack, and actually perform better.

You can use an instance of NSConditionLock to implement the wait. It is not super obvious how to do this. But you can create a shared instance on NSConditionLock in the variable condLock like this:

condLock = [[NSConditionLock alloc] initWithCondition:0];

And this is then how you would implement the waiting in my_connect():

[condLock lockWhenCondition:1];
// Add code that needs to be thread safe here if you like.
[condLock unlockWithCondition:0];

And this is how you would signal the NSConditionLock that it is time to continue from you delegate method.

[condLock lock];
// More thread safe code here if you like
[condLock unlockWithCondition:1];

You could assign some constants to use instead of 0 and 1 explicitly, and have a quite nice solution.


This code

 alertViewDelegate = [FirewallRest alloc];
 [alertViewDelegate retain];

looks very weird. In particular, init is never called on alertViewDelegate. You might expect things to not behave appropriately if the instance isn't properly initialized. You also leak alertViewDelegate, as you have two owership references to it (from alloc and retain, but no balancing releases. At the very least, these lines should be

alertViewDelegate = [[FireallRest alloc] init];

...

[alertViewDelegate release]; //balancing release at end of scope


This looks a bit weird.. alertViewDelegate = [FirewallRest alloc]; [alertViewDelegate retain]; What is happening here?

Where is 'waitingForResponse' declared? You use the 'extern' keyword. You shouldn't if you don't know what it is doing.

You may need to post a bit more code, including header file.

But, assuming waitingForResponse is a property of alertViewDelegate use self. waitingForResponse when setting waitingForResponse's value and use alertViewDelegate. waitingForResponse when testing it.


I ended up using a different system with an UIActionSheet:

in my_connect() I do:

[alertViewDelegate performSelectorOnMainThread:@selector(createActionSheet) withObject:nil waitUntilDone:YES];

then in the alertViewDelegate I create the action sheet:

- (void) createActionSheet {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
whatToDo = 4;

UIActionSheet *actionSheet = [[UIActionSheet alloc]
                              initWithTitle:[@"this app tries to contact: \n " stringByAppendingString:host]
                              delegate:self
                              cancelButtonTitle:nil
                              destructiveButtonTitle:nil
                              otherButtonTitles:@"Always allow!", @"Always Disallow!", @"Allow all for this App",  nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;

[actionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
[actionSheet release];
waitUntilDone = YES;

while (waitUntilDone == YES) {

    NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
    //ALog(@"running Loop1?");
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
    //ALog(@"running Loop2?");
    [pool2 release];
}
[pool release];

}

so this method is waiting until the delegate's methods are finished and then my_connect() continues.

The UI is still very responsive and I didn't notice any slowdown of the apps.

0

精彩评论

暂无评论...
验证码 换一张
取 消