YYTextArchiver.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. //
  2. // YYTextArchiver.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/3/16.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYTextArchiver.h"
  12. #import "YYTextRunDelegate.h"
  13. #import "YYTextRubyAnnotation.h"
  14. #import "UIDevice+YYAdd.h"
  15. /**
  16. When call CTRunDelegateGetTypeID() on some devices (runs iOS6), I got the error:
  17. "dyld: lazy symbol binding failed: Symbol not found: _CTRunDelegateGetTypeID"
  18. Here's a workaround for this issue.
  19. */
  20. static CFTypeID CTRunDelegateTypeID() {
  21. static CFTypeID typeID;
  22. static dispatch_once_t onceToken;
  23. dispatch_once(&onceToken, ^{
  24. /*
  25. if ((long)CTRunDelegateGetTypeID + 1 > 1) { //avoid compiler optimization
  26. typeID = CTRunDelegateGetTypeID();
  27. }
  28. */
  29. YYTextRunDelegate *delegate = [YYTextRunDelegate new];
  30. CTRunDelegateRef ref = delegate.CTRunDelegate;
  31. typeID = CFGetTypeID(ref);
  32. CFRelease(ref);
  33. });
  34. return typeID;
  35. }
  36. static CFTypeID CTRubyAnnotationTypeID() {
  37. static CFTypeID typeID;
  38. static dispatch_once_t onceToken;
  39. dispatch_once(&onceToken, ^{
  40. if ((long)CTRubyAnnotationGetTypeID + 1 > 1) { //avoid compiler optimization
  41. typeID = CTRunDelegateGetTypeID();
  42. } else {
  43. typeID = kCFNotFound;
  44. }
  45. });
  46. return typeID;
  47. }
  48. /**
  49. A wrapper for CGColorRef. Used for Archive/Unarchive/Copy.
  50. */
  51. @interface _YYCGColor : NSObject <NSCopying, NSCoding>
  52. @property (nonatomic, assign) CGColorRef CGColor;
  53. + (instancetype)colorWithCGColor:(CGColorRef)CGColor;
  54. @end
  55. @implementation _YYCGColor
  56. + (instancetype)colorWithCGColor:(CGColorRef)CGColor {
  57. _YYCGColor *color = [self new];
  58. color.CGColor = CGColor;
  59. return color;
  60. }
  61. - (void)setCGColor:(CGColorRef)CGColor {
  62. if (_CGColor != CGColor) {
  63. if (CGColor) CGColor = (CGColorRef)CFRetain(CGColor);
  64. if (_CGColor) CFRelease(_CGColor);
  65. _CGColor = CGColor;
  66. }
  67. }
  68. - (void)dealloc {
  69. if (_CGColor) CFRelease(_CGColor);
  70. _CGColor = NULL;
  71. }
  72. - (id)copyWithZone:(NSZone *)zone {
  73. _YYCGColor *color = [self.class new];
  74. color.CGColor = self.CGColor;
  75. return color;
  76. }
  77. - (void)encodeWithCoder:(NSCoder *)aCoder {
  78. UIColor *color = [UIColor colorWithCGColor:_CGColor];
  79. [aCoder encodeObject:color forKey:@"color"];
  80. }
  81. - (id)initWithCoder:(NSCoder *)aDecoder {
  82. self = [self init];
  83. UIColor *color = [aDecoder decodeObjectForKey:@"color"];
  84. self.CGColor = color.CGColor;
  85. return self;
  86. }
  87. @end
  88. /**
  89. A wrapper for CGImageRef. Used for Archive/Unarchive/Copy.
  90. */
  91. @interface _YYCGImage : NSObject <NSCoding, NSCopying>
  92. @property (nonatomic, assign) CGImageRef CGImage;
  93. + (instancetype)imageWithCGImage:(CGImageRef)CGImage;
  94. @end
  95. @implementation _YYCGImage
  96. + (instancetype)imageWithCGImage:(CGImageRef)CGImage {
  97. _YYCGImage *image = [self new];
  98. image.CGImage = CGImage;
  99. return image;
  100. }
  101. - (void)setCGImage:(CGImageRef)CGImage {
  102. if (_CGImage != CGImage) {
  103. if (CGImage) CGImage = (CGImageRef)CFRetain(CGImage);
  104. if (_CGImage) CFRelease(_CGImage);
  105. _CGImage = CGImage;
  106. }
  107. }
  108. - (void)dealloc {
  109. if (_CGImage) CFRelease(_CGImage);
  110. }
  111. - (id)copyWithZone:(NSZone *)zone {
  112. _YYCGImage *image = [self.class new];
  113. image.CGImage = self.CGImage;
  114. return image;
  115. }
  116. - (void)encodeWithCoder:(NSCoder *)aCoder {
  117. UIImage *image = [UIImage imageWithCGImage:_CGImage];
  118. [aCoder encodeObject:image forKey:@"image"];
  119. }
  120. - (id)initWithCoder:(NSCoder *)aDecoder {
  121. self = [self init];
  122. UIImage *image = [aDecoder decodeObjectForKey:@"image"];
  123. self.CGImage = image.CGImage;
  124. return self;
  125. }
  126. @end
  127. @implementation YYTextArchiver
  128. + (NSData *)archivedDataWithRootObject:(id)rootObject {
  129. if (!rootObject) return nil;
  130. NSMutableData *data = [NSMutableData data];
  131. YYTextArchiver *archiver = [[[self class] alloc] initForWritingWithMutableData:data];
  132. [archiver encodeRootObject:rootObject];
  133. [archiver finishEncoding];
  134. return data;
  135. }
  136. + (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path {
  137. NSData *data = [self archivedDataWithRootObject:rootObject];
  138. if (!data) return NO;
  139. return [data writeToFile:path atomically:YES];
  140. }
  141. - (instancetype)init {
  142. self = [super init];
  143. self.delegate = self;
  144. return self;
  145. }
  146. - (instancetype)initForWritingWithMutableData:(NSMutableData *)data {
  147. self = [super initForWritingWithMutableData:data];
  148. self.delegate = self;
  149. return self;
  150. }
  151. - (id)archiver:(NSKeyedArchiver *)archiver willEncodeObject:(id)object {
  152. CFTypeID typeID = CFGetTypeID((CFTypeRef)object);
  153. if (typeID == CTRunDelegateTypeID()) {
  154. CTRunDelegateRef runDelegate = (__bridge CFTypeRef)(object);
  155. id ref = CTRunDelegateGetRefCon(runDelegate);
  156. if (ref) return ref;
  157. } else if (typeID == CTRubyAnnotationTypeID()) {
  158. CTRubyAnnotationRef ctRuby = (__bridge CFTypeRef)(object);
  159. YYTextRubyAnnotation *ruby = [YYTextRubyAnnotation rubyWithCTRubyRef:ctRuby];
  160. if (ruby) return ruby;
  161. } else if (typeID == CGColorGetTypeID()) {
  162. return [_YYCGColor colorWithCGColor:(CGColorRef)object];
  163. } else if (typeID == CGImageGetTypeID()) {
  164. return [_YYCGImage imageWithCGImage:(CGImageRef)object];
  165. }
  166. return object;
  167. }
  168. @end
  169. @implementation YYTextUnarchiver
  170. + (id)unarchiveObjectWithData:(NSData *)data {
  171. if (data.length == 0) return nil;
  172. YYTextUnarchiver *unarchiver = [[self alloc] initForReadingWithData:data];
  173. return [unarchiver decodeObject];
  174. }
  175. + (id)unarchiveObjectWithFile:(NSString *)path {
  176. NSData *data = [NSData dataWithContentsOfFile:path];
  177. return [self unarchiveObjectWithData:data];
  178. }
  179. - (instancetype)init {
  180. self = [super init];
  181. self.delegate = self;
  182. return self;
  183. }
  184. - (instancetype)initForReadingWithData:(NSData *)data {
  185. self = [super initForReadingWithData:data];
  186. self.delegate = self;
  187. return self;
  188. }
  189. - (id)unarchiver:(NSKeyedUnarchiver *)unarchiver didDecodeObject:(id) NS_RELEASES_ARGUMENT object NS_RETURNS_RETAINED {
  190. if ([object class] == [YYTextRunDelegate class]) {
  191. YYTextRunDelegate *runDelegate = object;
  192. CTRunDelegateRef ct = runDelegate.CTRunDelegate;
  193. id ctObj = (__bridge id)ct;
  194. if (ct) CFRelease(ct);
  195. return ctObj;
  196. } else if ([object class] == [YYTextRubyAnnotation class]) {
  197. YYTextRubyAnnotation *ruby = object;
  198. if (kiOS8Later) {
  199. CTRubyAnnotationRef ct = ruby.CTRubyAnnotation;
  200. id ctObj = (__bridge id)(ct);
  201. if (ct) CFRelease(ct);
  202. return ctObj;
  203. } else {
  204. return object;
  205. }
  206. } else if ([object class] == [_YYCGColor class]) {
  207. _YYCGColor *color = object;
  208. return (id)color.CGColor;
  209. } else if ([object class] == [_YYCGImage class]) {
  210. _YYCGImage *image = object;
  211. return (id)image.CGImage;
  212. }
  213. return object;
  214. }
  215. @end