2017-01-18 5 views
0

URLSessionDataTask를 사용하여 API에서 XML 파일을 다운로드하고 있습니다.
XML은 다음과 같습니다 Libxml2를 사용하여 XML을 구문 분석 할 때 RAM을 많이 사용합니다.

<?xml version="1.0" encoding="UTF-8" ?> 
<ResultList id="12345678-0" platforms="A;B;C;D;E"> 
    <Book id="1111111111" author="Author A" title="Title A" price="9.95" ... /> 
    <Book id="1111111112" author="Author B" title="Title B" price="2.00" ... /> 
    <Book id="1111111113" author="Author C" title="Title C" price="5.00" ... /> 
    <ResultInfo bookcount="3" /> 
</ResultList> 

는 때때로 XML은 수천 권의 책을 가질 수있다.
저는 Libxml2의 SAX 파서를 사용하여 XML을 구문 분석합니다. 구문 분석하는 동안 나는 객체 Book를 작성하고 그래서 같이 XML에서 값을 설정하십시오있는 UITableViewController에서

private func startElementSAX(_ ctx: UnsafeMutableRawPointer?, name: UnsafePointer<xmlChar>?, prefix: UnsafePointer<xmlChar>?, URI: UnsafePointer<xmlChar>?, nb_namespaces: CInt, namespaces: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) { 

    let elementName = String(cString: name!) 

    switch elementName { 
    case "Book": 
     let book = buildBook(nb_attributes: nb_attributes, attributes: attributes) 
     parser.delegate?.onBook(book: book) 
    default: 
     break 
    } 
} 

func buildBook(nb_attributes: CInt, attributes: UnsafeMutablePointer<UnsafePointer<xmlChar>?>?) -> Book { 
    let fields = 5 /* (localname/prefix/URI/value/end) */ 
    let book = Book() 
    for i in 0..<Int(nb_attributes) { 
     if let localname = attributes?[i * fields + 0], 
      //let prefix = attributes?[i * fields + 1], 
      //let URI = attributes?[i * fields + 2], 
      let value_start = attributes?[i * fields + 3]//, 
      /*let value_end = attributes?[i * fields + 4]*/ { 

       let localnameString = String(cString: localname) 
       let string_start = String(cString: value_start) 
       //let string_end = String(cString: value_end) 

       if let end = string_start.characters.index(of: "\"") { 
        let value = string_start.substring(to: end) 
        book.setValue(value, forKey: localnameString) 
       } else { 
        book.setValue(string_start, forKey: localnameString) 
       } 
     } 
    } 
    return book 
} 

onBook(book: Book) 대리자 메서드는 배열에 책 객체를 추가하고 jQuery과 업데이트합니다. 여태까지는 그런대로 잘됐다.

이제 문제는 장치의 RAM이 너무 많아서 장치가 느려집니다. XML에 ~ 500 권의 도서가 있으면 500MB 이상의 RAM이 필요합니다. 나는 이유를 모른다. 나는 악기의 RAM을 조회 할 때, 나는 범주 이벤트 역사에서

HeapBufferStorage entries KB

(100)보다 큰 여러 항목으로 _HeapBufferStorage<_StringBufferIVars, UInt16>

Instruments

에 할당 된 모든 메모리 참조가

을 나열 buildBook()하는 방법입니다

Event History

Foundation에서 XMLParser를 사용할 때 onstructor XMLParser(contentsOf: URL) 먼저 전체 XML을 다운로드 한 다음 구문 분석합니다. 정상적인 RAM 사용법을가집니다. 아무리 많은 책이 있더라도. 하지만 UITableView에 최대한 빨리 책을 보여주고 싶습니다. 나는 단지 iOS 용 안드로이드의 XMLPullParser와 같은 것을 원한다.

autoreleasepool { 
    xmlParseChunk(ctxt, data, Int32(read), 0) 
} 

당신이 만약이 호출을 변경

xmlParseChunk(ctxt, data, Int32(read), 0) 

상당히 사용되는 메모리의 양을 감소 :

+0

SAX 파서가 XML 데이터를 파싱하는 작은 메모리를 사용한다, 다른 파서를 시도할까요? – aircraft

+0

[XMLTextReader 인터페이스] (http : // xmlsoft.org/xmlreader.html)은 LibXML2에서 "후계자"이기 때문에. 하지만 XML 파일을 다운로드하는 동안 XML 파일을 사용할 수 있는지 알고 있습니까? – ElegyD

답변

0
내가 libxml2를 사용하고

같은 코드 (인해 문제 this에) 및이 위와 같이 Push Parser 호출을 사용하면 문제가 해결 될 가능성이 높습니다. 그렇지 않으면 autoreleasepool 호출에서 대리자 호출을 래핑하면 도움이 될 수 있습니다.

이유는 많은 중간 개체가 만들어지고 자동 해제 풀에 추가되어 해제되지 않기 때문입니다. 자세한 내용은 this 게시물을 참조하십시오.

다른 방법으로 코드를 변경하여 자동 복구 풀에 추가되는 객체 수를 줄이는 방법도 있습니다. 예를 들어 필자가 피할 수있는 부분에서 공백을 제거하여 여분의 문자열을 생성하는 것을 발견했습니다.

또한이 문제는 사용자의 문제와 관련이 없지만 특성의 시작과 끝은 문자열 길이를 알려주며이를 사용해야합니다. 예를 들어

:

let valStart = UnsafeMutableRawPointer(mutating: attributes! 
    .advanced(by: 3 + Int(i * 5)).pointee) 
let valEnd = UnsafeMutableRawPointer(mutating: attributes! 
    .advanced(by: 4 + Int(i * 5)).pointee) 
let valData = Data(bytesNoCopy: valStart!, count: valEnd! - valStart!, 
    deallocator: .none) 
let attrValue = String(data: valData, encoding: String.Encoding.utf8)