【问题标题】:What is happening to instance variables referred to from within an imp_implementationWithBlock?从 imp_implementationWithBlock 中引用的实例变量发生了什么?
【发布时间】:2014-08-13 14:12:47
【问题描述】:

对于我正在处理的项目,我必须动态地提供一些动态属性的实现。这样做时,我注意到在测试期间我在 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

【问题讨论】:

    标签: objective-c objective-c-runtime


    【解决方案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;
        });
    }
    

    【讨论】:

    • 您说得对,先生。就像其他人知道的那样,如果 self 被声明为 _self,则执行 _self->_aCustomString 不起作用。它必须声明为 DRTestObject *_self
    猜你喜欢
    • 1970-01-01
    • 2012-04-06
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多