DDXML.h
7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* Welcome to KissXML.
*
* The project page has documentation if you have questions.
* https://github.com/robbiehanson/KissXML
*
* If you're new to the project you may wish to read the "Getting Started" wiki.
* https://github.com/robbiehanson/KissXML/wiki/GettingStarted
*
* KissXML provides a drop-in replacement for Apple's NSXML class cluster.
* The goal is to get the exact same behavior as the NSXML classes.
*
* For API Reference, see Apple's excellent documentation,
* either via Xcode's Mac OS X documentation, or via the web:
*
* https://github.com/robbiehanson/KissXML/wiki/Reference
**/
#import "DDXMLNode.h"
#import "DDXMLElement.h"
#import "DDXMLDocument.h"
#if TARGET_OS_IPHONE
// Since KissXML is a drop in replacement for NSXML,
// it may be desireable (when writing cross-platform code to be used on both Mac OS X and iOS)
// to use the NSXML prefixes instead of the DDXML prefix.
//
// This way, on Mac OS X it uses NSXML, and on iOS it uses KissXML.
#ifndef NSXMLNode
#define NSXMLNode DDXMLNode
#endif
#ifndef NSXMLElement
#define NSXMLElement DDXMLElement
#endif
#ifndef NSXMLDocument
#define NSXMLDocument DDXMLDocument
#endif
#ifndef NSXMLInvalidKind
#define NSXMLInvalidKind DDXMLInvalidKind
#endif
#ifndef NSXMLDocumentKind
#define NSXMLDocumentKind DDXMLDocumentKind
#endif
#ifndef NSXMLElementKind
#define NSXMLElementKind DDXMLElementKind
#endif
#ifndef NSXMLAttributeKind
#define NSXMLAttributeKind DDXMLAttributeKind
#endif
#ifndef NSXMLNamespaceKind
#define NSXMLNamespaceKind DDXMLNamespaceKind
#endif
#ifndef NSXMLProcessingInstructionKind
#define NSXMLProcessingInstructionKind DDXMLProcessingInstructionKind
#endif
#ifndef NSXMLCommentKind
#define NSXMLCommentKind DDXMLCommentKind
#endif
#ifndef NSXMLTextKind
#define NSXMLTextKind DDXMLTextKind
#endif
#ifndef NSXMLDTDKind
#define NSXMLDTDKind DDXMLDTDKind
#endif
#ifndef NSXMLEntityDeclarationKind
#define NSXMLEntityDeclarationKind DDXMLEntityDeclarationKind
#endif
#ifndef NSXMLAttributeDeclarationKind
#define NSXMLAttributeDeclarationKind DDXMLAttributeDeclarationKind
#endif
#ifndef NSXMLElementDeclarationKind
#define NSXMLElementDeclarationKind DDXMLElementDeclarationKind
#endif
#ifndef NSXMLNotationDeclarationKind
#define NSXMLNotationDeclarationKind DDXMLNotationDeclarationKind
#endif
#ifndef NSXMLNodeOptionsNone
#define NSXMLNodeOptionsNone DDXMLNodeOptionsNone
#endif
#ifndef NSXMLNodeExpandEmptyElement
#define NSXMLNodeExpandEmptyElement DDXMLNodeExpandEmptyElement
#endif
#ifndef NSXMLNodeCompactEmptyElement
#define NSXMLNodeCompactEmptyElement DDXMLNodeCompactEmptyElement
#endif
#ifndef NSXMLNodePrettyPrint
#define NSXMLNodePrettyPrint DDXMLNodePrettyPrint
#endif
#endif // #if TARGET_OS_IPHONE
// KissXML has rather straight-forward memory management:
// https://github.com/robbiehanson/KissXML/wiki/MemoryManagementThreadSafety
//
// There are 3 important concepts to keep in mind when working with KissXML:
//
//
// 1.) KissXML provides a light-weight wrapper around libxml.
//
// The parsing, creation, storage, etc of the xml tree is all done via libxml.
// This is a fast low-level C library that's been around for ages, and comes pre-installed on Mac OS X and iOS.
// KissXML provides an easy-to-use Objective-C library atop libxml.
// So a DDXMLNode, DDXMLElement, or DDXMLDocument are simply objective-c objects
// with pointers to the underlying libxml C structure.
// Then only time you need to be aware of any of this is when it comes to equality.
// In order to maximize speed and provide read-access thread-safety,
// the library may create multiple DDXML wrapper objects that point to the same underlying xml node.
// So don't assume you can test for equality with "==".
// Instead use the isEqual method (as you should generally do with objects anyway).
//
//
// 2.) XML is implicitly a tree heirarchy, and the XML API's are designed to allow traversal up & down the tree.
//
// The tree heirarchy and API contract have an implicit impact concerning memory management.
//
// <starbucks>
// <latte/>
// </starbucks>
//
// Imagine you have a DDXMLNode corresponding to the starbucks node,
// and you have a DDXMLNode corresponding to the latte node.
// Now imagine you release the starbucks node, but you retain a reference to the latte node.
// What happens?
// Well the latte node is a part of the xml tree heirarchy.
// So if the latte node is still around, the xml tree heirarchy must stick around as well.
// So even though the DDXMLNode corresponding to the starbucks node may get deallocated,
// the underlying xml tree structure won't be freed until the latte node gets dealloacated.
//
// In general, this means that KissXML remains thread-safe when reading and processing a tree.
// If you traverse a tree and fork off asynchronous tasks to process subnodes,
// the tree will remain properly in place until all your asynchronous tasks have completed.
// In other words, it just works.
//
// However, if you parse a huge document into memory, and retain a single node from the giant xml tree...
// Well you should see the problem this creates.
// Instead, in this situation, copy or detach the node if you want to keep it around.
// Or just extract the info you need from it.
//
//
// 3.) KissXML is read-access thread-safe, but write-access thread-unsafe (designed for speed).
//
// <starbucks>
// <latte/>
// </starbucks>
//
// Imagine you have a DDXMLNode corresponding to the starbucks node,
// and you have a DDXMLNode corresponding to the latte node.
// What happens if you invoke [starbucks removeChildAtIndex:0]?
// Well the undelying xml tree will remove the latte node, and release the associated memory.
// And what if you still have a reference to the DDXMLNode that corresponds to the latte node?
// Well the short answer is that you shouldn't use it. At all.
// This is pretty obvious when you think about it from the context of this simple example.
// But in the real world, you might have multiple threads running in parallel,
// and you might accidently modify a node while another thread is processing it.
//
// To completely fix this problem, and provide write-access thread-safety, would require extensive overhead.
// This overhead is completely unwanted in the majority of cases.
// Most XML usage patterns are heavily read-only.
// And in the case of xml creation or modification, it is generally done on the same thread.
// Thus the KissXML library is write-access thread-unsafe, but provides speedier performance.
//
// However, when such a bug does creep up, it produces horrible side-effects.
// Essentially the pointer to the underlying xml structure becomes a dangling pointer,
// which means that accessing the dangling pointer might give you the correct results, or completely random results.
// And attempting to make modifications to non-existant xml nodes via the dangling pointer might do nothing,
// or completely corrupt your heap and cause un-explainable crashes in random parts of your library.
// Heap corruption is one of the worst problems to track down.
// So to help out, the library provides a debugging macro to track down these problems.
// That is, if you invalidate the write-access thread-unsafe rule,
// this macro will tell you when you're trying to access a now-dangling pointer.
//
// How does it work?
// Well everytime a DDXML wrapper object is created atop a libxml structure,
// it marks the linkage in a table.
// And everytime a libxml structure is freed, it destorys all corresponding linkages in the table.
// So everytime a DDXML wrapper objects is about to dereference it's pointer,
// it first ensures the linkage still exists in the table.
//
// Set to 1 to enable
// Set to 0 to disable (this is the default)
//
// The debugging macro adds a significant amount of overhead, and should NOT be enabled on production builds.
#if DEBUG
#define DDXML_DEBUG_MEMORY_ISSUES 0
#else
#define DDXML_DEBUG_MEMORY_ISSUES 0 // Don't change me!
#endif