最近在开发iOS程序,调用WebService时候使用到了iOS内置的libxml2来作为DOM树的操作库,按照官方代码的参考,写了一段创建DOM的代码,部分片段如下:
xmlNodePtr generateSoap11MessageWrapper(void)
{
// create Document with Default version
const xmlDocPtr pDoc = xmlNewDoc(NULL);
{
// force version and encoding
pDoc->version = _XS"1.0";
pDoc->encoding = _XS"UTF-8";
}
// create Root-Element
const xmlNodePtr pRoot = xmlNewNode(NULL, _XS"Envelope");
{
// attach namespace definition to @pRoot->nsDef.
xmlNewNs(pRoot, _XS"http://www.w3.org/2001/XMLSchema-instance", _XS"xsi");
xmlNewNs(pRoot, _XS"http://www.w3.org/2001/XMLSchema", _XS"xsd");
const xmlNsPtr pNsSoap = xmlNewNs(pRoot, _XS"http://schemas.xmlsoap.org/soap/envelope/", _XS"soap");
// set the ns as the node namespace
xmlSetNs(pRoot, pNsSoap);
}
// set @pRoot as Document's root
xmlDocSetRootElement(pDoc, pRoot);
// assign Root-Element to a readable name (Envelope node)
const xmlNodePtr pEnvelope = pRoot;
// create no content Body node (inherit parent, @pEnvelope, namespace by using NULL in namespace)
const xmlNodePtr pBody = xmlNewChild(pEnvelope, NULL, _XS"Body", NULL);
// xmlFreeDoc(pDoc); // dont free the returning doc
// xmlMemoryDump(); // debug memory for regression tests
return pBody;
}
以上代码是参照libxml2官网的例子: http://xmlsoft.org/examples/tree2.c
+ (NSURLRequest*)generateSoap11Request:(WebServiceEnvelope*)envelope
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:envelope.serviceUrl];
const xmlNodePtr pBody= generateSoap11MessageWrapper();
{
// create soap action node.
const xmlNodePtr pAction = xmlNewChild(pBody, NULL, [envelope.action toXmlChar], NULL);
{
xmlNsPtr pNs = xmlNewNs(pAction, [envelope.namespace toXmlChar], NULL);
xmlSetNs(pAction, pNs);
}
// process parameters
for (NSString* key in [envelope.paramters keyEnumerator])
{
NSString* value = [envelope.paramters valueForKey:key];
xmlNewTextChild(pAction, NULL, [key toXmlChar], [value toXmlChar]);
}
}
NSString *soapAction = [NSString stringWithFormat:@"%@%@", envelope.namespace, envelope.action];
NSString* soapMessage = xmlDocToNSString(pBody->doc);
NSString* messageLength = [NSString stringWithFormat:@"%d", soapMessage.length];
[request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request addValue:messageLength forHTTPHeaderField:@"Content-Length"];
[request addValue:soapAction forHTTPHeaderField:@"SOAPAction"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody: [soapMessage dataUsingEncoding:NSUnicodeStringEncoding]];
// release doc
xmlFreeDoc(pBody->doc);
return request;
}
问题来了!
在4.3的模拟器上运行,没有什么特别情况,就是在调用generateSoap11Request时候,模拟器出现了
Application(768,0xa0c50540) malloc: *** error for object 0xb1e94: pointer being free was not allocated
*** set a breakpoint in malloc_error_break to debug
看没什么问题,程序能正常运行,就没管了。
结果。。。
上到iPad上跑一下(iPad是4.2)第一个界面请求数据就报错了!
光标停在释放xmlDoc的一行
xmlFreeDoc(pBody->doc);
断点是由一个SIGABRT引发的,错误信息和模拟器上的一样!
将“pointer being free was not allocated” Google一轮后都基本是说如何查看malloc_history的,而且在Xcode4.3上的(lldb)提示符上基本没用
最后使用出错的函数名及使用平台(“xmlFreeDoc iOS 4”)终于在Google上找到了一个maillist,里面清晰讲明了问题所在!
原文地址:http://mail.gnome.org/archives/xml/2010-October/msg00031.html
xmlFreeDoc does a xmlFree on doc->encoding, but you set it to a string
which was not dynamically allocated. In effect, you are calling
xmlFree on the "utf-8" string literal. This is wrong. You need to do
at least this:
doc->encoding = xmlStrDup("utf-8");
大意就是说:xmlFreeDoc会释放doc->encoding,如果初始化文档的时候使用类似doc->encoding=BAD_CAST "UTF-8"这种方式初始化的话就相当于释放一段栈内的内存(相当于C++中delete "literal"这样的操作),这样肯定会发生错误,所以嘛,我们必须动态申请一段内存,而这个情况libxml已经帮我们做了,使用xmlStrdup即可复制一段文字并且转换为xmlChar*类型,即:
doc->encoding = xmlStrDup("utf-8");
注:暂时来说,貌似只有xmlDoc内对象会存在这样的问题,而xmlNode内诸如名字内容这些,通过xmlNewChild新建的子节点均可以直接传入常量字符串,而在xmlFreeDoc时没有问题。