从imp_implementationWithBlock中引用的实例变量发生了什么?

时间:2014-08-13 14:12:48

标签: objective-c objective-c-runtime

对于我正在进行的项目,我必须动态地为一些动态属性提供实现。在这样做的过程中,我注意到在测试期间我在imp_implementationWithBlock块中引用的实例变量总是会返回我在声明块时所具有的值,而不是当前实例变量的值。

即:

  • 我的第一个实例的实例变量值为@" test1"
  • 在第一次初始化时,如果我的动态方法尚不存在,我使用imp_implementationWithBlock然后使用class_addMethod创建它。
  • 然后,如果访问动态属性obj1.dynProp,我会得到我的@" test1"价值回来。
  • 我实例化第二个对象,其值为@" test2"。
  • 动态属性已经有了实现,所以我在这方面什么也没做。
  • 现在,如果我访问动态属性obj2.dynProp,我仍然会得到@" test1"价值回来。

最后,我只是使用class_getInstanceVariable来检索实例变量,一切正常,但我想了解为什么它不起作用。显然,实例变量似乎与imp_implementationWithBlock中的块一起被复制,但是我无法找到相关的文档来解释究竟发生了什么。

这是一个简单的类,可以重现这个问题:

DRTestObject.h

#import <UIKit/UIKit.h>

@interface DRTestObject : NSObject

- (instancetype)initWithString:(NSString *)aCustomString;

@property (readonly, nonatomic) NSString *customString;

@end

DRTestObject.m

#import "DRTestObject.h"
#import <objc/runtime.h>

/*************************************************************************************/

@interface DRTestObject()
{
    NSString *_aCustomString;
}

@end

/*************************************************************************************/

@implementation DRTestObject

@dynamic customString;

- (instancetype)initWithString:(NSString *)aCustomString
{
    self = [self init];
    if(self){
        _aCustomString = aCustomString;
        [self addDynamicMethod];
    }
    return self;
}

- (void)addDynamicMethod
{
    if(![self alreadyHasImplementation]){
        IMP dynamicIMP = [self dynamicImplementation];
        class_addMethod([self class], NSSelectorFromString(@"customString"), dynamicIMP, [@"@NSString@:" UTF8String]);
    }
}

- (BOOL)alreadyHasImplementation
{
    Method method = class_getInstanceMethod([self class], NSSelectorFromString(@"customString"));
    return method != NULL;
}

- (IMP)dynamicImplementation
{
    return imp_implementationWithBlock(^NSString * (id _self) {
        return _aCustomString;
    });
}

@end

测试电话

DRTestObject *testObj1 = [[DRTestObject alloc] initWithString:@"testObj1"];
NSLog(@"TestObj1 Custom String : %@", testObj1.customString); // Returns testObj1

DRTestObject *testObj2 = [[DRTestObject alloc] initWithString:@"testObj2"];
NSLog(@"TestObj2 Custom String : %@", testObj2.customString); // Also returns testObj1

1 个答案:

答案 0 :(得分:1)

因为您的-(IMP)dynamicImplementation与:

相同
- (IMP)dynamicImplementation {
    id blockSelf = self;
    return imp_implementationWithBlock(^NSString * (id _self) {
        return blockSelf->_aCustomString;
    });
}

它捕获第一个实例并返回其_aCustomString ivar。

您应该使用_self

- (IMP)dynamicImplementation {
    return imp_implementationWithBlock(^NSString * (id _self) {
        return _self->_aCustomString;
    });
}
相关问题