(这是一项正在进行的工作。我想知道是否有人能改进它)
在目标C中,使用NSHost很容易解析主机名。
[[NSHost hostWithName:@"www.google.com"] address]可悲的是,iOS (iPhone)只包含NSHost的一个私有版本。
我在其他对象或方法中找到了许多方法来实现这一点,但是所有这些方法都只在结果中获得了IPv4地址。所以现在我发现的唯一有效的方法就是。
我第一次尝试使用异步CFHostStartInfoResolution,就像布杜纳根一样,但未能使其适应IPv6。
你们中的一些人会感激得到一个方法的工作,所以这里有一个,但如果你们知道一种方式将是异步的,我会很感激了解它。因为目前,我使用弹出窗口来警告下一次由于缓慢的细胞连接可能发生的冻结。
/**
Give the IPs corresponding to a Hostname
Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached
@author Christian Gonzalvez, http://wiki.gonzofamily.com
@param hostName A hostname
@return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
const char* hostnameC = [hostname UTF8String];
struct addrinfo hints, *res;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
int retval;
char buf[64];
NSMutableArray *result; //the array which will be return
NSMutableArray *result4; //the array of IPv4, to order them at the end
NSString *previousIP = nil;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;//AF_INET6;
hints.ai_flags = AI_CANONNAME;
//AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST
//AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED
retval = getaddrinfo(hostnameC, NULL, &hints, &res);
if (retval == 0)
{
if (res->ai_canonname)
{
result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]];
}
else
{
//it means the DNS didn't know this host
return nil;
}
result4= [NSMutableArray array];
while (res) {
switch (res->ai_family){
case AF_INET6:
s6 = (struct sockaddr_in6 *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v6!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
case AF_INET:
s4 = (struct sockaddr_in *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v4!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result4 addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
default:
NSLog(@"Neither IPv4 nor IPv6!");
}
//surprisingly every address is in double, let's add this test
previousIP = [NSString stringWithUTF8String:buf];
res = res->ai_next;
}
}else{
NSLog(@"no IP found");
return nil;
}
return [result arrayByAddingObjectsFromArray:result4];
}注意:我注意到大多数情况下只返回一个IPv6,我怀疑这是由于本地缓存时iOS内存优化所致。如果您一次又一次地运行此方法,有时您有3 IPv6,但是只有1 IPv4。
发布于 2011-05-20 18:41:47
如果希望在后台线程上运行方法,最简单的方法是使用performSelectorInBackground:withObject:;这是NSObject的实例方法,因此任何对象都可以使用它而不需要任何额外的工作(有趣的是,包括类对象,这在本例中是很好的,因为这是一个类方法):
[[self class] performSelectorInBackground:@selector(addressesForHostName:)
withObject:theHostName];在该方法中,您需要为线程设置一个自动释放池。您还需要设置某种回调方法来将返回值返回到主线程。确保不尝试在后台线程上执行任何GUI活动。在主线程上这样做是安全的。
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Do your stuff...
// Wait until done to allow memory to be managed properly
// If we did not do this, the array might be deallocated
// before the main thread had a chance to retain it
[self performSelectorOnMainThread:@selector(addressesCallback:)
withObject:[result arrayByAddingObjectsFromArray:result4]
waitUntilDone:YES];
// Inside a class method, self refers to the class object.
[pool drain];
}如果您一开始没有在主线程上,或者如果您需要更多的控制,您也可以查看NSOperation,它更强大,因此需要更多的工作。不过,它仍然比显式线程管理更容易!
希望这能解决你的问题。听起来就像让这个方法做你需要的,你只需要它不阻塞主线程。
发布于 2011-05-23 02:15:54
多亏了乔什,我才能做到,但以下是我必须做的:
而不是直接调用
self.ipAddressesString = [CJGIpAddress addressesForHostname:@"www.google.com"];我打电话
[self resolveNow:@"www.google.com"];并创建3种新方法:
- (void)resolveNow:(NSString *)hostname
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self performSelectorInBackground:@selector(hostname2ipAddresses:)
withObject:hostname];
}
- (void)hostname2ipAddresses:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//Here is my previous lonely line !! safely started in an other thread
self.ipAddressesString = [CJGIpAddress addressesForHostname:hostname];
[self performSelectorOnMainThread:@selector(resolutionDidFinish)
withObject:nil
waitUntilDone:YES];
[pool drain];
}
- (void)resolutionDidFinish
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
//My STUFF with self.ipAddressesString (now filled)
}编辑:在实践中,我在一个模型中使用了所有这些,所以当我在解析结束之前关闭视图时,我就崩溃了
因此,在视图中,我添加了dealloc,这是避免崩溃的必要条件。
- (void)dealloc
{
self.model.delegate = nil;
[super dealloc];
}然后-在模型中-我在做任何事情之前测试委托。
https://stackoverflow.com/questions/6068037
复制相似问题