From 5671c06969848b4c1afb2c41fb7f81e88af75e15 Mon Sep 17 00:00:00 2001 From: Scott Morrison Date: Thu, 2 May 2024 10:30:21 -0700 Subject: [PATCH 1/5] Adds ability to ignore selectors on mocks Messages matching ignored selectors will be sent directly to real objects bypassing objc message forwarding. This is used to deal with methods that have variadic parameter. (the NSInvocation object doesnt handle variadic parameters). Example Usage: OCMIgnore(@class(MYClass),@selector(methodWithVariadicParams:)); --- Source/OCMock/OCClassMockObject.m | 6 +++++ Source/OCMock/OCMockMacros.h | 2 ++ Source/OCMock/OCMockObject.h | 3 +++ Source/OCMock/OCMockObject.m | 34 +++++++++++++++++++++++++++++ Source/OCMock/OCPartialMockObject.m | 14 ++++++++++++ 5 files changed, 59 insertions(+) diff --git a/Source/OCMock/OCClassMockObject.m b/Source/OCMock/OCClassMockObject.m index 460a7cde..762166d7 100644 --- a/Source/OCMock/OCClassMockObject.m +++ b/Source/OCMock/OCClassMockObject.m @@ -166,6 +166,12 @@ - (void)prepareClassForClassMethodMocking - (void)setupForwarderForClassMethodSelector:(SEL)selector { + for(Class aClass in [OCMockObject classesIgnoringMockedSelector:selector]) + { + if([mockedClass isKindOfClass:aClass]) + return; + } + SEL aliasSelector = OCMAliasForOriginalSelector(selector); if(class_getClassMethod(mockedClass, aliasSelector) != NULL) return; diff --git a/Source/OCMock/OCMockMacros.h b/Source/OCMock/OCMockMacros.h index 22535572..1effa5bf 100644 --- a/Source/OCMock/OCMockMacros.h +++ b/Source/OCMock/OCMockMacros.h @@ -155,3 +155,5 @@ macro \ _Pragma("clang diagnostic pop") \ }) + +#define OCMIgnore(class,selector) do { [OCMockObject ignoreMethod:selector forClass:class];} while(0); diff --git a/Source/OCMock/OCMockObject.h b/Source/OCMock/OCMockObject.h index f5739716..4ffb97fb 100644 --- a/Source/OCMock/OCMockObject.h +++ b/Source/OCMock/OCMockObject.h @@ -43,6 +43,9 @@ + (id)observerMock __deprecated_msg("Please use XCTNSNotificationExpectation instead."); ++ (NSArray *)classesIgnoringMockedSelector:(SEL)aSelector; ++ (void)ignoreMethod:(SEL) selector forClass:(Class) aClass; + - (instancetype)init; - (void)setExpectationOrderMatters:(BOOL)flag; diff --git a/Source/OCMock/OCMockObject.m b/Source/OCMock/OCMockObject.m index d78cba44..0d948eeb 100644 --- a/Source/OCMock/OCMockObject.m +++ b/Source/OCMock/OCMockObject.m @@ -83,6 +83,26 @@ + (id)observerMock } +NSDictionary *> * _static_ignoredBySelectorName; + ++ (void)ignoreMethod:(SEL) aSelector forClass:(Class) aClass{ + @synchronized (self) + { + NSMutableDictionary * muIgnoreListBySelector = [[_static_ignoredBySelectorName?:@{} mutableCopy] autorelease]; + NSMutableSet * muIgnoreList = [[muIgnoreListBySelector[NSStringFromSelector(aSelector)]?:NSSet.new mutableCopy] autorelease]; + [muIgnoreList addObject:aClass ]; + muIgnoreListBySelector[NSStringFromSelector(aSelector)] = [muIgnoreList.copy autorelease]; + [_static_ignoredBySelectorName autorelease]; + _static_ignoredBySelectorName = muIgnoreListBySelector.copy; + } +} + ++ (NSArray *)classesIgnoringMockedSelector:(SEL)aSelector{ + return [_static_ignoredBySelectorName[NSStringFromSelector(aSelector)] allObjects]; +} + + + #pragma mark Initialisers, description, accessors, etc. - (instancetype)init @@ -363,6 +383,20 @@ - (id)forwardingTargetForSelector:(SEL)aSelector [recorder setShouldReturnMockFromInit:(class_getInstanceMethod(object_getClass(recorder), aSelector) == NO)]; return recorder; } + if (object_getClass(self) == OCPartialMockObject.class) + { + Ivar ivar = class_getInstanceVariable(OCPartialMockObject.class,"realObject"); + id mockObject = ivar?object_getIvar(self, ivar):nil; + if(mockObject) + { + for(Class aClass in [OCMockObject classesIgnoringMockedSelector:aSelector]) + { + if([mockObject isKindOfClass:aClass]) + return mockObject; + } + } + } + return nil; } diff --git a/Source/OCMock/OCPartialMockObject.m b/Source/OCMock/OCPartialMockObject.m index 6a7c53c3..f013cf53 100644 --- a/Source/OCMock/OCPartialMockObject.m +++ b/Source/OCMock/OCPartialMockObject.m @@ -199,6 +199,12 @@ - (void)prepareObjectForInstanceMethodMocking - (void)setupForwarderForSelector:(SEL)sel { + + for(Class aClass in [OCMockObject classesIgnoringMockedSelector:sel]) + { + if([realObject isKindOfClass:aClass]) + return; + } SEL aliasSelector = OCMAliasForOriginalSelector(sel); if(class_getInstanceMethod(object_getClass(realObject), aliasSelector) != NULL) return; @@ -232,6 +238,14 @@ - (Class)classForRealObject - (id)forwardingTargetForSelectorForRealObject:(SEL)sel { // in here "self" is a reference to the real object, not the mock + + for(Class aClass in [OCMockObject classesIgnoringMockedSelector:sel]) + { + if([self isKindOfClass:aClass]) + return self; + } + + OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self); if(mock == nil) [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self]; From 317efb4f662c9b9a964000bc9209450d7bd078c6 Mon Sep 17 00:00:00 2001 From: Scott Morrison Date: Fri, 13 Sep 2024 11:53:53 -0700 Subject: [PATCH 2/5] Addresses issue where a creating partialMock can remove stubs from a ClassMock Class mocks now have to be created prior to any partial mocks. --- Source/OCMock/OCClassMockObject.h | 4 +- Source/OCMock/OCClassMockObject.m | 108 ++++++++++-------- Source/OCMock/OCMockObject.m | 2 +- Source/OCMock/OCPartialMockObject.m | 2 +- .../OCMockObjectClassMethodMockingTests.m | 14 +-- 5 files changed, 70 insertions(+), 60 deletions(-) diff --git a/Source/OCMock/OCClassMockObject.h b/Source/OCMock/OCClassMockObject.h index 6f1ccffd..9ff6c77c 100644 --- a/Source/OCMock/OCClassMockObject.h +++ b/Source/OCMock/OCClassMockObject.h @@ -22,8 +22,8 @@ Class originalMetaClass; Class classCreatedForNewMetaClass; } - -- (id)initWithClass:(Class)aClass; +- (id)initForMockingClass:(Class)aClass; +- (id)initForMockingInstancesOfClass:(Class)aClass; - (Class)mockedClass; - (Class)mockObjectClass; // since -class returns the mockedClass diff --git a/Source/OCMock/OCClassMockObject.m b/Source/OCMock/OCClassMockObject.m index 762166d7..380fbdac 100644 --- a/Source/OCMock/OCClassMockObject.m +++ b/Source/OCMock/OCClassMockObject.m @@ -29,13 +29,20 @@ + (BOOL)supportsMocking:(NSString **)reason; @implementation OCClassMockObject #pragma mark Initialisers, description, accessors, etc. - -- (id)initWithClass:(Class)aClass +- (id)initForMockingClass:(Class)aClass { [self assertClassIsSupported:aClass]; [super init]; mockedClass = aClass; - [self prepareClassForClassMethodMocking]; + [self prepareClassForMockingInstances:NO classes:YES]; + return self; +} +- (id)initForMockingInstancesOfClass:(Class)aClass +{ + [self assertClassIsSupported:aClass]; + [super init]; + mockedClass = aClass; + [self prepareClassForMockingInstances:YES classes:NO]; return self; } @@ -104,7 +111,7 @@ - (void)addStub:(OCMInvocationStub *)aStub #pragma mark Class method mocking -- (void)prepareClassForClassMethodMocking +- (void)prepareClassForMockingInstances:(BOOL)forInstances classes:(BOOL)forClasses { /* the runtime and OCMock depend on string and array; we don't intercept methods on them to avoid endless loops */ if([[mockedClass class] isSubclassOfClass:[NSString class]] || [[mockedClass class] isSubclassOfClass:[NSArray class]]) @@ -116,51 +123,54 @@ - (void)prepareClassForClassMethodMocking /* if there is another mock for this exact class, stop it */ id otherMock = OCMGetAssociatedMockForClass(mockedClass, NO); - if(otherMock != nil) - [otherMock stopMockingClassMethods]; - - OCMSetAssociatedMockForClass(self, mockedClass); - - /* dynamically create a subclass and use its meta class as the meta class for the mocked class */ - classCreatedForNewMetaClass = OCMCreateSubclass(mockedClass, mockedClass); - originalMetaClass = object_getClass(mockedClass); - id newMetaClass = object_getClass(classCreatedForNewMetaClass); - - /* create a dummy initialize method */ - Method myDummyInitializeMethod = class_getInstanceMethod([self mockObjectClass], @selector(initializeForClassObject)); - const char *initializeTypes = method_getTypeEncoding(myDummyInitializeMethod); - IMP myDummyInitializeIMP = method_getImplementation(myDummyInitializeMethod); - class_addMethod(newMetaClass, @selector(initialize), myDummyInitializeIMP, initializeTypes); - - object_setClass(mockedClass, newMetaClass); // only after dummy initialize is installed (iOS9) - - /* point forwardInvocation: of the object to the implementation in the mock */ - Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForClassObject:)); - IMP myForwardIMP = method_getImplementation(myForwardMethod); - class_addMethod(newMetaClass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod)); - - /* adding forwarder for most class methods (instance methods on meta class) to allow for verify after run */ - NSArray *methodsNotToForward = @[ - @"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:", @"forwardInvocation:", @"isBlock", - @"instanceMethodForwarderForSelector:", @"instanceMethodSignatureForSelector:", @"resolveClassMethod:" - ]; - void (^setupForwarderFiltered)(Class, SEL) = ^(Class cls, SEL sel) { - if((cls == object_getClass([NSObject class])) || (cls == [NSObject class]) || (cls == object_getClass(cls))) - return; - if(OCMIsApplePrivateMethod(cls, sel)) - return; - if([methodsNotToForward containsObject:NSStringFromSelector(sel)]) - return; - @try - { - [self setupForwarderForClassMethodSelector:sel]; - } - @catch(NSException *e) - { - // ignore for now - } - }; - [NSObject enumerateMethodsInClass:originalMetaClass usingBlock:setupForwarderFiltered]; + if( otherMock != nil && forClasses) + { + [NSException raise:NSInvalidArgumentException format:@"Class Mock must be made before partial mocks"]; + } + if(otherMock == nil){ + OCMSetAssociatedMockForClass(self, mockedClass); + + /* dynamically create a subclass and use its meta class as the meta class for the mocked class */ + classCreatedForNewMetaClass = OCMCreateSubclass(mockedClass, mockedClass); + originalMetaClass = object_getClass(mockedClass); + id newMetaClass = object_getClass(classCreatedForNewMetaClass); + + /* create a dummy initialize method */ + Method myDummyInitializeMethod = class_getInstanceMethod([self mockObjectClass], @selector(initializeForClassObject)); + const char *initializeTypes = method_getTypeEncoding(myDummyInitializeMethod); + IMP myDummyInitializeIMP = method_getImplementation(myDummyInitializeMethod); + class_addMethod(newMetaClass, @selector(initialize), myDummyInitializeIMP, initializeTypes); + + object_setClass(mockedClass, newMetaClass); // only after dummy initialize is installed (iOS9) + + /* point forwardInvocation: of the object to the implementation in the mock */ + Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForClassObject:)); + IMP myForwardIMP = method_getImplementation(myForwardMethod); + class_addMethod(newMetaClass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod)); + + /* adding forwarder for most class methods (instance methods on meta class) to allow for verify after run */ + NSArray *methodsNotToForward = @[ + @"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:", @"forwardInvocation:", @"isBlock", + @"instanceMethodForwarderForSelector:", @"instanceMethodSignatureForSelector:", @"resolveClassMethod:" + ]; + void (^setupForwarderFiltered)(Class, SEL) = ^(Class cls, SEL sel) { + if((cls == object_getClass([NSObject class])) || (cls == [NSObject class]) || (cls == object_getClass(cls))) + return; + if(OCMIsApplePrivateMethod(cls, sel)) + return; + if([methodsNotToForward containsObject:NSStringFromSelector(sel)]) + return; + @try + { + [self setupForwarderForClassMethodSelector:sel]; + } + @catch(NSException *e) + { + // ignore for now + } + }; + [NSObject enumerateMethodsInClass:originalMetaClass usingBlock:setupForwarderFiltered]; + } } diff --git a/Source/OCMock/OCMockObject.m b/Source/OCMock/OCMockObject.m index 0d948eeb..dbc16b66 100644 --- a/Source/OCMock/OCMockObject.m +++ b/Source/OCMock/OCMockObject.m @@ -45,7 +45,7 @@ + (void)initialize + (id)mockForClass:(Class)aClass { - return [[[OCClassMockObject alloc] initWithClass:aClass] autorelease]; + return [[[OCClassMockObject alloc] initForMockingClass:aClass] autorelease]; } + (id)mockForProtocol:(Protocol *)aProtocol diff --git a/Source/OCMock/OCPartialMockObject.m b/Source/OCMock/OCPartialMockObject.m index f013cf53..4af4c487 100644 --- a/Source/OCMock/OCPartialMockObject.m +++ b/Source/OCMock/OCPartialMockObject.m @@ -34,7 +34,7 @@ - (id)initWithObject:(NSObject *)anObject if([anObject isProxy]) [NSException raise:NSInvalidArgumentException format:@"OCMock does not support partially mocking subclasses of NSProxy."]; Class const class = [self classToSubclassForObject:anObject]; - [super initWithClass:class]; + [super initForMockingInstancesOfClass:class]; realObject = [anObject retain]; [self prepareObjectForInstanceMethodMocking]; return self; diff --git a/Source/OCMockTests/OCMockObjectClassMethodMockingTests.m b/Source/OCMockTests/OCMockObjectClassMethodMockingTests.m index cccfb0f9..b8cb118f 100644 --- a/Source/OCMockTests/OCMockObjectClassMethodMockingTests.m +++ b/Source/OCMockTests/OCMockObjectClassMethodMockingTests.m @@ -220,7 +220,7 @@ - (void)testStubsCanDistinguishInstanceAndClassMethods - (void)testRevertsAllStubbedMethodsOnDealloc { - id mock = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; [[[[mock stub] classMethod] andReturn:@"mocked-foo"] foo]; [[[[mock stub] classMethod] andReturn:@"mocked-bar"] bar]; @@ -236,7 +236,7 @@ - (void)testRevertsAllStubbedMethodsOnDealloc - (void)testRevertsAllStubbedMethodsOnPartialMockDealloc { - id mock = [[OCPartialMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock = [[OCPartialMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; [[[[mock stub] classMethod] andReturn:@"mocked-foo"] foo]; [[[[mock stub] classMethod] andReturn:@"mocked-bar"] bar]; @@ -252,10 +252,10 @@ - (void)testRevertsAllStubbedMethodsOnPartialMockDealloc - (void)testSecondClassMockDeactivatesFirst { - id mock1 = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock1 = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; [[[mock1 stub] andReturn:@"mocked-foo-1"] foo]; - id mock2 = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock2 = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; XCTAssertEqualObjects(@"Foo-ClassMethod", [TestClassWithClassMethods foo]); [mock2 stopMocking]; @@ -264,7 +264,7 @@ - (void)testSecondClassMockDeactivatesFirst - (void)testStopMockingDisposesMetaClass { - id mock = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; char *createdSubclassName = strdup(object_getClassName([TestClassWithClassMethods class])); XCTAssertNotNil(objc_lookUpClass(createdSubclassName)); @@ -276,11 +276,11 @@ - (void)testStopMockingDisposesMetaClass - (void)testSecondClassMockDisposesFirstMetaClass { - id mock1 = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock1 = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; char *createdSubclassName1 = strdup(object_getClassName([TestClassWithClassMethods class])); XCTAssertNotNil(objc_lookUpClass(createdSubclassName1)); - id mock2 = [[OCClassMockObject alloc] initWithClass:[TestClassWithClassMethods class]]; + id mock2 = [[OCClassMockObject alloc] initForMockingClass:[TestClassWithClassMethods class]]; char *createdSubclassName2 = strdup(object_getClassName([TestClassWithClassMethods class])); XCTAssertNotNil(objc_lookUpClass(createdSubclassName2)); From 6fa4319843ee69342d379c3737ae058d9c4e4e26 Mon Sep 17 00:00:00 2001 From: Scott Morrison Date: Wed, 18 Sep 2024 19:47:41 -0700 Subject: [PATCH 3/5] Adds ability to remove stubs from an mocked object by selector --- Source/OCMock/OCMockObject.h | 1 + Source/OCMock/OCMockObject.m | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/OCMock/OCMockObject.h b/Source/OCMock/OCMockObject.h index 4ffb97fb..2146c01b 100644 --- a/Source/OCMock/OCMockObject.h +++ b/Source/OCMock/OCMockObject.h @@ -65,6 +65,7 @@ // internal use only - (void)addStub:(OCMInvocationStub *)aStub; +- (void)removeStubsForSelector:(SEL)selector; - (void)addExpectation:(OCMInvocationExpectation *)anExpectation; - (void)addInvocation:(NSInvocation *)anInvocation; diff --git a/Source/OCMock/OCMockObject.m b/Source/OCMock/OCMockObject.m index dbc16b66..07f08e4e 100644 --- a/Source/OCMock/OCMockObject.m +++ b/Source/OCMock/OCMockObject.m @@ -210,6 +210,19 @@ - (void)setExpectationOrderMatters:(BOOL)flag expectationOrderMatters = flag; } +- (void)removeStubsForSelector:(SEL)selector{ + @synchronized (stubs) { + int s = stubs.count; + while (--s){ + OCMInvocationStub * stub = stubs[s]; + if ([stub isKindOfClass:OCMInvocationStub.class]){ + if (stub.recordedInvocation.selector == selector){ + [stubs removeObjectAtIndex:s]; + } + } + } + } +} - (void)stopMocking { // invocations can contain objects that clients expect to be deallocated by now, From d110112ffa32dd00aa7a480d2cf57231b446c4ac Mon Sep 17 00:00:00 2001 From: Scott Morrison Date: Thu, 26 Sep 2024 15:41:48 -0700 Subject: [PATCH 4/5] Fixes issue with removing stubs --- Source/OCMock/OCMockObject.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OCMock/OCMockObject.m b/Source/OCMock/OCMockObject.m index 07f08e4e..ccc10f88 100644 --- a/Source/OCMock/OCMockObject.m +++ b/Source/OCMock/OCMockObject.m @@ -213,7 +213,7 @@ - (void)setExpectationOrderMatters:(BOOL)flag - (void)removeStubsForSelector:(SEL)selector{ @synchronized (stubs) { int s = stubs.count; - while (--s){ + while (s--){ OCMInvocationStub * stub = stubs[s]; if ([stub isKindOfClass:OCMInvocationStub.class]){ if (stub.recordedInvocation.selector == selector){ From d66b880ea3d7fdf9a4ed9ad364c49447c0d945b6 Mon Sep 17 00:00:00 2001 From: Scott Morrison Date: Sun, 20 Oct 2024 18:44:22 -0700 Subject: [PATCH 5/5] adds +[ OCMArg setToResultOfBlock:(id(^)(void))block] --- Source/OCMock/OCMArg.h | 1 + Source/OCMock/OCMArg.m | 4 ++++ Source/OCMock/OCMPassByRefSetter.h | 2 ++ Source/OCMock/OCMPassByRefSetter.m | 27 +++++++++++++++++++++++---- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Source/OCMock/OCMArg.h b/Source/OCMock/OCMArg.h index 27167078..8c332572 100644 --- a/Source/OCMock/OCMArg.h +++ b/Source/OCMock/OCMArg.h @@ -36,6 +36,7 @@ + (id *)setTo:(id)value; + (void *)setToValue:(NSValue *)value; ++ (id *)setToResultOfBlock:(id(^)(void))block; + (id)invokeBlock; + (id)invokeBlockWithArgs:(id)first, ... NS_REQUIRES_NIL_TERMINATION; diff --git a/Source/OCMock/OCMArg.m b/Source/OCMock/OCMArg.m index f7fce591..6267a80d 100644 --- a/Source/OCMock/OCMArg.m +++ b/Source/OCMock/OCMArg.m @@ -87,6 +87,10 @@ + (id *)setTo:(id)value return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease]; } ++ (id *)setToResultOfBlock:(id(^)(void))block{ + return (id *)[[[OCMPassByRefSetter alloc] initWithBlock:block] autorelease]; +} + + (void *)setToValue:(NSValue *)value { return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease]; diff --git a/Source/OCMock/OCMPassByRefSetter.h b/Source/OCMock/OCMPassByRefSetter.h index 18c429dd..434fc6c3 100644 --- a/Source/OCMock/OCMPassByRefSetter.h +++ b/Source/OCMock/OCMPassByRefSetter.h @@ -19,9 +19,11 @@ @interface OCMPassByRefSetter : OCMArgAction { id value; + id(^block)(void); } - (id)initWithValue:(id)value; +- (id)initWithBlock:(id(^)(void))block; + (BOOL)isPassByRefSetterInstance:(void *)ptr; diff --git a/Source/OCMock/OCMPassByRefSetter.m b/Source/OCMock/OCMPassByRefSetter.m index 7011c20d..eb0b7849 100644 --- a/Source/OCMock/OCMPassByRefSetter.m +++ b/Source/OCMock/OCMPassByRefSetter.m @@ -55,9 +55,23 @@ - (id)initWithValue:(id)aValue return self; } +- (id)initWithBlock:(id(^)(void))aBlock{ + if((self = [super init])) + { + block = [aBlock copy]; + @synchronized(_OCMPassByRefSetterInstances) + { + NSHashInsertKnownAbsent(_OCMPassByRefSetterInstances, self); + } + } + + return self; +} + - (void)dealloc { [value release]; + [block release]; @synchronized(_OCMPassByRefSetterInstances) { NSHashRemove(_OCMPassByRefSetterInstances, self); @@ -70,10 +84,15 @@ - (void)handleArgument:(id)arg void *pointerValue = [arg pointerValue]; if(pointerValue != NULL) { - if([value isKindOfClass:[NSValue class]]) - [(NSValue *)value getValue:pointerValue]; - else - *(id *)pointerValue = value; + if (block){ + *(id*)pointerValue = block(); + } + else{ + if([value isKindOfClass:[NSValue class]]) + [(NSValue *)value getValue:pointerValue]; + else + *(id *)pointerValue = value; + } } }