Rework the “disappearing messages” logic.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 9f569d376c
commit 6e52009ff0

@ -17,7 +17,6 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
@interface OWSReadReceiptsProcessor () @interface OWSReadReceiptsProcessor ()
@property (nonatomic, readonly) NSArray<OWSReadReceipt *> *readReceipts; @property (nonatomic, readonly) NSArray<OWSReadReceipt *> *readReceipts;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) TSStorageManager *storageManager;
@end @end
@ -34,7 +33,6 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
_readReceipts = [readReceipts copy]; _readReceipts = [readReceipts copy];
_storageManager = storageManager; _storageManager = storageManager;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
return self; return self;
} }
@ -85,7 +83,7 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
[TSIncomingMessage findMessageWithAuthorId:readReceipt.senderId timestamp:readReceipt.timestamp]; [TSIncomingMessage findMessageWithAuthorId:readReceipt.senderId timestamp:readReceipt.timestamp];
if (message) { if (message) {
[message markAsReadFromReadReceipt]; [message markAsReadFromReadReceipt];
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:readReceipt.timestamp]; [OWSDisappearingMessagesJob setExpirationForMessage:message expirationStartedAt:readReceipt.timestamp];
// If it was previously saved, no need to keep it around any longer. // If it was previously saved, no need to keep it around any longer.
[readReceipt remove]; [readReceipt remove];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]

@ -4,7 +4,6 @@
#import "OWSRecordTranscriptJob.h" #import "OWSRecordTranscriptJob.h"
#import "OWSAttachmentsProcessor.h" #import "OWSAttachmentsProcessor.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSIncomingSentMessageTranscript.h" #import "OWSIncomingSentMessageTranscript.h"
#import "OWSMessageSender.h" #import "OWSMessageSender.h"
#import "TSInfoMessage.h" #import "TSInfoMessage.h"

@ -4,7 +4,6 @@
#import "TSMessage.h" #import "TSMessage.h"
#import "NSDate+millisecondTimeStamp.h" #import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesJob.h"
#import "TSAttachment.h" #import "TSAttachment.h"
#import "TSAttachmentPointer.h" #import "TSAttachmentPointer.h"
#import "TSThread.h" #import "TSThread.h"

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/23/16. //
// Copyright © 2016 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -10,15 +11,13 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesJob : NSObject @interface OWSDisappearingMessagesJob : NSObject
- (instancetype)init NS_UNAVAILABLE; + (instancetype)sharedJob;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
- (void)run; - (instancetype)init NS_UNAVAILABLE;
- (void)setExpirationsForThread:(TSThread *)thread;
- (void)setExpirationForMessage:(TSMessage *)message;
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt;
- (void)runBy:(uint64_t)millisecondTimestamp;
+ (void)setExpirationsForThread:(TSThread *)thread;
+ (void)setExpirationForMessage:(TSMessage *)message;
+ (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt;
/** /**
* Synchronize our disappearing messages settings with that of the given message. Useful so we can * Synchronize our disappearing messages settings with that of the given message. Useful so we can
@ -31,9 +30,13 @@ NS_ASSUME_NONNULL_BEGIN
* @param contactsManager * @param contactsManager
* Provides the contact name responsible for any configuration changes in an info message. * Provides the contact name responsible for any configuration changes in an info message.
*/ */
- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message + (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager; contactsManager:(id<ContactsManagerProtocol>)contactsManager;
// Clean up any messages that expired since last launch immediately
// and continue cleaning in the background.
- (void)startIfNecessary;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -1,26 +1,46 @@
// Created by Michael Kirk on 9/23/16. //
// Copyright © 2016 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDisappearingMessagesJob.h" #import "OWSDisappearingMessagesJob.h"
#import "ContactsManagerProtocol.h" #import "ContactsManagerProtocol.h"
#import "NSDate+millisecondTimeStamp.h" #import "NSDate+millisecondTimeStamp.h"
#import "NSTimer+OWS.h"
#import "OWSDisappearingConfigurationUpdateInfoMessage.h" #import "OWSDisappearingConfigurationUpdateInfoMessage.h"
#import "OWSDisappearingMessagesConfiguration.h" #import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesFinder.h" #import "OWSDisappearingMessagesFinder.h"
#import "TSIncomingMessage.h" #import "TSIncomingMessage.h"
#import "TSMessage.h" #import "TSMessage.h"
#import "TSStorageManager.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesJob () @interface OWSDisappearingMessagesJob ()
// This property should only be accessed on the serialQueue.
@property (nonatomic, readonly) OWSDisappearingMessagesFinder *disappearingMessagesFinder; @property (nonatomic, readonly) OWSDisappearingMessagesFinder *disappearingMessagesFinder;
@property (atomic) uint64_t scheduledAt;
// These three properties should only be accessed on the main thread.
@property (nonatomic) BOOL hasStarted;
@property (nonatomic, nullable) NSTimer *timer;
@property (nonatomic, nullable) NSDate *timerScheduleDate;
@end @end
#pragma mark -
@implementation OWSDisappearingMessagesJob @implementation OWSDisappearingMessagesJob
+ (instancetype)sharedJob
{
static OWSDisappearingMessagesJob *sharedJob = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedJob = [[self alloc] initWithStorageManager:[TSStorageManager sharedManager]];
});
return sharedJob;
}
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager - (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{ {
self = [super init]; self = [super init];
@ -28,12 +48,23 @@ NS_ASSUME_NONNULL_BEGIN
return self; return self;
} }
_scheduledAt = ULLONG_MAX;
_disappearingMessagesFinder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:storageManager]; _disappearingMessagesFinder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:storageManager];
OWSSingletonAssert();
return self; return self;
} }
+ (dispatch_queue_t)serialQueue
{
static dispatch_queue_t queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.whispersystems.disappearing.messages", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
- (void)run - (void)run
{ {
uint64_t now = [NSDate ows_millisecondTimeStamp]; uint64_t now = [NSDate ows_millisecondTimeStamp];
@ -46,18 +77,17 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
DDLogDebug(@"%@ removing message which expired at: %lld", self.tag, message.expiresAt); DDLogDebug(@"%@ Removing message which expired at: %lld", self.tag, message.expiresAt);
[message remove]; [message remove];
expirationCount++; expirationCount++;
}]; }];
DDLogDebug(@"%@ removed %u expired messages", self.tag, expirationCount); DDLogDebug(@"%@ Removed %u expired messages", self.tag, expirationCount);
} }
- (void)runLoop - (void)runLoop
{ {
// allow next runAt to schedule. DDLogVerbose(@"%@ Run", self.tag);
self.scheduledAt = ULLONG_MAX;
[self run]; [self run];
@ -69,23 +99,19 @@ NS_ASSUME_NONNULL_BEGIN
unsigned int delaySeconds = (10 * 60); // 10 minutes. unsigned int delaySeconds = (10 * 60); // 10 minutes.
DDLogDebug( DDLogDebug(
@"%@ No more expiring messages. Setting next check %u seconds into the future", self.tag, delaySeconds); @"%@ No more expiring messages. Setting next check %u seconds into the future", self.tag, delaySeconds);
[self runBy:now + delaySeconds * 1000]; [self runByDate:[NSDate ows_dateWithMillisecondsSince1970:now + delaySeconds * 1000]];
return; return;
} }
uint64_t nextExpirationAt = [nextExpirationTimestampNumber unsignedLongLongValue]; uint64_t nextExpirationAt = [nextExpirationTimestampNumber unsignedLongLongValue];
uint64_t runByMilliseconds; [self runByDate:[NSDate ows_dateWithMillisecondsSince1970:MAX(nextExpirationAt, now)]];
if (nextExpirationAt < now + 1000) { }
DDLogWarn(@"%@ Next run requested at %llu, which is too soon. Delaying by 1 sec to prevent churn",
self.tag,
nextExpirationAt);
runByMilliseconds = now + 1000;
} else {
runByMilliseconds = nextExpirationAt;
}
DDLogVerbose(@"%@ Requesting next expiration to run by: %llu", self.tag, nextExpirationAt); + (void)setExpirationForMessage:(TSMessage *)message
[self runBy:runByMilliseconds]; {
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationForMessage:message];
});
} }
- (void)setExpirationForMessage:(TSMessage *)message - (void)setExpirationForMessage:(TSMessage *)message
@ -104,6 +130,13 @@ NS_ASSUME_NONNULL_BEGIN
[self setExpirationForMessage:message expirationStartedAt:[NSDate ows_millisecondTimeStamp]]; [self setExpirationForMessage:message expirationStartedAt:[NSDate ows_millisecondTimeStamp]];
} }
+ (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationForMessage:message expirationStartedAt:expirationStartedAt];
});
}
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt - (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{ {
if (!message.isExpiringMessage) { if (!message.isExpiringMessage) {
@ -120,7 +153,14 @@ NS_ASSUME_NONNULL_BEGIN
} }
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration. // Necessary that the async expiration run happens *after* the message is saved with expiration configuration.
[self runBy:message.expiresAt]; [self runByDate:[NSDate ows_dateWithMillisecondsSince1970:message.expiresAt]];
}
+ (void)setExpirationsForThread:(TSThread *)thread
{
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationsForThread:thread];
});
} }
- (void)setExpirationsForThread:(TSThread *)thread - (void)setExpirationsForThread:(TSThread *)thread
@ -138,26 +178,14 @@ NS_ASSUME_NONNULL_BEGIN
}]; }];
} }
- (void)runBy:(uint64_t)timestamp + (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager
{ {
// Prevent amplification. dispatch_async(self.serialQueue, ^{
if (timestamp >= self.scheduledAt) { [[self sharedJob] becomeConsistentWithConfigurationForMessage:message contactsManager:contactsManager];
DDLogVerbose(@"%@ expiration already scheduled before %llu", self.tag, timestamp); });
return;
}
// Update Schedule
DDLogVerbose(@"%@ Scheduled expiration run at %llu", self.tag, timestamp);
self.scheduledAt = timestamp;
uint64_t millisecondsDelay = timestamp - [NSDate ows_millisecondTimeStamp];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * millisecondsDelay),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[self runLoop];
});
} }
- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message - (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager contactsManager:(id<ContactsManagerProtocol>)contactsManager
{ {
@ -206,6 +234,83 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
- (void)startIfNecessary
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.hasStarted) {
return;
}
self.hasStarted = YES;
[self runNow];
});
}
- (void)runNow
{
[self runByDate:[NSDate new] ignoreMinDelay:YES];
}
- (void)runByDate:(NSDate *)date
{
[self runByDate:date ignoreMinDelay:NO];
}
- (void)runByDate:(NSDate *)date ignoreMinDelay:(BOOL)ignoreMinDelay
{
OWSAssert(date);
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.dateStyle = NSDateFormatterShortStyle;
dateFormatter.timeStyle = kCFDateFormatterMediumStyle;
dispatch_async(dispatch_get_main_queue(), ^{
// Don't run more often than once per second.
const NSTimeInterval kMinDelaySeconds = ignoreMinDelay ? 0.f : 1.f;
// Don't run less often than once per N minutes.
const NSTimeInterval kMaxDelaySeconds = 5 * 60.f;
NSTimeInterval delaySeconds
= MAX(kMinDelaySeconds, MIN(kMaxDelaySeconds, [date timeIntervalSinceDate:[NSDate new]]));
NSDate *timerScheduleDate = [NSDate dateWithTimeIntervalSinceNow:delaySeconds];
if (self.timerScheduleDate && [timerScheduleDate timeIntervalSinceDate:self.timerScheduleDate] > 0) {
DDLogVerbose(@"%@ Request to run at %@ (%d sec.) ignored due to scheduled run at %@ (%d sec.)",
self.tag,
[dateFormatter stringFromDate:date],
(int)round(MAX(0, [date timeIntervalSinceDate:[NSDate new]])),
[dateFormatter stringFromDate:self.timerScheduleDate],
(int)round(MAX(0, [self.timerScheduleDate timeIntervalSinceDate:[NSDate new]])));
return;
}
// Update Schedule
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.dateStyle = NSDateFormatterShortStyle;
dateFormatter.timeStyle = kCFDateFormatterMediumStyle;
DDLogVerbose(@"%@ Scheduled run at %@ (%d sec.)",
self.tag,
[dateFormatter stringFromDate:timerScheduleDate],
(int)round(MAX(0, [timerScheduleDate timeIntervalSinceDate:[NSDate new]])));
self.timerScheduleDate = timerScheduleDate;
[self.timer invalidate];
self.timer = [NSTimer weakScheduledTimerWithTimeInterval:delaySeconds
target:self
selector:@selector(timerDidFire)
userInfo:nil
repeats:NO];
});
}
- (void)timerDidFire
{
[self.timer invalidate];
self.timer = nil;
self.timerScheduleDate = nil;
dispatch_async(OWSDisappearingMessagesJob.serialQueue, ^{
[self runLoop];
});
}
#pragma mark - Logging #pragma mark - Logging
+ (NSString *)tag + (NSString *)tag

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/24/16. //
// Copyright © 2016 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSIncomingMessageReadObserver.h" #import "OWSIncomingMessageReadObserver.h"
#import "NSDate+millisecondTimeStamp.h" #import "NSDate+millisecondTimeStamp.h"
@ -13,7 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingMessageReadObserver () @interface OWSIncomingMessageReadObserver ()
@property BOOL isObserving; @property BOOL isObserving;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) OWSSendReadReceiptsJob *sendReadReceiptsJob; @property (nonatomic, readonly) OWSSendReadReceiptsJob *sendReadReceiptsJob;
@end @end
@ -34,7 +34,6 @@ NS_ASSUME_NONNULL_BEGIN
} }
_isObserving = NO; _isObserving = NO;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
_sendReadReceiptsJob = [[OWSSendReadReceiptsJob alloc] initWithMessageSender:messageSender]; _sendReadReceiptsJob = [[OWSSendReadReceiptsJob alloc] initWithMessageSender:messageSender];
return self; return self;
@ -61,7 +60,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
TSIncomingMessage *message = (TSIncomingMessage *)notification.object; TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
[self.disappearingMessagesJob setExpirationForMessage:message]; [OWSDisappearingMessagesJob setExpirationForMessage:message];
[self.sendReadReceiptsJob runWith:message]; [self.sendReadReceiptsJob runWith:message];
} }

@ -341,7 +341,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager; @property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) ContactsUpdater *contactsUpdater; @property (nonatomic, readonly) ContactsUpdater *contactsUpdater;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (atomic, readonly) NSMutableDictionary<NSString *, NSOperationQueue *> *sendingQueueMap; @property (atomic, readonly) NSMutableDictionary<NSString *, NSOperationQueue *> *sendingQueueMap;
@end @end
@ -366,7 +365,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
_uploadingService = [[OWSUploadingService alloc] initWithNetworkManager:networkManager]; _uploadingService = [[OWSUploadingService alloc] initWithNetworkManager:networkManager];
_dbConnection = storageManager.newDatabaseConnection; _dbConnection = storageManager.newDatabaseConnection;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
OWSSingletonAssert(); OWSSingletonAssert();
@ -1060,20 +1058,20 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self sendSyncTranscriptForMessage:message]; [self sendSyncTranscriptForMessage:message];
} }
[self.disappearingMessagesJob setExpirationForMessage:message]; [OWSDisappearingMessagesJob setExpirationForMessage:message];
} }
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt - (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt
{ {
[message updateWithWasSentAndDelivered]; [message updateWithWasSentAndDelivered];
[self becomeConsistentWithDisappearingConfigurationForMessage:message]; [self becomeConsistentWithDisappearingConfigurationForMessage:message];
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:sentAt]; [OWSDisappearingMessagesJob setExpirationForMessage:message expirationStartedAt:sentAt];
} }
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSOutgoingMessage *)outgoingMessage - (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSOutgoingMessage *)outgoingMessage
{ {
[self.disappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage [OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager]; contactsManager:self.contactsManager];
} }
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage - (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage

@ -13,7 +13,6 @@ NS_ASSUME_NONNULL_BEGIN
@class OWSSignalServiceProtosEnvelope; @class OWSSignalServiceProtosEnvelope;
@class OWSSignalServiceProtosDataMessage; @class OWSSignalServiceProtosDataMessage;
@class ContactsUpdater; @class ContactsUpdater;
@class OWSDisappearingMessagesJob;
@class OWSMessageSender; @class OWSMessageSender;
@protocol ContactsManagerProtocol; @protocol ContactsManagerProtocol;
@protocol OWSCallMessageHandler; @protocol OWSCallMessageHandler;

@ -48,12 +48,13 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager; @property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder; @property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder;
@property (nonatomic, readonly) OWSBlockingManager *blockingManager; @property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@end @end
#pragma mark -
@implementation TSMessagesManager @implementation TSMessagesManager
+ (instancetype)sharedManager { + (instancetype)sharedManager {
@ -103,7 +104,6 @@ NS_ASSUME_NONNULL_BEGIN
_messageSender = messageSender; _messageSender = messageSender;
_dbConnection = storageManager.newDatabaseConnection; _dbConnection = storageManager.newDatabaseConnection;
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
_incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithDatabase:storageManager.database]; _incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithDatabase:storageManager.database];
_blockingManager = [OWSBlockingManager sharedManager]; _blockingManager = [OWSBlockingManager sharedManager];
@ -940,8 +940,8 @@ NS_ASSUME_NONNULL_BEGIN
storageManager:self.storageManager]; storageManager:self.storageManager];
[readReceiptsProcessor process]; [readReceiptsProcessor process];
[self.disappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage [OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage
contactsManager:self.contactsManager]; contactsManager:self.contactsManager];
// Update thread preview in inbox // Update thread preview in inbox
[thread touch]; [thread touch];

@ -0,0 +1,19 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@interface NSTimer (OWS)
// This method avoids the classic NSTimer retain cycle bug
// by using a weak reference to the target.
+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
target:(id)target
selector:(SEL)selector
userInfo:(nullable id)userInfo
repeats:(BOOL)repeats;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,69 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "NSTimer+OWS.h"
#import <objc/runtime.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTimerProxy : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic) SEL selector;
@end
#pragma mark -
@implementation NSTimerProxy
- (void)timerFired:(NSDictionary *)userInfo
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.target performSelector:self.selector withObject:userInfo];
#pragma clang diagnostic pop
}
@end
#pragma mark -
static void *kNSTimer_OWS_Proxy = &kNSTimer_OWS_Proxy;
@implementation NSTimer (OWS)
- (NSTimerProxy *)ows_proxy
{
return objc_getAssociatedObject(self, kNSTimer_OWS_Proxy);
}
- (void)ows_setProxy:(NSTimerProxy *)proxy
{
OWSAssert(proxy);
objc_setAssociatedObject(self, kNSTimer_OWS_Proxy, proxy, OBJC_ASSOCIATION_RETAIN);
}
+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
target:(id)target
selector:(SEL)selector
userInfo:(nullable id)userInfo
repeats:(BOOL)repeats
{
NSTimerProxy *proxy = [NSTimerProxy new];
proxy.target = target;
proxy.selector = selector;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval
target:proxy
selector:@selector(timerFired:)
userInfo:userInfo
repeats:repeats];
[timer ows_setProxy:proxy];
return timer;
}
@end
NS_ASSUME_NONNULL_END
Loading…
Cancel
Save