Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions PINRemoteImage.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
139D50B01F672BBF00DE64E0 /* PINRemoteImageDownloadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B1F27F1E679D7A00ED87C4 /* PINRemoteImageDownloadQueue.h */; };
139D50B11F672BBF00DE64E0 /* PINResume.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B7E3B01E736C73000FC887 /* PINResume.h */; };
139D50B21F672BBF00DE64E0 /* PINSpeedRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 6860CB061F578287005E886E /* PINSpeedRecorder.h */; };
15E2A2683A71757D64EADA89 /* PINProgressiveImage+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 14326ABB868F40FB6CE7328E /* PINProgressiveImage+Private.h */; };
2035B2727792667AFEFDC0F2 /* PINRemoteLockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E9278EF02C46E2F93F5DBB8E /* PINRemoteLockTests.m */; };
440872302BFFC55700C053C2 /* PINRemoteImageDataConvertible.h in Headers */ = {isa = PBXBuildFile; fileRef = 4408722F2BFFC4AF00C053C2 /* PINRemoteImageDataConvertible.h */; settings = {ATTRIBUTES = (Public, ); }; };
440872312BFFC55B00C053C2 /* PINRemoteImageDataConvertible.h in Headers */ = {isa = PBXBuildFile; fileRef = 4408722F2BFFC4AF00C053C2 /* PINRemoteImageDataConvertible.h */; settings = {ATTRIBUTES = (Public, ); }; };
445C57202BF26B6A0005D70F /* NSData+ImageDetectors.h in Headers */ = {isa = PBXBuildFile; fileRef = 445C570A2BF26B6A0005D70F /* NSData+ImageDetectors.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -153,6 +155,8 @@
B5BB4024274DB69F0042AE1D /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5BB4022274DB69F0042AE1D /* Media.xcassets */; };
D93A340224182D46005EB15E /* TestAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D93A340124182D46005EB15E /* TestAnimatedImage.m */; };
D93A340324182D46005EB15E /* TestAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D93A340124182D46005EB15E /* TestAnimatedImage.m */; };
EAFC80CEC3D80C1A6CABE195 /* PINProgressiveImageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1386DBFC97AF2485ED332AF1 /* PINProgressiveImageTests.m */; };
F17058DB8820DB9B406074E0 /* PINProgressiveImage+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 14326ABB868F40FB6CE7328E /* PINProgressiveImage+Private.h */; };
F1B918FF1BCF23C900710963 /* PINRemoteImageCategoryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1B918DC1BCF23C800710963 /* PINRemoteImageCategoryManager.m */; };
F1B919011BCF23C900710963 /* NSData+ImageDetectors.m in Sources */ = {isa = PBXBuildFile; fileRef = F1B918DF1BCF23C800710963 /* NSData+ImageDetectors.m */; };
F1B9190F1BCF23C900710963 /* PINProgressiveImage.m in Sources */ = {isa = PBXBuildFile; fileRef = F1B918EE1BCF23C800710963 /* PINProgressiveImage.m */; };
Expand Down Expand Up @@ -201,8 +205,10 @@
/* Begin PBXFileReference section */
0CF0743524093139008F126B /* PINAPNGAnimatedImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PINAPNGAnimatedImage.m; sourceTree = "<group>"; };
0E71BE2522E94C7200FC5B99 /* fireworks.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = fireworks.gif; sourceTree = "<group>"; };
1386DBFC97AF2485ED332AF1 /* PINProgressiveImageTests.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = PINProgressiveImageTests.m; sourceTree = "<group>"; };
139D3A5A1F67284400F82935 /* PINRemoteImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PINRemoteImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
139D3A621F67284400F82935 /* PINRemoteImage-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PINRemoteImage-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
14326ABB868F40FB6CE7328E /* PINProgressiveImage+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINProgressiveImage+Private.h"; sourceTree = "<group>"; };
4408722F2BFFC4AF00C053C2 /* PINRemoteImageDataConvertible.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PINRemoteImageDataConvertible.h; sourceTree = "<group>"; };
4419469E2BF3AAF100E7A59C /* WebP.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebP.xcframework; path = webp/WebP.xcframework; sourceTree = "<group>"; };
441946A12BF3AB6500E7A59C /* WebPDemux.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebPDemux.xcframework; path = webp/WebPDemux.xcframework; sourceTree = "<group>"; };
Expand Down Expand Up @@ -283,6 +289,7 @@
B5BB4022274DB69F0042AE1D /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
D93A340024182D46005EB15E /* TestAnimatedImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestAnimatedImage.h; sourceTree = "<group>"; };
D93A340124182D46005EB15E /* TestAnimatedImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestAnimatedImage.m; sourceTree = "<group>"; };
E9278EF02C46E2F93F5DBB8E /* PINRemoteLockTests.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = PINRemoteLockTests.m; sourceTree = "<group>"; };
F1B918D11BCF239200710963 /* PINRemoteImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PINRemoteImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F1B918D61BCF239200710963 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F1B918DC1BCF23C800710963 /* PINRemoteImageCategoryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PINRemoteImageCategoryManager.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -409,6 +416,8 @@
ACD288C6E6B13E6DA226D252 /* NSDate+PINCacheTests.h */,
ACD28D963D79EEC14EE071CE /* NSDate+PINCacheTests.m */,
B5BB4022274DB69F0042AE1D /* Media.xcassets */,
1386DBFC97AF2485ED332AF1 /* PINProgressiveImageTests.m */,
E9278EF02C46E2F93F5DBB8E /* PINRemoteLockTests.m */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -508,6 +517,7 @@
68B7E3B11E736C73000FC887 /* PINResume.m */,
6860CB061F578287005E886E /* PINSpeedRecorder.h */,
6860CB071F578287005E886E /* PINSpeedRecorder.m */,
14326ABB868F40FB6CE7328E /* PINProgressiveImage+Private.h */,
);
name = Project;
path = Classes;
Expand Down Expand Up @@ -621,6 +631,7 @@
139D50B21F672BBF00DE64E0 /* PINSpeedRecorder.h in Headers */,
68912D87208FDB4900F5FE0E /* PINRemoteWeakProxy.h in Headers */,
689613E5208FD90B00D2095C /* PINDisplayLink.h in Headers */,
15E2A2683A71757D64EADA89 /* PINProgressiveImage+Private.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -667,6 +678,7 @@
445C572C2BF26B6A0005D70F /* PINRemoteImage.h in Headers */,
445C57202BF26B6A0005D70F /* NSData+ImageDetectors.h in Headers */,
445C57222BF26B6A0005D70F /* PINAnimatedImage.h in Headers */,
F17058DB8820DB9B406074E0 /* PINProgressiveImage+Private.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -879,6 +891,8 @@
683128F51F95045200D5B4A8 /* PINAnimatedImage+PINAnimatedImageTesting.m in Sources */,
D93A340224182D46005EB15E /* TestAnimatedImage.m in Sources */,
ACD28AB87FABF6BA3B9BF4E4 /* NSDate+PINCacheTests.m in Sources */,
EAFC80CEC3D80C1A6CABE195 /* PINProgressiveImageTests.m in Sources */,
2035B2727792667AFEFDC0F2 /* PINRemoteLockTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
31 changes: 31 additions & 0 deletions Source/Classes/PINProgressiveImage+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// PINProgressiveImage+Private.h
// PINRemoteImage
//
// Created by Vitor Navarro on 30/7/26.
//
//

#import <PINRemoteImage/PINProgressiveImage.h>

NS_ASSUME_NONNULL_BEGIN

@interface PINProgressiveImage (Private)

/**
Returns the current data and relinquishes ownership of it, avoiding a copy.

This is destructive and deliberately kept out of the public header: it must only be
called once no further data can be appended for this task -- i.e. from the URL
session completion handler, after every -didReceiveData: for the task has already
been delivered synchronously on its serial delegate queue. Calling it from any other
context (e.g. mid-flight, via KVC, or from a debugger) can silently truncate the
buffer fed to an in-progress incremental image decode.

@return NSData The data accumulated so far. Returns nil on a second call.
*/
- (nullable NSData *)takeData;

@end

NS_ASSUME_NONNULL_END
53 changes: 52 additions & 1 deletion Source/Classes/PINProgressiveImage.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
@property (nonatomic, assign) NSUInteger scannedByte;
@property (nonatomic, assign) NSInteger sosCount;
@property (nonatomic, strong) PINRemoteLock *lock;
// Set once -takeData has handed off mutableData. Overloads mutableData == nil (which
// otherwise means "nothing received yet") with a second meaning: "already handed off,
// this object is terminal." See the guard at the top of -updateProgressiveImageWithData:.
@property (nonatomic, assign) BOOL dataTaken;
#if DEBUG
@property (nonatomic, assign) CFTimeInterval scanTime;
#endif
Expand Down Expand Up @@ -133,6 +137,13 @@
- (void)updateProgressiveImageWithData:(nonnull NSData *)data expectedNumberOfBytes:(int64_t)expectedNumberOfBytes isResume:(BOOL)isResume
{
[self.lock lock];
if (self.dataTaken) {
// Data was already handed off to the completion path (-takeData); this object
// is terminal. Without this guard, a late append would allocate a fresh, short
// buffer here and silently corrupt the incremental image source it fed earlier.
[self.lock unlock];
return;
}
if (isResume) {
NSAssert(self.mutableData == nil, @"If we're resuming, data shouldn't be setup yet.");
self.startingBytes = data.length;
Expand Down Expand Up @@ -265,7 +276,47 @@
- (NSData *)data
{
[self.lock lock];
NSData *data = [self.mutableData copy];
NSData *data = nil;
if (self.memorySafeDataEnabled) {
@try {
data = [self.mutableData copy];
} @catch (NSException *exception) {
// Foundation's page allocator raises NSInvalidArgumentException rather than
// returning nil when it cannot satisfy a large allocation. Callers already
// treat nil as "no data" (see PINRemoteImageDownloadTask).
data = nil;
}
} else {
data = [self.mutableData copy];
}
[self.lock unlock];
return data;
}

// Exactly equivalent to `[self data] == nil` today, without materializing a copy.
// NOT the same as `data.length == 0` -- a fresh-but-allocated zero-length buffer makes
// `-data` return non-nil (see PINRemoteImageDownloadTask's retry/emptiness check).
- (BOOL)hasData
{
[self.lock lock];
BOOL hasData = (self.mutableData != nil);
[self.lock unlock];
return hasData;
}

// Transfers ownership of the accumulated buffer to the caller without copying.
// ONLY safe once no further data can arrive for this task -- i.e. from the URL
// session completion handler, whose per-task delegate queue is serial and has
// already delivered every -didReceiveData: synchronously.
// Returns a dynamically-mutable NSMutableData upcast to NSData: it is uniquely owned
// (we drop our reference) and nothing in-repo mutates it. Foundation offers no way to
// obtain a truly immutable NSData from an NSMutableData without copying.
- (NSData *)takeData
{
[self.lock lock];
NSData *data = self.mutableData;
self.mutableData = nil;
self.dataTaken = YES;
[self.lock unlock];
return data;
}
Expand Down Expand Up @@ -360,7 +411,7 @@
.colorSpace = NULL,
// (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little)
// requests a BGRA buffer.
.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,

Check warning on line 414 in Source/Classes/PINProgressiveImage.m

View workflow job for this annotation

GitHub Actions / Test

bitwise operation between different enumeration types ('enum CGImageAlphaInfo' and 'enum CGBitmapInfo') [-Wenum-enum-conversion]
.version = 0,
.decode = NULL,
.renderingIntent = kCGRenderingIntentDefault
Expand Down
29 changes: 26 additions & 3 deletions Source/Classes/PINRemoteImageDownloadTask.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "PINRemoteImageCallbacks.h"
#import "PINRemoteLock.h"
#import "PINSpeedRecorder.h"
#import "PINProgressiveImage+Private.h"

@interface PINRemoteImageDownloadTask ()
{
Expand Down Expand Up @@ -311,9 +312,15 @@ - (void)scheduleDownloadWithRequest:(NSURLRequest *)request
#endif

if (error.code != NSURLErrorCancelled) {
NSData *data = self.progressImage.data;

if (error == nil && data == nil) {
// Read once so this completion stays on a single path even if the switch flips mid-flight.
BOOL memorySafeData = self.manager.isMemorySafeProgressiveDataEnabled;
// When memory-safe, decide emptiness and retry WITHOUT materializing the buffer --
// on a retry the copy was discarded, and the allocation itself can fail under memory
// pressure. -hasData preserves the copy path's semantics exactly:
// an allocated-but-zero-length buffer is not "empty" (see PINProgressiveImage).
NSData *data = memorySafeData ? nil : self.progressImage.data;
BOOL isEmpty = memorySafeData ? (self.progressImage.hasData == NO) : (data == nil);
if (error == nil && isEmpty) {
error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain
code:PINRemoteImageManagerErrorImageEmpty
userInfo:nil];
Expand All @@ -338,10 +345,26 @@ - (void)scheduleDownloadWithRequest:(NSURLRequest *)request
return;
}

if (memorySafeData) {
// Terminal: no further data can arrive, so hand off the buffer instead of
// copying it. -takeData allocates nothing, so it cannot throw under memory
// pressure -- this is what actually removes the doubling.
data = [self.progressImage takeData];
if (error == nil && data == nil) {
// Load-bearing: with -hasData gating the retry decision above, this only
// fires if -progressImage itself is nil, keeping completionHandler from
// ever seeing (nil, response, nil).
error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain
code:PINRemoteImageManagerErrorImageEmpty
userInfo:nil];
}
}

completionHandler(data, response, error);
}
}];
}]];
self->_progressImage.memorySafeDataEnabled = self.manager.isMemorySafeProgressiveDataEnabled;
}];
}

Expand Down
1 change: 1 addition & 0 deletions Source/Classes/PINRemoteImageManager+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSURLResponse
@property (nonatomic, readonly) NSArray <NSNumber *> *progressThresholds;
@property (nonatomic, readonly) NSTimeInterval estimatedRemainingTimeThreshold;
@property (nonatomic, readonly) BOOL shouldBlurProgressive;
@property (nonatomic, readonly) BOOL isMemorySafeProgressiveDataEnabled;
@property (nonatomic, readonly) CGSize maxProgressiveRenderSize;

@end
Expand Down
2 changes: 2 additions & 0 deletions Source/Classes/PINRemoteImageManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
@property (nonatomic, strong) NSMapTable <NSUUID *, PINRemoteImageTask *> *UUIDToTask;
@property (nonatomic, strong) NSArray <NSNumber *> *progressThresholds;
@property (nonatomic, assign) BOOL shouldBlurProgressive;
@property (nonatomic, assign) BOOL isMemorySafeProgressiveDataEnabled;
@property (nonatomic, assign) CGSize maxProgressiveRenderSize;
@property (nonatomic, assign) NSTimeInterval estimatedRemainingTimeThreshold;
@property (nonatomic, strong) dispatch_queue_t callbackQueue;
Expand Down Expand Up @@ -262,6 +263,7 @@
_shouldUpgradeLowQualityImages = configuration.shouldUpgradeLowQualityImages;
_shouldBlurProgressive = configuration.shouldBlurProgressive;
_maxProgressiveRenderSize = configuration.maxProgressiveRenderSize;
_isMemorySafeProgressiveDataEnabled = configuration.isMemorySafeProgressiveDataEnabled;
self.tasks = [[NSMutableDictionary alloc] init];
self.canceledTasks = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5];
self.UUIDToTask = [NSMapTable weakToWeakObjectsMapTable];
Expand Down Expand Up @@ -1569,14 +1571,14 @@
//we return the hash of it instead.
if (cacheKey.length > PINRemoteImageManagerCacheKeyMaxLength) {
__block CC_MD5_CTX ctx;
CC_MD5_Init(&ctx);

Check warning on line 1574 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Test

'CC_MD5_Init' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1574 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / example-spm-xcode

'CC_MD5_Init' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1574 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Analyze

'CC_MD5_Init' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]
NSData *data = [cacheKey dataUsingEncoding:NSUTF8StringEncoding];
[data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) {
CC_MD5_Update(&ctx, bytes, (CC_LONG)byteRange.length);

Check warning on line 1577 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Test

'CC_MD5_Update' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1577 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / example-spm-xcode

'CC_MD5_Update' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1577 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Analyze

'CC_MD5_Update' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]
}];

unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5_Final(digest, &ctx);

Check warning on line 1581 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Test

'CC_MD5_Final' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1581 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / example-spm-xcode

'CC_MD5_Final' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

Check warning on line 1581 in Source/Classes/PINRemoteImageManager.m

View workflow job for this annotation

GitHub Actions / Analyze

'CC_MD5_Final' is deprecated: first deprecated in iOS 13.0 - This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger). [-Wdeprecated-declarations]

NSMutableString *hexString = [NSMutableString stringWithCapacity:(CC_MD5_DIGEST_LENGTH * 2)];
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
Expand Down
1 change: 1 addition & 0 deletions Source/Classes/PINRemoteImageManagerConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ - (nonnull instancetype)init {
_highQualityBPSThreshold = 500000;
_lowQualityBPSThreshold = 50000; // approximately edge speed
_shouldUpgradeLowQualityImages = NO;
_isMemorySafeProgressiveDataEnabled = NO;
}
return self;
}
Expand Down
11 changes: 7 additions & 4 deletions Source/Classes/PINRemoteLock.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ - (void)lockWithBlock:(dispatch_block_t)block
#else
pthread_mutex_lock(&_lock);
#endif
block();
@try {
Comment thread
andyfinnell marked this conversation as resolved.
Resolved
block();
} @finally {
#if PINREMOTELOCK_DEBUG
[_lock unlock];
[_recursiveLock unlock];
[_lock unlock];
[_recursiveLock unlock];
#else
pthread_mutex_unlock(&_lock);
pthread_mutex_unlock(&_lock);
#endif
}
}

- (void)lock
Expand Down
5 changes: 5 additions & 0 deletions Source/Classes/include/PINRemoteImage/PINProgressiveImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
@property (atomic, assign) CFTimeInterval estimatedRemainingTimeThreshold;
@property (nonatomic, strong, readonly, nonnull) NSURLSessionDataTask * dataTask;
@property (nonatomic, readonly) CFTimeInterval estimatedRemainingTime;
/** When YES, -data returns nil instead of raising if copying the buffer fails. Defaults to NO. */
@property (atomic, assign, getter=isMemorySafeDataEnabled) BOOL memorySafeDataEnabled;

- (nonnull instancetype)init NS_UNAVAILABLE;
- (nonnull instancetype)initWithDataTask:(nonnull NSURLSessionDataTask *)dataTask;
Expand All @@ -49,4 +51,7 @@
*/
- (nullable NSData *)data;

/// YES if any data has been received. Equivalent to `[self data] != nil`, without copying.
@property (nonatomic, readonly) BOOL hasData;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@
/** Whether high quality images should be downloaded when a low quality image is cached if network connectivity has improved. */
@property (nonatomic, readwrite, assign) BOOL shouldUpgradeLowQualityImages;

/**
Opt-in switch for memory-safe handling of downloaded image data. Defaults to NO.

When YES, a finished download hands its accumulated buffer to the completion path instead of copying it, and
-[PINProgressiveImage data] returns nil instead of raising when Foundation fails to allocate the copy under
memory pressure.
*/
@property (nonatomic, readwrite, assign) BOOL isMemorySafeProgressiveDataEnabled;

@end
Loading
Loading