Use DatabaseDictionary instead of NSDictionary. Work around a Swift memory leak with NSDictionary.

This commit is contained in:
Brent Simmons 2019-03-02 16:17:06 -08:00
parent 797c7cb0fb
commit e04250f1b3
6 changed files with 64 additions and 92 deletions

View File

@ -40,31 +40,30 @@ extension Article {
self.init(accountID: accountID, articleID: parsedItem.syncServiceID, feedID: feedID, uniqueID: parsedItem.uniqueID, title: parsedItem.title, contentHTML: parsedItem.contentHTML, contentText: parsedItem.contentText, url: parsedItem.url, externalURL: parsedItem.externalURL, summary: parsedItem.summary, imageURL: parsedItem.imageURL, bannerImageURL: parsedItem.bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, attachments: attachments, status: status)
}
private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: NSMutableDictionary) {
private func addPossibleStringChangeWithKeyPath(_ comparisonKeyPath: KeyPath<Article,String?>, _ otherArticle: Article, _ key: String, _ dictionary: inout DatabaseDictionary) {
if self[keyPath: comparisonKeyPath] != otherArticle[keyPath: comparisonKeyPath] {
dictionary.addOptionalStringDefaultingEmpty(self[keyPath: comparisonKeyPath], key)
dictionary[key] = self[keyPath: comparisonKeyPath] ?? ""
}
}
func changesFrom(_ existingArticle: Article) -> NSDictionary? {
func changesFrom(_ existingArticle: Article) -> DatabaseDictionary? {
if self == existingArticle {
return nil
}
let d = NSMutableDictionary()
var d = DatabaseDictionary()
if uniqueID != existingArticle.uniqueID {
d[DatabaseKey.uniqueID] = uniqueID
}
addPossibleStringChangeWithKeyPath(\Article.title, existingArticle, DatabaseKey.title, d)
addPossibleStringChangeWithKeyPath(\Article.contentHTML, existingArticle, DatabaseKey.contentHTML, d)
addPossibleStringChangeWithKeyPath(\Article.contentText, existingArticle, DatabaseKey.contentText, d)
addPossibleStringChangeWithKeyPath(\Article.url, existingArticle, DatabaseKey.url, d)
addPossibleStringChangeWithKeyPath(\Article.externalURL, existingArticle, DatabaseKey.externalURL, d)
addPossibleStringChangeWithKeyPath(\Article.summary, existingArticle, DatabaseKey.summary, d)
addPossibleStringChangeWithKeyPath(\Article.imageURL, existingArticle, DatabaseKey.imageURL, d)
addPossibleStringChangeWithKeyPath(\Article.bannerImageURL, existingArticle, DatabaseKey.bannerImageURL, d)
addPossibleStringChangeWithKeyPath(\Article.title, existingArticle, DatabaseKey.title, &d)
addPossibleStringChangeWithKeyPath(\Article.contentHTML, existingArticle, DatabaseKey.contentHTML, &d)
addPossibleStringChangeWithKeyPath(\Article.contentText, existingArticle, DatabaseKey.contentText, &d)
addPossibleStringChangeWithKeyPath(\Article.url, existingArticle, DatabaseKey.url, &d)
addPossibleStringChangeWithKeyPath(\Article.externalURL, existingArticle, DatabaseKey.externalURL, &d)
addPossibleStringChangeWithKeyPath(\Article.summary, existingArticle, DatabaseKey.summary, &d)
addPossibleStringChangeWithKeyPath(\Article.imageURL, existingArticle, DatabaseKey.imageURL, &d)
addPossibleStringChangeWithKeyPath(\Article.bannerImageURL, existingArticle, DatabaseKey.bannerImageURL, &d)
// If updated versions of dates are nil, and we have existing dates, keep the existing dates.
// This is data thats good to have, and its likely that a feed removing dates is doing so in error.
@ -91,27 +90,44 @@ extension Article {
extension Article: DatabaseObject {
public func databaseDictionary() -> NSDictionary? {
let d = NSMutableDictionary()
public func databaseDictionary() -> DatabaseDictionary? {
var d = DatabaseDictionary()
d[DatabaseKey.articleID] = articleID
d[DatabaseKey.feedID] = feedID
d[DatabaseKey.uniqueID] = uniqueID
d.addOptionalString(title, DatabaseKey.title)
d.addOptionalString(contentHTML, DatabaseKey.contentHTML)
d.addOptionalString(contentText, DatabaseKey.contentText)
d.addOptionalString(url, DatabaseKey.url)
d.addOptionalString(externalURL, DatabaseKey.externalURL)
d.addOptionalString(summary, DatabaseKey.summary)
d.addOptionalString(imageURL, DatabaseKey.imageURL)
d.addOptionalString(bannerImageURL, DatabaseKey.bannerImageURL)
d.addOptionalDate(datePublished, DatabaseKey.datePublished)
d.addOptionalDate(dateModified, DatabaseKey.dateModified)
return (d.copy() as! NSDictionary)
if let title = title {
d[DatabaseKey.title] = title
}
if let contentHTML = contentHTML {
d[DatabaseKey.contentHTML] = contentHTML
}
if let contentText = contentText {
d[DatabaseKey.contentText] = contentText
}
if let url = url {
d[DatabaseKey.url] = url
}
if let externalURL = externalURL {
d[DatabaseKey.externalURL] = externalURL
}
if let summary = summary {
d[DatabaseKey.summary] = summary
}
if let imageURL = imageURL {
d[DatabaseKey.imageURL] = imageURL
}
if let bannerImageURL = bannerImageURL {
d[DatabaseKey.bannerImageURL] = bannerImageURL
}
if let datePublished = datePublished {
d[DatabaseKey.datePublished] = datePublished
}
if let dateModified = dateModified {
d[DatabaseKey.dateModified] = dateModified
}
return d
}
public var databaseID: String {
@ -160,35 +176,8 @@ extension Set where Element == Article {
return self.map{ $0 as DatabaseObject }
}
func databaseDictionaries() -> [NSDictionary]? {
func databaseDictionaries() -> [DatabaseDictionary]? {
return self.compactMap { $0.databaseDictionary() }
}
}
private extension NSMutableDictionary {
func addOptionalString(_ value: String?, _ key: String) {
if let value = value {
self[key] = value
}
}
func addOptionalStringDefaultingEmpty(_ value: String?, _ key: String) {
if let value = value {
self[key] = value
}
else {
self[key] = ""
}
}
func addOptionalDate(_ date: Date?, _ key: String) {
if let date = date {
self[key] = date as NSDate
}
}
}

View File

@ -29,17 +29,8 @@ extension ArticleStatus: DatabaseObject {
return articleID
}
public func databaseDictionary() -> NSDictionary? {
let d = NSMutableDictionary()
d[DatabaseKey.articleID] = articleID
d[DatabaseKey.read] = read
d[DatabaseKey.starred] = starred
d[DatabaseKey.userDeleted] = userDeleted
d[DatabaseKey.dateArrived] = dateArrived
return (d.copy() as! NSDictionary)
public func databaseDictionary() -> DatabaseDictionary? {
return [DatabaseKey.articleID: articleID, DatabaseKey.read: read, DatabaseKey.starred: starred, DatabaseKey.userDeleted: userDeleted, DatabaseKey.dateArrived: dateArrived]
}
}

View File

@ -60,13 +60,8 @@ extension Attachment: DatabaseObject {
return attachmentID
}
public func databaseDictionary() -> NSDictionary? {
let d = NSMutableDictionary()
d[DatabaseKey.attachmentID] = attachmentID
d[DatabaseKey.url] = url
public func databaseDictionary() -> DatabaseDictionary? {
var d: DatabaseDictionary = [DatabaseKey.attachmentID: attachmentID, DatabaseKey.url: url]
if let mimeType = mimeType {
d[DatabaseKey.mimeType] = mimeType
}
@ -79,15 +74,14 @@ extension Attachment: DatabaseObject {
if let durationInSeconds = durationInSeconds {
d[DatabaseKey.durationInSeconds] = NSNumber(value: durationInSeconds)
}
return (d.copy() as! NSDictionary)
return d
}
}
extension Set where Element == Attachment {
func databaseDictionaries() -> [NSDictionary] {
func databaseDictionaries() -> [DatabaseDictionary] {
return self.compactMap { $0.databaseDictionary() }
}

View File

@ -48,12 +48,9 @@ extension Author: DatabaseObject {
return authorID
}
public func databaseDictionary() -> NSDictionary? {
let d = NSMutableDictionary()
d[DatabaseKey.authorID] = authorID
public func databaseDictionary() -> DatabaseDictionary? {
var d: DatabaseDictionary = [DatabaseKey.authorID: authorID]
if let name = name {
d[DatabaseKey.name] = name
}
@ -66,8 +63,7 @@ extension Author: DatabaseObject {
if let emailAddress = emailAddress {
d[DatabaseKey.emailAddress] = emailAddress
}
return (d.copy() as! NSDictionary)
return d
}
}

View File

@ -108,9 +108,11 @@ private extension SearchTable {
}
func insert(_ article: ArticleSearchInfo, _ database: FMDatabase) -> Int {
let rowDictionary = NSMutableDictionary()
rowDictionary.setObject(article.title ?? "", forKey: DatabaseKey.title as NSString)
rowDictionary.setObject(article.bodyForIndex, forKey: DatabaseKey.body as NSString)
let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? ""]
// rowDictionary[DatabaseKey.title] = article.title ?? ""
// rowDictionary[DatabaseKey.body] = article.bodyForIndex
// rowDictionary.setObject(article.title ?? "", forKey: DatabaseKey.title as NSString)
// rowDictionary.setObject(article.bodyForIndex, forKey: DatabaseKey.body as NSString)
insertRow(rowDictionary, insertType: .normal, in: database)
return Int(database.lastInsertRowId())
}
@ -168,12 +170,12 @@ private extension SearchTable {
return
}
let updateDictionary = NSMutableDictionary()
var updateDictionary = DatabaseDictionary()
if title != searchInfo.title {
updateDictionary.setObject(title, forKey: DatabaseKey.title as NSString)
updateDictionary[DatabaseKey.title] = title
}
if article.bodyForIndex != searchInfo.body {
updateDictionary.setObject(article.bodyForIndex, forKey: DatabaseKey.body as NSString)
updateDictionary[DatabaseKey.body] = article.bodyForIndex
}
updateRowsWithDictionary(updateDictionary, whereKey: DatabaseKey.rowID, matches: searchInfo.rowID, database: database)
}

@ -1 +1 @@
Subproject commit 3d57e08e9d905c432e86a2544ccfb1390257aec2
Subproject commit 92f72ec2c179a5cb5c1b69df8ab4d428d598c243