diff --git a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj index c7955c67..00624c40 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj @@ -30,10 +30,6 @@ 881B3747152253810079220B /* NSObject+RACFastEnumeration.m in Sources */ = {isa = PBXBuildFile; fileRef = 881B3745152253810079220B /* NSObject+RACFastEnumeration.m */; }; 881B37CC152260BF0079220B /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 881B37CA152260BF0079220B /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 881B37CD152260BF0079220B /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = 881B37CB152260BF0079220B /* RACUnit.m */; }; - 882036E31509677A002428D3 /* EXTNil.h in Headers */ = {isa = PBXBuildFile; fileRef = 882036DD1509677A002428D3 /* EXTNil.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 882036E41509677A002428D3 /* EXTNil.m in Sources */ = {isa = PBXBuildFile; fileRef = 882036DE1509677A002428D3 /* EXTNil.m */; }; - 882036E51509677A002428D3 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 882036DF1509677A002428D3 /* EXTRuntimeExtensions.h */; }; - 882036E61509677A002428D3 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 882036E01509677A002428D3 /* EXTRuntimeExtensions.m */; }; 8820370A15096A4F002428D3 /* RACMaybe.h in Headers */ = {isa = PBXBuildFile; fileRef = 8820370815096A4F002428D3 /* RACMaybe.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8820370B15096A4F002428D3 /* RACMaybe.m in Sources */ = {isa = PBXBuildFile; fileRef = 8820370915096A4F002428D3 /* RACMaybe.m */; }; 8820937C1501C8A600796685 /* RACSubscribableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8820937B1501C8A600796685 /* RACSubscribableSpec.m */; }; @@ -47,8 +43,6 @@ 8857BB7E152A2747009804CC /* NSObject+RACExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 8857BB7C152A2747009804CC /* NSObject+RACExtensions.h */; }; 8857BB7F152A2747009804CC /* NSObject+RACExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8857BB7D152A2747009804CC /* NSObject+RACExtensions.m */; }; 8857BB82152A27A9009804CC /* NSObject+RACKVOWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 8857BB81152A27A9009804CC /* NSObject+RACKVOWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 886044D8152A9693008ABD0D /* NSArray+EXTNilSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 886044D6152A9693008ABD0D /* NSArray+EXTNilSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 886044D9152A9693008ABD0D /* NSArray+EXTNilSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 886044D7152A9693008ABD0D /* NSArray+EXTNilSupport.m */; }; 8866784E1518D93E00DE77EC /* RACSubscribable+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = 8866784D1518D93D00DE77EC /* RACSubscribable+Operations.m */; }; 886678711518DCD800DE77EC /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */; }; 8867D5F9152BDAC300321BD5 /* RACSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = 8867D5F7152BDAC300321BD5 /* RACSwizzling.h */; }; @@ -64,6 +58,8 @@ 88977C861512EA7C00A09EC5 /* RACAsyncSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4AB481510F8F10011494F /* RACAsyncSubject.m */; }; 88977C871512EA7E00A09EC5 /* RACMaybe.m in Sources */ = {isa = PBXBuildFile; fileRef = 8820370915096A4F002428D3 /* RACMaybe.m */; }; 88ABAB631509A67C0073CDA2 /* RACAsyncFunctionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 88ABAB621509A5E40073CDA2 /* RACAsyncFunctionOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88B76F8E153726B00053EAE2 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B76F8C153726B00053EAE2 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88B76F8F153726B00053EAE2 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B76F8D153726B00053EAE2 /* RACTuple.m */; }; 88CBB39A1517A29F00DDC7D7 /* NSObject+RACSubscribable.h in Headers */ = {isa = PBXBuildFile; fileRef = 88CBB3981517A29E00DDC7D7 /* NSObject+RACSubscribable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88CBB39B1517A29F00DDC7D7 /* NSObject+RACSubscribable.m in Sources */ = {isa = PBXBuildFile; fileRef = 88CBB3991517A29E00DDC7D7 /* NSObject+RACSubscribable.m */; }; 88CDF7DE15000FCF00163A9F /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88CDF7DD15000FCF00163A9F /* SenTestingKit.framework */; }; @@ -75,7 +71,7 @@ 88D4AB4A1510F8F10011494F /* RACAsyncSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4AB481510F8F10011494F /* RACAsyncSubject.m */; }; 88DA309715071CBA00C19D0F /* RACValueTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 88DA309515071CBA00C19D0F /* RACValueTransformer.h */; settings = {ATTRIBUTES = (); }; }; 88DA309815071CBA00C19D0F /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DA309615071CBA00C19D0F /* RACValueTransformer.m */; }; - 88F5870215361BCD0084BD32 /* RACConnectableSubscribable.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F5870015361BCD0084BD32 /* RACConnectableSubscribable.h */; }; + 88F5870215361BCD0084BD32 /* RACConnectableSubscribable.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F5870015361BCD0084BD32 /* RACConnectableSubscribable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88F5870315361BCD0084BD32 /* RACConnectableSubscribable.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F5870115361BCD0084BD32 /* RACConnectableSubscribable.m */; }; 88F5870615361C170084BD32 /* RACConnectableSubscribable+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F5870515361C170084BD32 /* RACConnectableSubscribable+Private.h */; }; 88F70068152D2D7B00B32771 /* NSObject+RACBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F70066152D2D7B00B32771 /* NSObject+RACBindings.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -185,10 +181,6 @@ 881B3745152253810079220B /* NSObject+RACFastEnumeration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACFastEnumeration.m"; sourceTree = ""; }; 881B37CA152260BF0079220B /* RACUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnit.h; sourceTree = ""; }; 881B37CB152260BF0079220B /* RACUnit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnit.m; sourceTree = ""; }; - 882036DD1509677A002428D3 /* EXTNil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTNil.h; sourceTree = ""; }; - 882036DE1509677A002428D3 /* EXTNil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTNil.m; sourceTree = ""; }; - 882036DF1509677A002428D3 /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTRuntimeExtensions.h; sourceTree = ""; }; - 882036E01509677A002428D3 /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTRuntimeExtensions.m; sourceTree = ""; }; 8820370815096A4F002428D3 /* RACMaybe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACMaybe.h; sourceTree = ""; }; 8820370915096A4F002428D3 /* RACMaybe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMaybe.m; sourceTree = ""; }; 8820937B1501C8A600796685 /* RACSubscribableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscribableSpec.m; sourceTree = ""; }; @@ -205,8 +197,6 @@ 8857BB7C152A2747009804CC /* NSObject+RACExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACExtensions.h"; sourceTree = ""; }; 8857BB7D152A2747009804CC /* NSObject+RACExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACExtensions.m"; sourceTree = ""; }; 8857BB81152A27A9009804CC /* NSObject+RACKVOWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACKVOWrapper.h"; sourceTree = ""; }; - 886044D6152A9693008ABD0D /* NSArray+EXTNilSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+EXTNilSupport.h"; sourceTree = ""; }; - 886044D7152A9693008ABD0D /* NSArray+EXTNilSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+EXTNilSupport.m"; sourceTree = ""; }; 8866784D1518D93D00DE77EC /* RACSubscribable+Operations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSubscribable+Operations.m"; sourceTree = ""; }; 886678701518DCD800DE77EC /* NSObject+RACPropertySubscribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACPropertySubscribing.m"; sourceTree = ""; }; 8867D5F7152BDAC300321BD5 /* RACSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSwizzling.h; sourceTree = ""; }; @@ -217,6 +207,8 @@ 88977C58151296D600A09EC5 /* RACSubscribable+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RACSubscribable+Private.h"; sourceTree = ""; }; 88977C7E1512E69200A09EC5 /* RACSubscribable+Operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSubscribable+Operations.h"; sourceTree = ""; }; 88ABAB621509A5E40073CDA2 /* RACAsyncFunctionOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACAsyncFunctionOperation.h; sourceTree = ""; }; + 88B76F8C153726B00053EAE2 /* RACTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTuple.h; sourceTree = ""; }; + 88B76F8D153726B00053EAE2 /* RACTuple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTuple.m; sourceTree = ""; }; 88CBB3981517A29E00DDC7D7 /* NSObject+RACSubscribable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACSubscribable.h"; sourceTree = ""; }; 88CBB3991517A29E00DDC7D7 /* NSObject+RACSubscribable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACSubscribable.m"; sourceTree = ""; }; 88CDF7BF15000FCE00163A9F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -298,25 +290,11 @@ name = Products; sourceTree = ""; }; - 882036DA1509677A002428D3 /* libextobjc */ = { - isa = PBXGroup; - children = ( - 882036DD1509677A002428D3 /* EXTNil.h */, - 882036DE1509677A002428D3 /* EXTNil.m */, - 882036DF1509677A002428D3 /* EXTRuntimeExtensions.h */, - 882036E01509677A002428D3 /* EXTRuntimeExtensions.m */, - ); - name = libextobjc; - path = ../../external/libextobjc; - sourceTree = ""; - }; 886044CC152A95B5008ABD0D /* Extensions */ = { isa = PBXGroup; children = ( 8857BB7C152A2747009804CC /* NSObject+RACExtensions.h */, 8857BB7D152A2747009804CC /* NSObject+RACExtensions.m */, - 886044D6152A9693008ABD0D /* NSArray+EXTNilSupport.h */, - 886044D7152A9693008ABD0D /* NSArray+EXTNilSupport.m */, 8867D5F7152BDAC300321BD5 /* RACSwizzling.h */, 8867D5F8152BDAC300321BD5 /* RACSwizzling.m */, 886DCD84152EA272004A666F /* NSObject+RACOperations.h */, @@ -395,7 +373,6 @@ 88CDF7C615000FCE00163A9F /* Supporting Files */ = { isa = PBXGroup; children = ( - 882036DA1509677A002428D3 /* libextobjc */, 88CDF7C715000FCE00163A9F /* ReactiveCocoa-Info.plist */, 88CDF7C815000FCE00163A9F /* InfoPlist.strings */, 88CDF7CD15000FCE00163A9F /* ReactiveCocoa-Prefix.pch */, @@ -463,6 +440,8 @@ 881B3745152253810079220B /* NSObject+RACFastEnumeration.m */, 881B37CA152260BF0079220B /* RACUnit.h */, 881B37CB152260BF0079220B /* RACUnit.m */, + 88B76F8C153726B00053EAE2 /* RACTuple.h */, + 88B76F8D153726B00053EAE2 /* RACTuple.m */, ); name = Core; sourceTree = ""; @@ -496,7 +475,6 @@ 88D4AB3E1510F6C30011494F /* RACReplaySubject.h in Headers */, 88D4AB491510F8F10011494F /* RACAsyncSubject.h in Headers */, 883A84DA1513964B006DB4C7 /* RACBehaviorSubject.h in Headers */, - 882036E31509677A002428D3 /* EXTNil.h in Headers */, 883A84DF1513B5EC006DB4C7 /* RACDisposable.h in Headers */, 88CBB39A1517A29F00DDC7D7 /* NSObject+RACSubscribable.h in Headers */, 88977C801512E69200A09EC5 /* RACSubscribable+Operations.h in Headers */, @@ -504,14 +482,13 @@ 884476E4152367D100958F44 /* RACScopedDisposable.h in Headers */, 881B3746152253810079220B /* NSObject+RACFastEnumeration.h in Headers */, 88F70068152D2D7B00B32771 /* NSObject+RACBindings.h in Headers */, - 886044D8152A9693008ABD0D /* NSArray+EXTNilSupport.h in Headers */, 8857BB82152A27A9009804CC /* NSObject+RACKVOWrapper.h in Headers */, 886DCD86152EA272004A666F /* NSObject+RACOperations.h in Headers */, - 882036E51509677A002428D3 /* EXTRuntimeExtensions.h in Headers */, + 88F5870215361BCD0084BD32 /* RACConnectableSubscribable.h in Headers */, + 88B76F8E153726B00053EAE2 /* RACTuple.h in Headers */, 88DA309715071CBA00C19D0F /* RACValueTransformer.h in Headers */, 8857BB7E152A2747009804CC /* NSObject+RACExtensions.h in Headers */, 8867D5F9152BDAC300321BD5 /* RACSwizzling.h in Headers */, - 88F5870215361BCD0084BD32 /* RACConnectableSubscribable.h in Headers */, 88F5870615361C170084BD32 /* RACConnectableSubscribable+Private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -700,8 +677,6 @@ 88037FCD1505648C001A5B19 /* NSButton+RACCommandSupport.m in Sources */, 88037FCF1505648C001A5B19 /* RACAsyncCommand.m in Sources */, 88DA309815071CBA00C19D0F /* RACValueTransformer.m in Sources */, - 882036E41509677A002428D3 /* EXTNil.m in Sources */, - 882036E61509677A002428D3 /* EXTRuntimeExtensions.m in Sources */, 8820370B15096A4F002428D3 /* RACMaybe.m in Sources */, 880B9177150B09190008488E /* RACSubject.m in Sources */, 88D4AB3F1510F6C30011494F /* RACReplaySubject.m in Sources */, @@ -716,11 +691,11 @@ 881B37CD152260BF0079220B /* RACUnit.m in Sources */, 884476E5152367D100958F44 /* RACScopedDisposable.m in Sources */, 8857BB7F152A2747009804CC /* NSObject+RACExtensions.m in Sources */, - 886044D9152A9693008ABD0D /* NSArray+EXTNilSupport.m in Sources */, 8867D5FA152BDAC300321BD5 /* RACSwizzling.m in Sources */, 88F70069152D2D7B00B32771 /* NSObject+RACBindings.m in Sources */, 886DCD87152EA272004A666F /* NSObject+RACOperations.m in Sources */, 88F5870315361BCD0084BD32 /* RACConnectableSubscribable.m in Sources */, + 88B76F8F153726B00053EAE2 /* RACTuple.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.h b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.h deleted file mode 100644 index 67adce00..00000000 --- a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// NSArray+EXTNilSupport.h -// ReactiveCocoa -// -// Created by Josh Abernathy on 4/2/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import - - -@interface NSArray (EXTNilSupport) - -- (id)rac_objectOrNilAtIndex:(NSUInteger)index; - -@end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.m b/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.m deleted file mode 100644 index 1a0deec0..00000000 --- a/ReactiveCocoaFramework/ReactiveCocoa/NSArray+EXTNilSupport.m +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSArray+EXTNilSupport.m -// ReactiveCocoa -// -// Created by Josh Abernathy on 4/2/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "NSArray+EXTNilSupport.h" -#import "EXTNil.h" - - -@implementation NSArray (EXTNilSupport) - -- (id)rac_objectOrNilAtIndex:(NSUInteger)index { - id object = [self objectAtIndex:index]; - return [object isKindOfClass:[EXTNil class]] ? nil : object; -} - -@end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.h b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.h index 472b6ed4..238ae6d3 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.h +++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.h @@ -9,10 +9,11 @@ #import @class RACSubscribable; +@class RACTuple; @interface NSObject (RACOperations) -- (RACSubscribable *)rac_whenAny:(NSArray *)keyPaths reduce:(id (^)(NSArray *xs))reduceBlock; +- (RACSubscribable *)rac_whenAny:(NSArray *)keyPaths reduce:(id (^)(RACTuple *xs))reduceBlock; @end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.m b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.m index f4aa4f2a..ef191423 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.m +++ b/ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACOperations.m @@ -11,14 +11,14 @@ #import "NSObject+RACSubscribable.h" #import "RACSubscribable.h" #import "RACSubscribable+Operations.h" -#import "EXTNil.h" #import "RACSubscriber.h" #import "NSObject+RACFastEnumeration.h" +#import "RACTuple.h" @implementation NSObject (RACOperations) -- (RACSubscribable *)rac_whenAny:(NSArray *)keyPaths reduce:(id (^)(NSArray *xs))reduceBlock { +- (RACSubscribable *)rac_whenAny:(NSArray *)keyPaths reduce:(id (^)(RACTuple *xs))reduceBlock { NSParameterAssert(keyPaths != nil); NSParameterAssert(reduceBlock != NULL); @@ -26,13 +26,13 @@ return [RACSubscribable createSubscribable:^RACDisposable *(id observer) { NSObject *strongSelf = weakSelf; - NSArray * (^currentValues)(void) = ^{ + RACTuple * (^currentValues)(void) = ^{ NSMutableArray *values = [NSMutableArray arrayWithCapacity:keyPaths.count]; for(NSString *keyPath in keyPaths) { - [values addObject:[strongSelf valueForKeyPath:keyPath] ? : [EXTNil null]]; + [values addObject:[strongSelf valueForKeyPath:keyPath] ? : [NSNull null]]; } - return values; + return [RACTuple tupleWithObjectsFromArray:values]; }; [observer sendNext:reduceBlock(currentValues())]; diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACMaybe.m b/ReactiveCocoaFramework/ReactiveCocoa/RACMaybe.m index 42b12c70..ea59bb71 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/RACMaybe.m +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACMaybe.m @@ -7,7 +7,7 @@ // #import "RACMaybe.h" -#import "EXTNil.h" +#import "RACTuple.h" @interface RACMaybe () @property (nonatomic, strong) id object; @@ -25,7 +25,7 @@ + (id)maybeWithObject:(id)object { RACMaybe *maybe = [[self alloc] init]; - maybe.object = object ? : [EXTNil null]; + maybe.object = object ? : [RACTupleNil tupleNil]; return maybe; } @@ -43,4 +43,8 @@ return self.error != nil; } +- (id)object { + return object == [RACTupleNil tupleNil] ? nil : object; +} + @end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m index ae7a2a8d..3d870298 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m @@ -7,7 +7,6 @@ // #import "RACReplaySubject.h" -#import "EXTNil.h" #import "RACSubscriber.h" @interface RACReplaySubject () @@ -33,7 +32,7 @@ - (RACDisposable *)subscribe:(id)observer { RACDisposable * disposable = [super subscribe:observer]; for(id value in self.valuesReceived) { - [observer sendNext:value]; + [observer sendNext:[value isKindOfClass:[NSNull class]] ? nil : value]; } return disposable; @@ -45,7 +44,7 @@ - (void)sendNext:(id)value { [super sendNext:value]; - [self.valuesReceived addObject:value ? : [EXTNil null]]; + [self.valuesReceived addObject:value ? : [NSNull null]]; while(self.valuesReceived.count > self.capacity) { [self.valuesReceived removeObjectAtIndex:0]; diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.h b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.h index 96c41c32..6acc2af1 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.h +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.h @@ -8,6 +8,7 @@ #import "RACSubscribable.h" +@class RACTuple; @class RACConnectableSubscribable; @@ -37,17 +38,17 @@ // Divide the `next`s of the subscribable into windows. When `openSubscribable` sends a next, a window is opened and the `closeBlock` is asked for a close subscribable. The window is closed when the close subscribable sends a `next`. - (instancetype)windowWithStart:(id)openSubscribable close:(id (^)(id start))closeBlock; -// Divide the `next`s into buffers with `bufferCount` items each. +// Divide the `next`s into buffers with `bufferCount` items each. The `next` will be a RACTuple of values. - (instancetype)buffer:(NSUInteger)bufferCount; -// Divide the `next`s into buffers delivery every `interval` seconds. +// Divide the `next`s into buffers delivery every `interval` seconds. The `next` will be a RACTuple of values. - (instancetype)bufferWithTime:(NSTimeInterval)interval; // Take `count` `next`s and then completes. - (instancetype)take:(NSUInteger)count; // Combine the latest values from each of the subscribables once all the subscribables have sent a `next`. -+ (instancetype)combineLatest:(NSArray *)observables reduce:(id (^)(NSArray *xs))reduceBlock; ++ (instancetype)combineLatest:(NSArray *)observables reduce:(id (^)(RACTuple *xs))reduceBlock; // Sends a `+[RACUnit defaultUnit]` when all the subscribables have sent a `next`. + (instancetype)whenAll:(NSArray *)observables; @@ -109,7 +110,7 @@ // The source must be a subscribable of subscribables. Subscribe and send `next`s for the latest subscribable. This is mostly useful when combined with `-selectMany:`. - (instancetype)switch; -// Add every `next` to an array. Note that this is a blocking call. +// Add every `next` to an array. Nils are represented by NSNulls. Note that this is a blocking call. - (NSArray *)toArray; // Creates and returns a connectable subscribable. This allows you to share 1 single subscription to the underlying subscribable. diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.m b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.m index 1598d1e4..912d26cd 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.m +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACSubscribable+Operations.m @@ -12,10 +12,10 @@ #import "NSObject+RACExtensions.h" #import "RACBehaviorSubject.h" #import "RACDisposable.h" -#import "EXTNil.h" #import "RACUnit.h" #import "RACMaybe.h" #import "RACConnectableSubscribable+Private.h" +#import "RACTuple.h" @implementation RACSubscribable (Operations) @@ -275,13 +275,13 @@ __block RACDisposable *innerDisposable = nil; RACDisposable *outerDisposable = [[self windowWithStart:windowOpenSubject close:^(id start) { return [[[RACSubscribable interval:interval] take:1] doNext:^(id x) { - [observer sendNext:values]; + [observer sendNext:[RACTuple tupleWithObjectsFromArray:values]]; [values removeAllObjects]; [windowOpenSubject sendNext:[RACUnit defaultUnit]]; }]; }] subscribeNext:^(id x) { innerDisposable = [x subscribeNext:^(id x) { - [values addObject:x ? : [EXTNil null]]; + [values addObject:x ? : [RACTupleNil tupleNil]]; }]; } error:^(NSError *error) { [observer sendError:error]; @@ -314,7 +314,7 @@ }]; } -+ (instancetype)combineLatest:(NSArray *)observables reduce:(id (^)(NSArray *xs))reduceBlock { ++ (instancetype)combineLatest:(NSArray *)observables reduce:(id (^)(RACTuple *xs))reduceBlock { NSParameterAssert(reduceBlock != NULL); return [RACSubscribable createSubscribable:^(id observer) { @@ -323,7 +323,7 @@ NSMutableDictionary *lastValues = [NSMutableDictionary dictionaryWithCapacity:observables.count]; for(id observable in observables) { RACDisposable *disposable = [observable subscribe:[RACSubscriber subscriberWithNext:^(id x) { - [lastValues setObject:x ? : [EXTNil null] forKey:[NSString stringWithFormat:@"%p", observable]]; + [lastValues setObject:x ? : [NSNull null] forKey:[NSString stringWithFormat:@"%p", observable]]; if(lastValues.count == observables.count) { NSMutableArray *orderedValues = [NSMutableArray arrayWithCapacity:observables.count]; @@ -331,7 +331,7 @@ [orderedValues addObject:[lastValues objectForKey:[NSString stringWithFormat:@"%p", o]]]; } - [observer sendNext:reduceBlock(orderedValues)]; + [observer sendNext:reduceBlock([RACTuple tupleWithObjectsFromArray:orderedValues])]; } } error:^(NSError *error) { [observer sendError:error]; @@ -356,7 +356,7 @@ } + (instancetype)whenAll:(NSArray *)observables { - return [self combineLatest:observables reduce:^(NSArray *xs) { return [RACUnit defaultUnit]; }]; + return [self combineLatest:observables reduce:^(RACTuple *xs) { return [RACUnit defaultUnit]; }]; } + (instancetype)merge:(NSArray *)observables { @@ -679,7 +679,7 @@ NSMutableArray *values = [NSMutableArray array]; __block BOOL stop = NO; [self subscribeNext:^(id x) { - [values addObject:x ? : [EXTNil null]]; + [values addObject:x ? : [NSNull null]]; } error:^(NSError *error) { stop = YES; } completed:^{ diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h new file mode 100644 index 00000000..babc6c68 --- /dev/null +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.h @@ -0,0 +1,30 @@ +// +// RACTuple.h +// ReactiveCocoa +// +// Created by Josh Abernathy on 4/12/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface RACTupleNil : NSObject ++ (RACTupleNil *)tupleNil; +@end + + +// A tuple is an ordered collection of objects. It may contain nils, represented by RACTupleNil. +@interface RACTuple : NSObject + +@property (nonatomic, readonly) NSUInteger count; + ++ (id)tupleWithObjectsFromArray:(NSArray *)array; ++ (id)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION; + +// Returns the object at `index` or nil if the object is a RACTupleNil. +- (id)objectAtIndex:(NSUInteger)index; + +- (NSArray *)allObjects; + +@end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m new file mode 100644 index 00000000..6b1ef5f7 --- /dev/null +++ b/ReactiveCocoaFramework/ReactiveCocoa/RACTuple.m @@ -0,0 +1,99 @@ +// +// RACTuple.m +// ReactiveCocoa +// +// Created by Josh Abernathy on 4/12/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "RACTuple.h" + + +@implementation RACTupleNil + ++ (RACTupleNil *)tupleNil { + static dispatch_once_t onceToken; + static RACTupleNil *tupleNil = nil; + dispatch_once(&onceToken, ^{ + tupleNil = [[self alloc] init]; + }); + + return tupleNil; +} + +@end + + +@interface RACTuple () +@property (nonatomic, strong) NSArray *backingArray; +@end + + +@implementation RACTuple + +- (id)init { + self = [super init]; + if(self == nil) return nil; + + self.backingArray = [NSArray array]; + + return self; +} + + +#pragma mark NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { + return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len]; +} + + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone *)zone { + // we're immutable, bitches! + return self; +} + + +#pragma mark API + +@synthesize backingArray; + ++ (id)tupleWithObjectsFromArray:(NSArray *)array { + RACTuple *tuple = [[self alloc] init]; + tuple.backingArray = [array copy]; + return tuple; +} + ++ (id)tupleWithObjects:(id)object, ... { + RACTuple *tuple = [[self alloc] init]; + + NSMutableArray *objects = [NSMutableArray array]; + + va_list args; + va_start(args, object); + for(id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) { + [objects addObject:currentObject]; + } + va_end(args); + + tuple.backingArray = [objects copy]; + + return tuple; +} + +- (id)objectAtIndex:(NSUInteger)index { + id object = [self.backingArray objectAtIndex:index]; + return object == [RACTupleNil tupleNil] ? nil : object; +} + +- (NSArray *)allObjects { + return self.backingArray; +} + +- (NSUInteger)count { + return self.backingArray.count; +} + +@end diff --git a/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h index 99378a2a..e2278e4b 100644 --- a/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h +++ b/ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h @@ -14,7 +14,6 @@ #import #import #import -#import #import #import #import @@ -28,3 +27,4 @@ #import #import #import +#import diff --git a/external/libextobjc/EXTNil.h b/external/libextobjc/EXTNil.h deleted file mode 100644 index bc20c941..00000000 --- a/external/libextobjc/EXTNil.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// EXTNil.h -// extobjc -// -// Created by Justin Spahr-Summers on 2011-04-25. -// Released into the public domain. -// - -#import - -/** - * Like \c NSNull, this class provides a singleton object that can be used to - * represent a \c NULL or \c nil value. Unlike \c NSNull, this object behaves - * more similarly to a \c nil object, responding to messages with "zero" values. - * This eliminates the need for \c NSNull class or equality checks with - * collections that need to contain null values. - * - * This class will pretend to be \c NSNull when queried for its class or - * compared for equality, to keep compatibility with code that expects or uses - * \c NSNull. - * - * @note Because this class does still behave like an object in some ways, it - * will respond to certain \c NSObject protocol methods where an actually \c nil - * object would not. - */ -@interface EXTNil : NSProxy { - -} - -/** - * Returns the singleton \c EXTNil instance. This naming matches that of \c - * NSNull -- \c nil as a method name is unusable because it is a language - * keyword. - */ -+ (id)null; - -@end diff --git a/external/libextobjc/EXTNil.m b/external/libextobjc/EXTNil.m deleted file mode 100644 index 46fcec77..00000000 --- a/external/libextobjc/EXTNil.m +++ /dev/null @@ -1,89 +0,0 @@ -// -// EXTNil.m -// extobjc -// -// Created by Justin Spahr-Summers on 2011-04-25. -// Released into the public domain. -// - -#import "EXTNil.h" -#import "EXTRuntimeExtensions.h" - -static id singleton = nil; - -@implementation EXTNil -+ (void)initialize { - if (self == [EXTNil class]) { - if (!singleton) - singleton = [self alloc]; - } -} - -+ (EXTNil *)null { - return singleton; -} - -- (id)init { - return self; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - return self; -} - -#pragma mark Forwarding machinery - -- (void)forwardInvocation:(NSInvocation *)anInvocation { - NSUInteger returnLength = [[anInvocation methodSignature] methodReturnLength]; - if (!returnLength) { - // nothing to do - return; - } - - // set return value to all zero bits - char buffer[returnLength]; - memset(buffer, 0, returnLength); - - [anInvocation setReturnValue:buffer]; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { - return ext_globalMethodSignatureForSelector(selector); -} - -- (BOOL)respondsToSelector:(SEL)selector { - // behave like nil - return NO; -} - -#pragma mark NSObject protocol - -- (BOOL)conformsToProtocol:(Protocol *)aProtocol { - return NO; -} - -- (NSUInteger)hash { - return 0; -} - -- (BOOL)isEqual:(id)obj { - return !obj || obj == self || [obj isEqual:[NSNull null]]; -} - -- (BOOL)isKindOfClass:(Class)class { - return [class isEqual:[EXTNil class]] || [class isEqual:[NSNull class]]; -} - -- (BOOL)isMemberOfClass:(Class)class { - return [class isEqual:[EXTNil class]] || [class isEqual:[NSNull class]]; -} - -- (BOOL)isProxy { - // not really a proxy -- we just inherit from NSProxy because it makes - // method signature lookup simpler - return NO; -} - -@end diff --git a/external/libextobjc/EXTRuntimeExtensions.h b/external/libextobjc/EXTRuntimeExtensions.h deleted file mode 100644 index 0821e5b5..00000000 --- a/external/libextobjc/EXTRuntimeExtensions.h +++ /dev/null @@ -1,372 +0,0 @@ -// -// EXTRuntimeExtensions.h -// extobjc -// -// Created by Justin Spahr-Summers on 2011-03-05. -// Released into the public domain. -// - -#import - -/** - * A callback indicating that the given method failed to be added to the given - * class. The reason for the failure depends on the attempted task. - */ -typedef void (*ext_failedMethodCallback)(Class, Method); - -/** - * Used with #ext_injectMethods to determine injection behavior. - */ -typedef enum { - /** - * Indicates that any existing methods on the destination class should be - * overwritten. - */ - ext_methodInjectionReplace = 0x00, - - /** - * Avoid overwriting methods on the immediate destination class. - */ - ext_methodInjectionFailOnExisting = 0x01, - - /** - * Avoid overriding methods implemented in any superclass of the destination - * class. - */ - ext_methodInjectionFailOnSuperclassExisting = 0x02, - - /** - * Avoid overwriting methods implemented in the immediate destination class - * or any superclass. This is equivalent to - * ext_methodInjectionFailOnExisting | ext_methodInjectionFailOnSuperclassExisting. - */ - ext_methodInjectionFailOnAnyExisting = 0x03, - - /** - * Ignore the \c +load class method. This does not affect instance method - * injection. - */ - ext_methodInjectionIgnoreLoad = 1U << 2, - - /** - * Ignore the \c +initialize class method. This does not affect instance method - * injection. - */ - ext_methodInjectionIgnoreInitialize = 1U << 3 -} ext_methodInjectionBehavior; - -/** - * A mask for the overwriting behavior flags of #ext_methodInjectionBehavior. - */ -static const ext_methodInjectionBehavior ext_methodInjectionOverwriteBehaviorMask = 0x3; - -/** - * Describes the memory management policy of a property. - */ -typedef enum { - /** - * The value is assigned. - */ - ext_propertyMemoryManagementPolicyAssign = 0, - - /** - * The value is retained. - */ - ext_propertyMemoryManagementPolicyRetain, - - /** - * The value is copied. - */ - ext_propertyMemoryManagementPolicyCopy -} ext_propertyMemoryManagementPolicy; - -/** - * Describes the attributes and type information of a property. - */ -typedef struct { - /** - * Whether this property was declared with the \c readonly attribute. - */ - BOOL readonly; - - /** - * Whether this property was declared with the \c nonatomic attribute. - */ - BOOL nonatomic; - - /** - * Whether the property is a weak reference. - */ - BOOL weak; - - /** - * Whether the property is eligible for garbage collection. - */ - BOOL canBeCollected; - - /** - * Whether this property is defined with \c \@dynamic. - */ - BOOL dynamic; - - /** - * The memory management policy for this property. This will always be - * #ext_propertyMemoryManagementPolicyAssign if #readonly is \c YES. - */ - ext_propertyMemoryManagementPolicy memoryManagementPolicy; - - /** - * The selector for the getter of this property. This will reflect any - * custom \c getter= attribute provided in the property declaration, or the - * inferred getter name otherwise. - */ - SEL getter; - - /** - * The selector for the setter of this property. This will reflect any - * custom \c setter= attribute provided in the property declaration, or the - * inferred setter name otherwise. - * - * @note If #readonly is \c YES, this value will represent what the setter - * \e would be, if the property were writable. - */ - SEL setter; - - /** - * The backing instance variable for this property, or \c NULL if \c - * \c @synthesize was not used, and therefore no instance variable exists. This - * would also be the case if the property is implemented dynamically. - */ - const char *ivar; - - /** - * If this property is defined as being an instance of a specific class, - * this will be the class object representing it. - * - * This will be \c nil if the property was defined as type \c id, if the - * property is not of an object type, or if the class could not be found at - * runtime. - */ - Class objectClass; - - /** - * The type encoding for the value of this property. This is the type as it - * would be returned by the \c \@encode() directive. - */ - char type[]; -} ext_propertyAttributes; - -/** - * Iterates through the first \a count entries in \a methods and attempts to add - * each one to \a aClass. If a method by the same name already exists on \a - * aClass, it is \e not overridden. If \a checkSuperclasses is \c YES, and - * a method by the same name already exists on any superclass of \a aClass, it - * is not overridden. - * - * Returns the number of methods added successfully. For each method that fails - * to be added, \a failedToAddCallback (if provided) is invoked. - */ -unsigned ext_addMethods (Class aClass, Method *methods, unsigned count, BOOL checkSuperclasses, ext_failedMethodCallback failedToAddCallback); - -/** - * Iterates through all instance and class methods of \a srcClass and attempts - * to add each one to \a dstClass. If a method by the same name already exists - * on \a aClass, it is \e not overridden. If \a checkSuperclasses is \c YES, and - * a method by the same name already exists on any superclass of \a aClass, it - * is not overridden. - * - * Returns whether all methods were added successfully. For each method that fails - * to be added, \a failedToAddCallback (if provided) is invoked. - * - * @note This ignores any \c +load method on \a srcClass. \a srcClass and \a - * dstClass must not be metaclasses. - */ -BOOL ext_addMethodsFromClass (Class srcClass, Class dstClass, BOOL checkSuperclasses, ext_failedMethodCallback failedToAddCallback); - -/** - * Returns the superclass of \a receiver which immediately descends from \a - * superclass. If \a superclass is not in the hierarchy of \a receiver, or is - * equal to \a receiver, \c nil is returned. - */ -Class ext_classBeforeSuperclass (Class receiver, Class superclass); - -/** - * Returns whether \a receiver is \a aClass, or inherits directly from it. - */ -BOOL ext_classIsKindOfClass (Class receiver, Class aClass); - -/** - * Returns the full list of classes registered with the runtime, terminated with - * \c NULL. If \a count is not \c NULL, it is filled in with the total number of - * classes returned. You must \c free() the returned array. - */ -Class *ext_copyClassList (unsigned *count); - -/** - * Looks through the complete list of classes registered with the runtime and - * finds all classes which conform to \a protocol. Returns \c *count classes - * terminated by a \c NULL. You must \c free() the returned array. If there are no - * classes conforming to \a protocol, \c NULL is returned. - * - * @note \a count may be \c NULL. - */ -Class *ext_copyClassListConformingToProtocol (Protocol *protocol, unsigned *count); - -/** - * Returns a pointer to a structure containing information about \a property. - * You must \c free() the returned pointer. Returns \c NULL if there is an error - * obtaining information from \a property. - */ -ext_propertyAttributes *ext_copyPropertyAttributes (objc_property_t property); - -/** - * Looks through the complete list of classes registered with the runtime and - * finds all classes which are descendant from \a aClass. Returns \c - * *subclassCount classes terminated by a \c NULL. You must \c free() the - * returned array. If there are no subclasses of \a aClass, \c NULL is - * returned. - * - * @note \a subclassCount may be \c NULL. \a aClass may be a metaclass to get - * all subclass metaclass objects. - */ -Class *ext_copySubclassList (Class aClass, unsigned *subclassCount); - -/** - * Finds the instance method named \a aSelector on \a aClass and returns it, or - * returns \c NULL if no such instance method exists. Unlike \c - * class_getInstanceMethod(), this does not search superclasses. - * - * @note To get class methods in this manner, use a metaclass for \a aClass. - */ -Method ext_getImmediateInstanceMethod (Class aClass, SEL aSelector); - -/** - * Returns the value of \c Ivar \a IVAR from instance \a OBJ. The instance - * variable must be of type \a TYPE, and is returned as such. - * - * @warning Depending on the platform, this may or may not work with aggregate - * or floating-point types. - */ -#define ext_getIvar(OBJ, IVAR, TYPE) \ - ((TYPE (*)(id, Ivar)object_getIvar)((OBJ), (IVAR))) - -/** - * Returns the value of the instance variable identified by the string \a NAME - * from instance \a OBJ. The instance variable must be of type \a TYPE, and is - * returned as such. - * - * @note \a OBJ is evaluated twice. - * - * @warning Depending on the platform, this may or may not work with aggregate - * or floating-point types. - */ -#define ext_getIvarByName(OBJ, NAME, TYPE) \ - ext_getIvar((OBJ), class_getInstanceVariable(object_getClass((OBJ)), (NAME)), TYPE) - -/** - * Returns the accessor methods for \a property, as implemented in \a aClass or - * any of its superclasses. The getter, if implemented, is returned in \a - * getter, and the setter, if implemented, is returned in \a setter. If either - * \a getter or \a setter are \c NULL, that accessor is not returned. If either - * accessor is not implemented, the argument is left unmodified. - * - * Returns \c YES if a valid accessor was found, or \c NO if \a aClass and its - * superclasses do not implement \a property or if an error occurs. - */ -BOOL ext_getPropertyAccessorsForClass (objc_property_t property, Class aClass, Method *getter, Method *setter); - -/** - * For all classes registered with the runtime, invokes \c - * methodSignatureForSelector: and \c instanceMethodSignatureForSelector: to - * determine a method signature for \a aSelector. If one or more valid - * signatures is found, the first one is returned. If no valid signatures were - * found, \c nil is returned. - */ -NSMethodSignature *ext_globalMethodSignatureForSelector (SEL aSelector); - -/** - * Highly-configurable method injection. Adds the first \a count entries from \a - * methods into \a aClass according to \a behavior. - * - * Returns the number of methods added successfully. For each method that fails - * to be added, \a failedToAddCallback (if provided) is invoked. - * - * @note \c +load and \c +initialize methods are included in the number of - * successful methods when ignored for injection. - */ -unsigned ext_injectMethods (Class aClass, Method *methods, unsigned count, ext_methodInjectionBehavior behavior, ext_failedMethodCallback failedToAddCallback); - -/** - * Invokes #ext_injectMethods with the instance methods and class methods from - * \a srcClass. #ext_methodInjectionIgnoreLoad is added to #behavior for class - * method injection. - * - * Returns whether all methods were added successfully. For each method that fails - * to be added, \a failedToAddCallback (if provided) is invoked. - * - * @note \c +load and \c +initialize methods are considered to be added - * successfully when ignored for injection. - */ -BOOL ext_injectMethodsFromClass (Class srcClass, Class dstClass, ext_methodInjectionBehavior behavior, ext_failedMethodCallback failedToAddCallback); - -/** - * Loads a "special protocol" into an internal list. A special protocol is any - * protocol for which implementing classes need injection behavior (i.e., any - * class conforming to the protocol needs to be reflected upon). Returns \c NO - * if loading failed. - * - * Using this facility proceeds as follows: - * - * @li Each protocol is loaded with #ext_loadSpecialProtocol and a custom block - * that describes its injection behavior on each conforming class. - * @li Each protocol is marked as being ready for injection with - * #ext_specialProtocolReadyForInjection. - * @li The entire Objective-C class list is retrieved, and each special - * protocol's \a injectionBehavior block is run for all conforming classes. - * - * It is an error to call this function without later calling - * #ext_specialProtocolReadyForInjection as well. - * - * @note A special protocol X which conforms to another special protocol Y is - * always injected \e after Y. - */ -BOOL ext_loadSpecialProtocol (Protocol *protocol, void (^injectionBehavior)(Class destinationClass)); - -/** - * Marks a special protocol as being ready for injection. Injection is actually - * performed only after all special protocols have been marked in this way. - * - * @sa ext_loadSpecialProtocol - */ -void ext_specialProtocolReadyForInjection (Protocol *protocol); - -/** - * "Removes" any instance method matching \a methodName from \a aClass. This - * removal can mean one of two things: - * - * @li If any superclass of \a aClass implements a method by the same name, the - * implementation of the closest such superclass is used. - * @li If no superclasses of \a aClass implement a method by the same name, the - * method is replaced with an implementation internal to the runtime, used for - * message forwarding. - * - * @warning Adding a method by the same name into a superclass of \a aClass \e - * after using this function may obscure it from the subclass. - */ -void ext_removeMethod (Class aClass, SEL methodName); - -/** - * Iterates through the first \a count entries in \a methods and adds each one - * to \a aClass, replacing any existing implementation. - */ -void ext_replaceMethods (Class aClass, Method *methods, unsigned count); - -/** - * Iterates through all instance and class methods of \a srcClass and adds each - * one to \a dstClass, replacing any existing implementation. - * - * @note This ignores any \c +load method on \a srcClass. \a srcClass and \a - * dstClass must not be metaclasses. - */ -void ext_replaceMethodsFromClass (Class srcClass, Class dstClass); - diff --git a/external/libextobjc/EXTRuntimeExtensions.m b/external/libextobjc/EXTRuntimeExtensions.m deleted file mode 100644 index a8ce3744..00000000 --- a/external/libextobjc/EXTRuntimeExtensions.m +++ /dev/null @@ -1,932 +0,0 @@ -// -// EXTRuntimeExtensions.m -// extobjc -// -// Created by Justin Spahr-Summers on 2011-03-05. -// Released into the public domain. -// - -#import "EXTRuntimeExtensions.h" -#import -#import -#import -#import -#import -#import -#import - -typedef NSMethodSignature *(*methodSignatureForSelectorIMP)(id, SEL, SEL); -typedef void (^ext_specialProtocolInjectionBlock)(Class); - -// contains the information needed to reference a full special protocol -typedef struct { - // the actual protocol declaration (@protocol block) - __unsafe_unretained Protocol *protocol; - - // the injection block associated with this protocol - // - // this block is RETAINED and must eventually be released by transferring it - // back to ARC - void *injectionBlock; - - // whether this protocol is ready to be injected to its conforming classes - // - // this does NOT refer to a special protocol having been injected already - BOOL ready; -} EXTSpecialProtocol; - -// the full list of special protocols (an array of EXTSpecialProtocol structs) -static EXTSpecialProtocol * restrict specialProtocols = NULL; - -// the number of special protocols stored in the array -static size_t specialProtocolCount = 0; - -// the total capacity of the array -// we use a doubling algorithm to amortize the cost of insertion, so this is -// generally going to be a power-of-two -static size_t specialProtocolCapacity = 0; - -// the number of EXTSpecialProtocols which have been marked as ready for -// injection (though not necessary injected) -// -// in other words, the total count which have 'ready' set to YES -static size_t specialProtocolsReady = 0; - -// a mutex is used to guard against multiple threads changing the above static -// variables -static pthread_mutex_t specialProtocolsLock = PTHREAD_MUTEX_INITIALIZER; - -/** - * This function actually performs the hard work of special protocol injection. - * It obtains a full list of all classes registered with the Objective-C - * runtime, finds those conforming to special protocols, and then runs the - * injection blocks as appropriate. - */ -static void ext_injectSpecialProtocols (void) { - /* - * don't lock specialProtocolsLock in this function, as it is called only - * from public functions which already perform the synchronization - */ - - /* - * This will sort special protocols in the order they should be loaded. If - * a special protocol conforms to another special protocol, the former - * will be prioritized above the latter. - */ - qsort_b(specialProtocols, specialProtocolCount, sizeof(EXTSpecialProtocol), ^(const void *a, const void *b){ - // if the pointers are equal, it must be the same protocol - if (a == b) - return 0; - - const EXTSpecialProtocol *protoA = a; - const EXTSpecialProtocol *protoB = b; - - // A higher return value here means a higher priority - int (^protocolInjectionPriority)(const EXTSpecialProtocol *) = ^(const EXTSpecialProtocol *specialProtocol){ - int runningTotal = 0; - - for (size_t i = 0;i < specialProtocolCount;++i) { - // the pointer passed into this block is guaranteed to point - // into the 'specialProtocols' array, so we can compare the - // pointers directly for identity - if (specialProtocol == specialProtocols + i) - continue; - - if (protocol_conformsToProtocol(specialProtocol->protocol, specialProtocols[i].protocol)) - runningTotal++; - } - - return runningTotal; - }; - - /* - * This will return: - * 0 if the protocols are equal in priority (such that load order does not matter) - * < 0 if A is more important than B - * > 0 if B is more important than A - */ - return protocolInjectionPriority(protoB) - protocolInjectionPriority(protoA); - }); - - unsigned classCount = 0; - Class *allClasses = ext_copyClassList(&classCount); - - if (!classCount || !allClasses) { - fprintf(stderr, "ERROR: No classes registered with the runtime\n"); - return; - } - - /* - * set up an autorelease pool in case any Cocoa classes get used during - * the injection process or +initialize - */ - @autoreleasepool { - // loop through the special protocols, and apply each one to all the - // classes in turn - // - // ORDER IS IMPORTANT HERE: protocols have to be injected to all classes in - // the order in which they appear in specialProtocols. Consider classes - // X and Y that implement protocols A and B, respectively. B needs to get - // its implementation into Y before A gets into X. - for (size_t i = 0;i < specialProtocolCount;++i) { - Protocol *protocol = specialProtocols[i].protocol; - - // transfer ownership of the injection block to ARC and remove it - // from the structure - ext_specialProtocolInjectionBlock injectionBlock = (__bridge_transfer id)specialProtocols[i].injectionBlock; - specialProtocols[i].injectionBlock = NULL; - - // loop through all classes - for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { - Class class = allClasses[classIndex]; - - // if this class doesn't conform to the protocol, continue to the - // next class immediately - if (!class_conformsToProtocol(class, protocol)) - continue; - - injectionBlock(class); - } - } - } - - // free the allocated class list - free(allClasses); - - // now that everything's injected, the special protocol list can also be - // destroyed - free(specialProtocols); specialProtocols = NULL; - specialProtocolCount = 0; - specialProtocolCapacity = 0; - specialProtocolsReady = 0; -} - -unsigned ext_injectMethods ( - Class aClass, - Method *methods, - unsigned count, - ext_methodInjectionBehavior behavior, - ext_failedMethodCallback failedToAddCallback -) { - unsigned successes = 0; - - /* - * set up an autorelease pool in case any Cocoa classes invoke +initialize - * during this process - */ - @autoreleasepool { - BOOL isMeta = class_isMetaClass(aClass); - - if (!isMeta) { - // clear any +load and +initialize ignore flags - behavior &= ~(ext_methodInjectionIgnoreLoad | ext_methodInjectionIgnoreInitialize); - } - - for (unsigned methodIndex = 0;methodIndex < count;++methodIndex) { - Method method = methods[methodIndex]; - SEL methodName = method_getName(method); - - if (behavior & ext_methodInjectionIgnoreLoad) { - if (methodName == @selector(load)) { - ++successes; - continue; - } - } - - if (behavior & ext_methodInjectionIgnoreInitialize) { - if (methodName == @selector(initialize)) { - ++successes; - continue; - } - } - - BOOL success = YES; - IMP impl = method_getImplementation(method); - const char *type = method_getTypeEncoding(method); - - switch (behavior & ext_methodInjectionOverwriteBehaviorMask) { - case ext_methodInjectionFailOnExisting: - success = class_addMethod(aClass, methodName, impl, type); - break; - - case ext_methodInjectionFailOnAnyExisting: - if (class_getInstanceMethod(aClass, methodName)) { - success = NO; - break; - } - - // else fall through - - case ext_methodInjectionReplace: - class_replaceMethod(aClass, methodName, impl, type); - break; - - case ext_methodInjectionFailOnSuperclassExisting: - { - Class superclass = class_getSuperclass(aClass); - if (superclass && class_getInstanceMethod(superclass, methodName)) - success = NO; - else - class_replaceMethod(aClass, methodName, impl, type); - } - - break; - - default: - fprintf(stderr, "ERROR: Unrecognized method injection behavior: %i\n", (int)(behavior & ext_methodInjectionOverwriteBehaviorMask)); - } - - if (success) - ++successes; - else - failedToAddCallback(aClass, method); - } - } - - return successes; -} - -BOOL ext_injectMethodsFromClass ( - Class srcClass, - Class dstClass, - ext_methodInjectionBehavior behavior, - ext_failedMethodCallback failedToAddCallback) -{ - unsigned count, addedCount; - BOOL success = YES; - - count = 0; - Method *instanceMethods = class_copyMethodList(srcClass, &count); - - addedCount = ext_injectMethods( - dstClass, - instanceMethods, - count, - behavior, - failedToAddCallback - ); - - free(instanceMethods); - if (addedCount < count) - success = NO; - - count = 0; - Method *classMethods = class_copyMethodList(object_getClass(srcClass), &count); - - // ignore +load - behavior |= ext_methodInjectionIgnoreLoad; - addedCount = ext_injectMethods( - object_getClass(dstClass), - classMethods, - count, - behavior, - failedToAddCallback - ); - - free(classMethods); - if (addedCount < count) - success = NO; - - return success; -} - -Class ext_classBeforeSuperclass (Class receiver, Class superclass) { - Class previousClass = nil; - - while (![receiver isEqual:superclass]) { - previousClass = receiver; - receiver = class_getSuperclass(receiver); - } - - return previousClass; -} - -Class *ext_copyClassList (unsigned *count) { - // get the number of classes registered with the runtime - int classCount = objc_getClassList(NULL, 0); - if (!classCount) { - if (count) - *count = 0; - - return NULL; - } - - // allocate space for them plus NULL - Class *allClasses = (Class *)malloc(sizeof(Class) * (classCount + 1)); - if (!allClasses) { - fprintf(stderr, "ERROR: Could allocate memory for all classes\n"); - if (count) - *count = 0; - - return NULL; - } - - // and then actually pull the list of the class objects - classCount = objc_getClassList(allClasses, classCount); - allClasses[classCount] = NULL; - - @autoreleasepool { - // weed out classes that do weird things when reflected upon - for (int i = 0;i < classCount;) { - Class class = allClasses[i]; - BOOL keep = YES; - - if (keep) - keep &= class_respondsToSelector(class, @selector(methodSignatureForSelector:)); - - if (keep) { - if (class_respondsToSelector(class, @selector(isProxy))) - keep &= ![class isProxy]; - } - - if (!keep) { - if (--classCount > i) { - memmove(allClasses + i, allClasses + i + 1, (classCount - i) * sizeof(*allClasses)); - } - - continue; - } - - ++i; - } - } - - if (count) - *count = (unsigned)classCount; - - return allClasses; -} - -unsigned ext_addMethods (Class aClass, Method *methods, unsigned count, BOOL checkSuperclasses, ext_failedMethodCallback failedToAddCallback) { - ext_methodInjectionBehavior behavior = ext_methodInjectionFailOnExisting; - if (checkSuperclasses) - behavior |= ext_methodInjectionFailOnSuperclassExisting; - - return ext_injectMethods( - aClass, - methods, - count, - behavior, - failedToAddCallback - ); -} - -BOOL ext_addMethodsFromClass (Class srcClass, Class dstClass, BOOL checkSuperclasses, ext_failedMethodCallback failedToAddCallback) { - ext_methodInjectionBehavior behavior = ext_methodInjectionFailOnExisting; - if (checkSuperclasses) - behavior |= ext_methodInjectionFailOnSuperclassExisting; - - return ext_injectMethodsFromClass(srcClass, dstClass, behavior, failedToAddCallback); -} - -BOOL ext_classIsKindOfClass (Class receiver, Class aClass) { - while (receiver) { - if (receiver == aClass) - return YES; - - receiver = class_getSuperclass(receiver); - } - - return NO; -} - -Class *ext_copyClassListConformingToProtocol (Protocol *protocol, unsigned *count) { - Class *allClasses; - - /* - * set up an autorelease pool in case any Cocoa classes invoke +initialize - * during this process - */ - @autoreleasepool { - unsigned classCount = 0; - allClasses = ext_copyClassList(&classCount); - - // we're going to reuse allClasses for the return value, so returnIndex will - // keep track of the indices we replace with new values - unsigned returnIndex = 0; - - for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { - Class cls = allClasses[classIndex]; - if (class_conformsToProtocol(cls, protocol)) - allClasses[returnIndex++] = cls; - } - - allClasses[returnIndex] = NULL; - if (count) - *count = returnIndex; - } - - return allClasses; -} - -ext_propertyAttributes *ext_copyPropertyAttributes (objc_property_t property) { - const char * const attrString = property_getAttributes(property); - if (!attrString) { - fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); - return NULL; - } - - if (attrString[0] != 'T') { - fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); - return NULL; - } - - const char *typeString = attrString + 1; - const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); - if (!next) { - fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - size_t typeLength = next - typeString; - if (!typeLength) { - fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - // allocate enough space for the structure and the type string (plus a NUL) - ext_propertyAttributes *attributes = calloc(1, sizeof(ext_propertyAttributes) + typeLength + 1); - if (!attributes) { - fprintf(stderr, "ERROR: Could not allocate ext_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - // copy the type string - strncpy(attributes->type, typeString, typeLength); - attributes->type[typeLength] = '\0'; - - // if this is an object type, and immediately followed by a quoted string... - if (*typeString == *(@encode(id)) && *next == '"') { - // we should be able to extract a class name - const char *className = next + 1; - next = strchr(className, '"'); - - if (!next) { - fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - if (className != next) { - size_t classNameLength = next - className; - char trimmedName[classNameLength]; - - strncpy(trimmedName, className, classNameLength); - trimmedName[classNameLength] = '\0'; - - // attempt to look up the class in the runtime - attributes->objectClass = objc_getClass(trimmedName); - } - } - - if (*next != '\0') { - // skip past any junk before the first flag - next = strchr(next, ','); - } - - while (next && *next == ',') { - char flag = next[1]; - next += 2; - - switch (flag) { - case '\0': - break; - - case 'R': - attributes->readonly = YES; - break; - - case 'C': - attributes->memoryManagementPolicy = ext_propertyMemoryManagementPolicyCopy; - break; - - case '&': - attributes->memoryManagementPolicy = ext_propertyMemoryManagementPolicyRetain; - break; - - case 'N': - attributes->nonatomic = YES; - break; - - case 'G': - case 'S': - { - const char *nextFlag = strchr(next, ','); - SEL name = NULL; - - if (!nextFlag) { - // assume that the rest of the string is the selector - const char *selectorString = next; - next = ""; - - name = sel_registerName(selectorString); - } else { - size_t selectorLength = nextFlag - next; - if (!selectorLength) { - fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - goto errorOut; - } - - char selectorString[selectorLength + 1]; - - strncpy(selectorString, next, selectorLength); - selectorString[selectorLength] = '\0'; - - name = sel_registerName(selectorString); - next = nextFlag; - } - - if (flag == 'G') - attributes->getter = name; - else - attributes->setter = name; - } - - break; - - case 'D': - attributes->dynamic = YES; - attributes->ivar = NULL; - break; - - case 'V': - // assume that the rest of the string (if present) is the ivar name - if (*next == '\0') { - // if there's nothing there, let's assume this is dynamic - attributes->ivar = NULL; - } else { - attributes->ivar = next; - next = ""; - } - - break; - - case 'W': - attributes->weak = YES; - break; - - case 'P': - attributes->canBeCollected = YES; - break; - - case 't': - fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - - // skip over this type encoding - while (*next != ',' && *next != '\0') - ++next; - - break; - - default: - fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); - } - } - - if (next && *next != '\0') { - fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property)); - } - - if (!attributes->getter) { - // use the property name as the getter by default - attributes->getter = sel_registerName(property_getName(property)); - } - - if (!attributes->setter) { - const char *propertyName = property_getName(property); - size_t propertyNameLength = strlen(propertyName); - - // we want to transform the name to setProperty: style - size_t setterLength = propertyNameLength + 4; - - char setterName[setterLength + 1]; - strncpy(setterName, "set", 3); - strncpy(setterName + 3, propertyName, propertyNameLength); - - // capitalize property name for the setter - setterName[3] = (char)toupper(setterName[3]); - - setterName[setterLength - 1] = ':'; - setterName[setterLength] = '\0'; - - attributes->setter = sel_registerName(setterName); - } - - return attributes; - -errorOut: - free(attributes); - return NULL; -} - -Class *ext_copySubclassList (Class targetClass, unsigned *subclassCount) { - unsigned classCount = 0; - Class *allClasses = ext_copyClassList(&classCount); - if (!allClasses || !classCount) { - fprintf(stderr, "ERROR: No classes registered with the runtime, cannot find %s!\n", class_getName(targetClass)); - return NULL; - } - - // we're going to reuse allClasses for the return value, so returnIndex will - // keep track of the indices we replace with new values - unsigned returnIndex = 0; - - BOOL isMeta = class_isMetaClass(targetClass); - - for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { - Class cls = allClasses[classIndex]; - Class superclass = class_getSuperclass(cls); - - while (superclass != NULL) { - if (isMeta) { - if (object_getClass(superclass) == targetClass) - break; - } else if (superclass == targetClass) - break; - - superclass = class_getSuperclass(superclass); - } - - if (!superclass) - continue; - - // at this point, 'cls' is definitively a subclass of targetClass - if (isMeta) - cls = object_getClass(cls); - - allClasses[returnIndex++] = cls; - } - - allClasses[returnIndex] = NULL; - if (subclassCount) - *subclassCount = returnIndex; - - return allClasses; -} - -Method ext_getImmediateInstanceMethod (Class aClass, SEL aSelector) { - unsigned methodCount = 0; - Method *methods = class_copyMethodList(aClass, &methodCount); - Method foundMethod = NULL; - - for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) { - if (method_getName(methods[methodIndex]) == aSelector) { - foundMethod = methods[methodIndex]; - break; - } - } - - free(methods); - return foundMethod; -} - -BOOL ext_getPropertyAccessorsForClass (objc_property_t property, Class aClass, Method *getter, Method *setter) { - ext_propertyAttributes *attributes = ext_copyPropertyAttributes(property); - if (!attributes) - return NO; - - SEL getterName = attributes->getter; - SEL setterName = attributes->setter; - - free(attributes); - attributes = NO; - - /* - * set up an autorelease pool in case this sends aClass its first message - */ - @autoreleasepool { - Method foundGetter = class_getInstanceMethod(aClass, getterName); - if (!foundGetter) { - return NO; - } - - if (getter) - *getter = foundGetter; - - if (setter) { - Method foundSetter = class_getInstanceMethod(aClass, setterName); - if (foundSetter) - *setter = foundSetter; - } - } - - return YES; -} - -NSMethodSignature *ext_globalMethodSignatureForSelector (SEL aSelector) { - // set up a simplistic cache to avoid repeatedly scouring every class in the - // runtime - static const size_t selectorCacheLength = 1 << 8; - static const uintptr_t selectorCacheMask = (selectorCacheLength - 1); - static void * volatile selectorCache[selectorCacheLength]; - - const char *cachedType = selectorCache[(uintptr_t)aSelector & selectorCacheMask]; - if (cachedType) { - return [NSMethodSignature signatureWithObjCTypes:cachedType]; - } - - unsigned classCount = 0; - Class *classes = ext_copyClassList(&classCount); - if (!classes) - return nil; - - NSMethodSignature *signature = nil; - - /* - * set up an autorelease pool in case any Cocoa classes invoke +initialize - * during this process - */ - @autoreleasepool { - for (unsigned i = 0;i < classCount;++i) { - Class cls = classes[i]; - Method method; - - method = class_getInstanceMethod(cls, aSelector); - if (!method) - method = class_getClassMethod(cls, aSelector); - - if (method) { - const char *type = method_getTypeEncoding(method); - uintptr_t cacheLocation = ((uintptr_t)aSelector & selectorCacheMask); - - // this doesn't need to be a barrier, and we don't care whether - // it succeeds, since our only goal is to make things faster in - // the future - OSAtomicCompareAndSwapPtr(selectorCache[cacheLocation], (void *)type, selectorCache + cacheLocation); - - signature = [NSMethodSignature signatureWithObjCTypes:type]; - break; - } - } - } - - free(classes); - return signature; -} - -BOOL ext_loadSpecialProtocol (Protocol *protocol, void (^injectionBehavior)(Class destinationClass)) { - @autoreleasepool { - NSCParameterAssert(protocol != nil); - NSCParameterAssert(injectionBehavior != nil); - - // lock the mutex to prevent accesses from other threads while we perform - // this work - if (pthread_mutex_lock(&specialProtocolsLock) != 0) { - fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n"); - return NO; - } - - // if we've hit the hard maximum for number of special protocols, we can't - // continue - if (specialProtocolCount == SIZE_MAX) { - pthread_mutex_unlock(&specialProtocolsLock); - return NO; - } - - // if the array has no more space, we will need to allocate additional - // entries - if (specialProtocolCount >= specialProtocolCapacity) { - size_t newCapacity; - if (specialProtocolCapacity == 0) - // if there are no entries, make space for just one - newCapacity = 1; - else { - // otherwise, double the current capacity - newCapacity = specialProtocolCapacity << 1; - - // if the new capacity is less than the current capacity, that's - // unsigned integer overflow - if (newCapacity < specialProtocolCapacity) { - // set it to the maximum possible instead - newCapacity = SIZE_MAX; - - // if the new capacity is still not greater than the current - // (for instance, if it was already SIZE_MAX), we can't continue - if (newCapacity <= specialProtocolCapacity) { - pthread_mutex_unlock(&specialProtocolsLock); - return NO; - } - } - } - - // we have a new capacity, so resize the list of all special protocols - // to add the new entries - void * restrict ptr = realloc(specialProtocols, sizeof(*specialProtocols) * newCapacity); - if (!ptr) { - // the allocation failed, abort - pthread_mutex_unlock(&specialProtocolsLock); - return NO; - } - - specialProtocols = ptr; - specialProtocolCapacity = newCapacity; - } - - // at this point, there absolutely must be at least one empty entry in the - // array - assert(specialProtocolCount < specialProtocolCapacity); - - // disable warning about "leaking" this block, which is released in - // ext_injectSpecialProtocols() - #ifndef __clang_analyzer__ - ext_specialProtocolInjectionBlock copiedBlock = [injectionBehavior copy]; - - // construct a new EXTSpecialProtocol structure and add it to the first - // empty space in the array - specialProtocols[specialProtocolCount] = (EXTSpecialProtocol){ - .protocol = protocol, - .injectionBlock = (__bridge_retained void *)copiedBlock, - .ready = NO - }; - #endif - - ++specialProtocolCount; - pthread_mutex_unlock(&specialProtocolsLock); - } - - // success! - return YES; -} - -void ext_specialProtocolReadyForInjection (Protocol *protocol) { - @autoreleasepool { - NSCParameterAssert(protocol != nil); - - // lock the mutex to prevent accesses from other threads while we perform - // this work - if (pthread_mutex_lock(&specialProtocolsLock) != 0) { - fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n"); - return; - } - - // loop through all the special protocols in our list, trying to find the - // one associated with 'protocol' - for (size_t i = 0;i < specialProtocolCount;++i) { - if (specialProtocols[i].protocol == protocol) { - // found the matching special protocol, check to see if it's - // already ready - if (!specialProtocols[i].ready) { - // if it's not, mark it as being ready now - specialProtocols[i].ready = YES; - - // since this special protocol was in our array, and it was not - // loaded, the total number of protocols loaded must be less - // than the total count at this point in time - assert(specialProtocolsReady < specialProtocolCount); - - // ... and then increment the total number of special protocols - // loaded – if it now matches the total count of special - // protocols, begin the injection process - if (++specialProtocolsReady == specialProtocolCount) - ext_injectSpecialProtocols(); - } - - break; - } - } - - pthread_mutex_unlock(&specialProtocolsLock); - } -} - -void ext_removeMethod (Class aClass, SEL methodName) { - Method existingMethod = ext_getImmediateInstanceMethod(aClass, methodName); - if (!existingMethod) { - return; - } - - /* - * set up an autorelease pool in case any Cocoa classes invoke +initialize - * during this process - */ - @autoreleasepool { - Method superclassMethod = NULL; - Class superclass = class_getSuperclass(aClass); - if (superclass) - superclassMethod = class_getInstanceMethod(superclass, methodName); - - if (superclassMethod) { - method_setImplementation(existingMethod, method_getImplementation(superclassMethod)); - } else { - // since we now know that the method doesn't exist on any - // superclass, get an IMP internal to the runtime for message forwarding - IMP forward = class_getMethodImplementation(superclass, methodName); - - method_setImplementation(existingMethod, forward); - } - } -} - -void ext_replaceMethods (Class aClass, Method *methods, unsigned count) { - ext_injectMethods( - aClass, - methods, - count, - ext_methodInjectionReplace, - NULL - ); -} - -void ext_replaceMethodsFromClass (Class srcClass, Class dstClass) { - ext_injectMethodsFromClass(srcClass, dstClass, ext_methodInjectionReplace, NULL); -} -