LNUtilities.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. import Foundation
  2. import UIKit
  3. import MetalKit
  4. import AVFoundation
  5. import ObjectiveC
  6. @objcMembers
  7. public final class LNVAPLogger: NSObject {
  8. public typealias LNLogHandler = (_ level: Int, _ file: String, _ line: Int, _ function: String, _ module: String, _ message: String) -> Void
  9. private static let lock = NSRecursiveLock()
  10. private static var handler: LNLogHandler?
  11. @objc(registerLogHandler:)
  12. public static func registerLogHandler(_ handler: @escaping LNLogHandler) {
  13. lock.lock()
  14. self.handler = handler
  15. lock.unlock()
  16. }
  17. @objc(clearLogHandler)
  18. public static func clearLogHandler() {
  19. lock.lock()
  20. handler = nil
  21. lock.unlock()
  22. }
  23. @objc(logLevel:file:line:func:module:message:)
  24. public static func log(level: Int, file: String, line: Int, func function: String, module: String, message: String) {
  25. let sanitized = message.replacingOccurrences(of: "%", with: "")
  26. lock.lock()
  27. let current = handler
  28. lock.unlock()
  29. if let current {
  30. current(level, file, line, function, module, sanitized)
  31. return
  32. }
  33. #if DEBUG
  34. let fileName = (file as NSString).lastPathComponent
  35. NSLog("<\(level)> \(fileName)(\(line)):\(function) [\(module)] - \(sanitized)")
  36. #endif
  37. }
  38. }
  39. @objcMembers
  40. public final class LNVAPSafeMutableArray: NSObject {
  41. private let lock = NSRecursiveLock()
  42. private var storage: NSMutableArray
  43. public override init() {
  44. self.storage = NSMutableArray()
  45. super.init()
  46. }
  47. @objc(initWithCapacity:)
  48. public init(capacity: Int) {
  49. self.storage = NSMutableArray(capacity: capacity)
  50. super.init()
  51. }
  52. @objc(initWithArray:)
  53. public init(array: [Any]) {
  54. self.storage = NSMutableArray(array: array)
  55. super.init()
  56. }
  57. @objc(count)
  58. public var count: Int {
  59. lock.lock()
  60. defer { lock.unlock() }
  61. return storage.count
  62. }
  63. @objc(objectAtIndex:)
  64. public func object(at index: Int) -> Any {
  65. lock.lock()
  66. defer { lock.unlock() }
  67. return storage[index]
  68. }
  69. @objc(firstObject)
  70. public var firstObject: Any? {
  71. lock.lock()
  72. defer { lock.unlock() }
  73. return storage.firstObject
  74. }
  75. @objc(lastObject)
  76. public var lastObject: Any? {
  77. lock.lock()
  78. defer { lock.unlock() }
  79. return storage.lastObject
  80. }
  81. @objc(addObject:)
  82. public func add(_ object: Any) {
  83. lock.lock()
  84. storage.add(object)
  85. lock.unlock()
  86. }
  87. @objc(insertObject:atIndex:)
  88. public func insert(_ object: Any, at index: Int) {
  89. lock.lock()
  90. storage.insert(object, at: index)
  91. lock.unlock()
  92. }
  93. @objc(removeObjectAtIndex:)
  94. public func removeObject(at index: Int) {
  95. lock.lock()
  96. storage.removeObject(at: index)
  97. lock.unlock()
  98. }
  99. @objc(removeLastObject)
  100. public func removeLastObject() {
  101. lock.lock()
  102. storage.removeLastObject()
  103. lock.unlock()
  104. }
  105. @objc(removeAllObjects)
  106. public func removeAllObjects() {
  107. lock.lock()
  108. storage.removeAllObjects()
  109. lock.unlock()
  110. }
  111. @objc(containsObject:)
  112. public func contains(_ object: Any) -> Bool {
  113. lock.lock()
  114. defer { lock.unlock() }
  115. return storage.contains(object)
  116. }
  117. @objc(allObjects)
  118. public func allObjects() -> [Any] {
  119. lock.lock()
  120. defer { lock.unlock() }
  121. return storage.copy() as? [Any] ?? []
  122. }
  123. }
  124. @objcMembers
  125. public final class LNVAPSafeMutableDictionary: NSObject {
  126. private let lock = NSRecursiveLock()
  127. private var storage: NSMutableDictionary
  128. public override init() {
  129. self.storage = NSMutableDictionary()
  130. super.init()
  131. }
  132. @objc(initWithCapacity:)
  133. public init(capacity: Int) {
  134. self.storage = NSMutableDictionary(capacity: capacity)
  135. super.init()
  136. }
  137. @objc(initWithDictionary:)
  138. public init(dictionary: [AnyHashable: Any]) {
  139. self.storage = NSMutableDictionary(dictionary: dictionary)
  140. super.init()
  141. }
  142. @objc(count)
  143. public var count: Int {
  144. lock.lock()
  145. defer { lock.unlock() }
  146. return storage.count
  147. }
  148. @objc(objectForKey:)
  149. public func object(forKey key: NSCopying) -> Any? {
  150. lock.lock()
  151. defer { lock.unlock() }
  152. return storage.object(forKey: key)
  153. }
  154. @objc(setObject:forKey:)
  155. public func setObject(_ object: Any, forKey key: NSCopying) {
  156. lock.lock()
  157. storage.setObject(object, forKey: key)
  158. lock.unlock()
  159. }
  160. @objc(removeObjectForKey:)
  161. public func removeObject(forKey key: NSCopying) {
  162. lock.lock()
  163. storage.removeObject(forKey: key)
  164. lock.unlock()
  165. }
  166. @objc(removeAllObjects)
  167. public func removeAllObjects() {
  168. lock.lock()
  169. storage.removeAllObjects()
  170. lock.unlock()
  171. }
  172. @objc(allKeys)
  173. public var allKeys: [Any] {
  174. lock.lock()
  175. defer { lock.unlock() }
  176. return storage.allKeys
  177. }
  178. @objc(allValues)
  179. public var allValues: [Any] {
  180. lock.lock()
  181. defer { lock.unlock() }
  182. return storage.allValues
  183. }
  184. @objc(dictionaryRepresentation)
  185. public func dictionaryRepresentation() -> [AnyHashable: Any] {
  186. lock.lock()
  187. defer { lock.unlock() }
  188. return storage.copy() as? [AnyHashable: Any] ?? [:]
  189. }
  190. }
  191. @objcMembers
  192. public final class LNVAPWeakProxy: NSObject {
  193. private weak var target: NSObjectProtocol?
  194. @objc(initWithTarget:)
  195. public init(target: NSObjectProtocol?) {
  196. self.target = target
  197. super.init()
  198. }
  199. @objc(proxyWithTarget:)
  200. public static func proxy(with target: NSObjectProtocol?) -> LNVAPWeakProxy {
  201. LNVAPWeakProxy(target: target)
  202. }
  203. public override func forwardingTarget(for aSelector: Selector!) -> Any? {
  204. target
  205. }
  206. public override func responds(to aSelector: Selector!) -> Bool {
  207. target?.responds(to: aSelector) ?? false
  208. }
  209. }
  210. @objcMembers
  211. public final class LNVAPMetalShaderFunctionLoader: NSObject {
  212. private let device: MTLDevice
  213. private var defaultLibrary: MTLLibrary?
  214. @objc(initWithDevice:)
  215. public init(device: MTLDevice) {
  216. self.device = device
  217. super.init()
  218. }
  219. @objc(loadFunctionWithName:)
  220. public func loadFunction(withName functionName: String) -> MTLFunction? {
  221. if defaultLibrary == nil {
  222. if let path = Bundle(for: Self.self).path(forResource: "default", ofType: "metallib") {
  223. defaultLibrary = try? device.makeLibrary(filepath: path)
  224. }
  225. if defaultLibrary == nil {
  226. defaultLibrary = device.makeDefaultLibrary()
  227. }
  228. }
  229. return defaultLibrary?.makeFunction(name: functionName)
  230. }
  231. }
  232. @objcMembers
  233. public final class LNVAPMetalUtil: NSObject {
  234. public static let vapAttachmentVertexFunctionName = "vapAttachment_vertexShader"
  235. public static let vapAttachmentFragmentFunctionName = "vapAttachment_FragmentShader"
  236. public static let vapVertexFunctionName = "vap_vertexShader"
  237. public static let vapYUVFragmentFunctionName = "vap_yuvFragmentShader"
  238. public static let vapMaskFragmentFunctionName = "vap_maskFragmentShader"
  239. public static let vapMaskBlurFragmentFunctionName = "vap_maskBlurFragmentShader"
  240. public static let vapMTLVerticesIdentity: [Float] = [-1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0]
  241. public static let vapMTLTextureCoordinatesIdentity: [Float] = [0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]
  242. public static let vapMTLTextureCoordinatesFor90: [Float] = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]
  243. @objc(genVerticesWithRect:containerSize:reverse:)
  244. public static func genVertices(rect: CGRect, containerSize: CGSize, reverse: Bool) -> [Float] {
  245. guard containerSize.width > 0, containerSize.height > 0 else { return vapMTLVerticesIdentity }
  246. let originX = Float(-1 + 2 * rect.origin.x / containerSize.width)
  247. let originY = Float(1 - 2 * rect.origin.y / containerSize.height)
  248. let width = Float(2 * rect.size.width / containerSize.width)
  249. let height = Float(2 * rect.size.height / containerSize.height)
  250. if reverse {
  251. return [originX, originY - height, 0.0, 1.0,
  252. originX, originY, 0.0, 1.0,
  253. originX + width, originY - height, 0.0, 1.0,
  254. originX + width, originY, 0.0, 1.0]
  255. }
  256. return [originX, originY, 0.0, 1.0,
  257. originX, originY - height, 0.0, 1.0,
  258. originX + width, originY, 0.0, 1.0,
  259. originX + width, originY - height, 0.0, 1.0]
  260. }
  261. @objc(genTextureCoordinatesWithRect:containerSize:reverse:degree:)
  262. public static func genTextureCoordinates(rect: CGRect, containerSize: CGSize, reverse: Bool, degree: Int) -> [Float] {
  263. _ = degree
  264. guard containerSize.width > 0, containerSize.height > 0 else { return vapMTLTextureCoordinatesIdentity }
  265. let originX = Float(rect.origin.x / containerSize.width)
  266. let originY = Float(rect.origin.y / containerSize.height)
  267. let width = Float(rect.size.width / containerSize.width)
  268. let height = Float(rect.size.height / containerSize.height)
  269. if reverse {
  270. return [originX, originY,
  271. originX, originY + height,
  272. originX + width, originY,
  273. originX + width, originY + height]
  274. }
  275. return [originX, originY + height,
  276. originX, originY,
  277. originX + width, originY + height,
  278. originX + width, originY]
  279. }
  280. @objc(sourceSizeForCenterFullWithSourceSize:renderSize:)
  281. public static func sourceSizeForCenterFull(sourceSize: CGSize, renderSize: CGSize) -> CGSize {
  282. if sourceSize.width >= renderSize.width, sourceSize.height >= renderSize.height {
  283. return sourceSize
  284. }
  285. return rect(inside: CGRect(origin: .zero, size: renderSize), aspectRatio: sourceSize, contentMode: .scaleAspectFill).size
  286. }
  287. @objc(rectForCenterFullWithSourceSize:renderSize:)
  288. public static func rectForCenterFull(sourceSize: CGSize, renderSize: CGSize) -> CGRect {
  289. if sourceSize.width >= renderSize.width, sourceSize.height >= renderSize.height {
  290. return CGRect(x: (sourceSize.width - renderSize.width) / 2.0,
  291. y: (sourceSize.height - renderSize.height) / 2.0,
  292. width: renderSize.width,
  293. height: renderSize.height)
  294. }
  295. let fill = rect(inside: CGRect(origin: .zero, size: renderSize), aspectRatio: sourceSize, contentMode: .scaleAspectFill)
  296. return CGRect(x: -fill.origin.x, y: -fill.origin.y, width: renderSize.width, height: renderSize.height)
  297. }
  298. @objc(rectInsideBoundingRect:aspectRatio:contentMode:)
  299. public static func rect(inside boundingRect: CGRect, aspectRatio: CGSize, contentMode: UIView.ContentMode) -> CGRect {
  300. guard aspectRatio.width > 0, aspectRatio.height > 0 else { return boundingRect }
  301. switch contentMode {
  302. case .scaleToFill:
  303. return boundingRect
  304. case .scaleAspectFit:
  305. return AVMakeRect(aspectRatio: aspectRatio, insideRect: boundingRect)
  306. case .scaleAspectFill:
  307. let ratio = max(boundingRect.width / aspectRatio.width, boundingRect.height / aspectRatio.height)
  308. let size = CGSize(width: aspectRatio.width * ratio, height: aspectRatio.height * ratio)
  309. return CGRect(x: boundingRect.origin.x + (boundingRect.width - size.width) / 2.0,
  310. y: boundingRect.origin.y + (boundingRect.height - size.height) / 2.0,
  311. width: size.width,
  312. height: size.height)
  313. case .center:
  314. return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
  315. y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
  316. width: aspectRatio.width,
  317. height: aspectRatio.height)
  318. case .top:
  319. return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
  320. y: boundingRect.origin.y,
  321. width: aspectRatio.width,
  322. height: aspectRatio.height)
  323. case .bottom:
  324. return CGRect(x: boundingRect.origin.x + (boundingRect.width - aspectRatio.width) / 2.0,
  325. y: boundingRect.maxY - aspectRatio.height,
  326. width: aspectRatio.width,
  327. height: aspectRatio.height)
  328. case .left:
  329. return CGRect(x: boundingRect.origin.x,
  330. y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
  331. width: aspectRatio.width,
  332. height: aspectRatio.height)
  333. case .right:
  334. return CGRect(x: boundingRect.maxX - aspectRatio.width,
  335. y: boundingRect.origin.y + (boundingRect.height - aspectRatio.height) / 2.0,
  336. width: aspectRatio.width,
  337. height: aspectRatio.height)
  338. case .topLeft:
  339. return CGRect(origin: boundingRect.origin, size: aspectRatio)
  340. case .topRight:
  341. return CGRect(x: boundingRect.maxX - aspectRatio.width,
  342. y: boundingRect.origin.y,
  343. width: aspectRatio.width,
  344. height: aspectRatio.height)
  345. case .bottomLeft:
  346. return CGRect(x: boundingRect.origin.x,
  347. y: boundingRect.maxY - aspectRatio.height,
  348. width: aspectRatio.width,
  349. height: aspectRatio.height)
  350. case .bottomRight:
  351. return CGRect(x: boundingRect.maxX - aspectRatio.width,
  352. y: boundingRect.maxY - aspectRatio.height,
  353. width: aspectRatio.width,
  354. height: aspectRatio.height)
  355. default:
  356. return boundingRect
  357. }
  358. }
  359. }
  360. public extension UIView {
  361. @objc func lnStopVAPIfNeeded() {
  362. stopHWDMP4()
  363. }
  364. }
  365. @objcMembers
  366. public final class LNVAPDeviceUtil: NSObject {
  367. private static var cachedSystemVersion: Double = (UIDevice.current.systemVersion as NSString).doubleValue
  368. public static var systemVersionNum: Double {
  369. cachedSystemVersion
  370. }
  371. public static var defaultMTLResourceOption: MTLResourceOptions {
  372. if #available(iOS 9.0, *) {
  373. return .storageModeShared
  374. }
  375. return []
  376. }
  377. }
  378. public extension NSArray {
  379. @objc(ln_rectValue)
  380. func ln_rectValue() -> CGRect {
  381. guard count >= 4 else { return .zero }
  382. for index in 0..<4 {
  383. let value = self[index]
  384. if !(value is NSNumber) && !(value is NSString) {
  385. return .zero
  386. }
  387. }
  388. return CGRect(x: (self[0] as? NSNumber)?.doubleValue ?? ((self[0] as? NSString)?.doubleValue ?? 0),
  389. y: (self[1] as? NSNumber)?.doubleValue ?? ((self[1] as? NSString)?.doubleValue ?? 0),
  390. width: (self[2] as? NSNumber)?.doubleValue ?? ((self[2] as? NSString)?.doubleValue ?? 0),
  391. height: (self[3] as? NSNumber)?.doubleValue ?? ((self[3] as? NSString)?.doubleValue ?? 0))
  392. }
  393. }
  394. public extension NSDictionary {
  395. @objc(ln_floatValueForKey:)
  396. func ln_floatValue(forKey key: String?) -> CGFloat {
  397. guard let key else { return 0 }
  398. guard let value = self[key], !(value is NSNull) else { return 0 }
  399. if let number = value as? NSNumber { return CGFloat(number.doubleValue) }
  400. if let string = value as? NSString { return CGFloat(string.doubleValue) }
  401. return 0
  402. }
  403. @objc(ln_integerValueForKey:)
  404. func ln_integerValue(forKey key: String?) -> Int {
  405. guard let key else { return 0 }
  406. guard let value = self[key], !(value is NSNull) else { return 0 }
  407. if let number = value as? NSNumber { return number.intValue }
  408. if let string = value as? NSString { return string.integerValue }
  409. return 0
  410. }
  411. @objc(ln_stringValueForKey:)
  412. func ln_stringValue(forKey key: String?) -> String {
  413. guard let key else { return "" }
  414. guard let value = self[key], !(value is NSNull) else { return "" }
  415. if let string = value as? String { return string }
  416. if let number = value as? NSNumber { return number.description }
  417. return ""
  418. }
  419. @objc(ln_dictionaryValueForKey:)
  420. func ln_dictionaryValue(forKey key: String?) -> NSDictionary? {
  421. guard let key else { return nil }
  422. return self[key] as? NSDictionary
  423. }
  424. @objc(ln_arrayValueForKey:)
  425. func ln_arrayValue(forKey key: String?) -> NSArray? {
  426. guard let key else { return nil }
  427. return self[key] as? NSArray
  428. }
  429. }
  430. public extension UIColor {
  431. @objc(ln_colorWithHexString:)
  432. static func ln_color(hexString: String) -> UIColor? {
  433. var normalized = hexString.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
  434. if normalized.hasPrefix("#") {
  435. normalized.removeFirst()
  436. } else if normalized.hasPrefix("0X") {
  437. normalized.removeFirst(2)
  438. }
  439. guard [3, 4, 6, 8].contains(normalized.count) else { return nil }
  440. func hexValue(_ text: String) -> CGFloat? {
  441. UInt64(text, radix: 16).map { CGFloat($0) / 255.0 }
  442. }
  443. let r: CGFloat
  444. let g: CGFloat
  445. let b: CGFloat
  446. let a: CGFloat
  447. switch normalized.count {
  448. case 3, 4:
  449. guard
  450. let rr = hexValue(String(normalized[normalized.startIndex])),
  451. let gg = hexValue(String(normalized[normalized.index(normalized.startIndex, offsetBy: 1)])),
  452. let bb = hexValue(String(normalized[normalized.index(normalized.startIndex, offsetBy: 2)]))
  453. else { return nil }
  454. r = rr
  455. g = gg
  456. b = bb
  457. if normalized.count == 4 {
  458. let index = normalized.index(normalized.startIndex, offsetBy: 3)
  459. guard let aa = hexValue(String(normalized[index])) else { return nil }
  460. a = aa
  461. } else {
  462. a = 1
  463. }
  464. case 6, 8:
  465. let start = normalized.startIndex
  466. let r0 = normalized[start...normalized.index(start, offsetBy: 1)]
  467. let g0 = normalized[normalized.index(start, offsetBy: 2)...normalized.index(start, offsetBy: 3)]
  468. let b0 = normalized[normalized.index(start, offsetBy: 4)...normalized.index(start, offsetBy: 5)]
  469. guard
  470. let rr = hexValue(String(r0)),
  471. let gg = hexValue(String(g0)),
  472. let bb = hexValue(String(b0))
  473. else { return nil }
  474. r = rr
  475. g = gg
  476. b = bb
  477. if normalized.count == 8 {
  478. let a0 = normalized[normalized.index(start, offsetBy: 6)...normalized.index(start, offsetBy: 7)]
  479. guard let aa = hexValue(String(a0)) else { return nil }
  480. a = aa
  481. } else {
  482. a = 1
  483. }
  484. default:
  485. return nil
  486. }
  487. return UIColor(red: r, green: g, blue: b, alpha: a)
  488. }
  489. }
  490. private final class LNGestureBlockTarget: NSObject {
  491. private let block: (Any) -> Void
  492. init(block: @escaping (Any) -> Void) {
  493. self.block = block
  494. }
  495. @objc func invoke(_ sender: Any) {
  496. block(sender)
  497. }
  498. }
  499. private var lnGestureTargetKey: UInt8 = 0
  500. public extension UIGestureRecognizer {
  501. @objc(ln_initWithActionBlock:)
  502. convenience init(lnActionBlock: @escaping (Any) -> Void) {
  503. self.init()
  504. ln_addActionBlock(lnActionBlock)
  505. }
  506. @objc(ln_addActionBlock:)
  507. func ln_addActionBlock(_ block: @escaping (Any) -> Void) {
  508. let target = LNGestureBlockTarget(block: block)
  509. addTarget(target, action: #selector(LNGestureBlockTarget.invoke(_:)))
  510. var targets = objc_getAssociatedObject(self, &lnGestureTargetKey) as? [LNGestureBlockTarget] ?? []
  511. targets.append(target)
  512. objc_setAssociatedObject(self, &lnGestureTargetKey, targets, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  513. }
  514. @objc(ln_removeAllActionBlocks)
  515. func ln_removeAllActionBlocks() {
  516. let targets = objc_getAssociatedObject(self, &lnGestureTargetKey) as? [LNGestureBlockTarget] ?? []
  517. for target in targets {
  518. removeTarget(target, action: #selector(LNGestureBlockTarget.invoke(_:)))
  519. }
  520. objc_setAssociatedObject(self, &lnGestureTargetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  521. }
  522. }
  523. private enum LNNotificationQueueStore {
  524. static let lock = NSRecursiveLock()
  525. static var store: [String: OperationQueue] = [:]
  526. }
  527. public extension Notification.Name {
  528. var ln_notificationOperationQueue: OperationQueue {
  529. get {
  530. LNNotificationQueueStore.lock.lock()
  531. defer { LNNotificationQueueStore.lock.unlock() }
  532. if let queue = LNNotificationQueueStore.store[rawValue] {
  533. return queue
  534. }
  535. let queue = OperationQueue()
  536. queue.maxConcurrentOperationCount = 1
  537. LNNotificationQueueStore.store[rawValue] = queue
  538. return queue
  539. }
  540. nonmutating set {
  541. LNNotificationQueueStore.lock.lock()
  542. LNNotificationQueueStore.store[rawValue] = newValue
  543. LNNotificationQueueStore.lock.unlock()
  544. }
  545. }
  546. }
  547. private final class LNWeakNotificationBox: NSObject {
  548. weak var observer: NSObjectProtocol?
  549. weak var center: NotificationCenter?
  550. var token: NSObjectProtocol?
  551. init(observer: NSObjectProtocol, center: NotificationCenter) {
  552. self.observer = observer
  553. self.center = center
  554. }
  555. }
  556. public extension NotificationCenter {
  557. @objc(ln_addSafeObserver:selector:name:object:)
  558. func ln_addSafeObserver(_ observer: NSObject, selector: Selector, name: Notification.Name?, object: Any?) {
  559. if #available(iOS 9.0, *) {
  560. addObserver(observer, selector: selector, name: name, object: object)
  561. return
  562. }
  563. let queue = name?.ln_notificationOperationQueue
  564. let box = LNWeakNotificationBox(observer: observer, center: self)
  565. box.token = addObserver(forName: name, object: object, queue: queue) { [weak box] note in
  566. guard let box, let target = box.observer else {
  567. if let token = box?.token {
  568. box?.center?.removeObserver(token)
  569. }
  570. return
  571. }
  572. _ = (target as AnyObject).perform(selector, with: note)
  573. }
  574. }
  575. @objc(ln_addSafeObserver:selector:name:object:queue:)
  576. func ln_addSafeObserver(_ observer: NSObject, selector: Selector, name: Notification.Name?, object: Any?, queue: OperationQueue) {
  577. if let name {
  578. name.ln_notificationOperationQueue = queue
  579. }
  580. ln_addSafeObserver(observer, selector: selector, name: name, object: object)
  581. }
  582. func ln_addWeakObserver(_ weakObserver: NSObject, name: Notification.Name?, using block: @escaping (Notification, NSObject) -> Void) {
  583. let box = LNWeakNotificationBox(observer: weakObserver, center: self)
  584. box.token = addObserver(forName: name, object: nil, queue: nil) { [weak box] note in
  585. guard let box, let target = box.observer as? NSObject else {
  586. if let token = box?.token {
  587. box?.center?.removeObserver(token)
  588. }
  589. return
  590. }
  591. block(note, target)
  592. }
  593. }
  594. }