强烈提及代表们

时间:2014-02-02 17:12:20

标签: ios objective-c delegates

我在某处读到,如果我在我的应用程序中有代表,我应该对它们保持弱引用 然而,当我调试应用程序时,应用程序不想继续,当我呼吁代表 因为我的代表在到达该方法时已被解除分配

为什么?

如果我把引用变为“强大”,一切正常,但我不确定这对我的内存分配有什么影响,以及这些代表不在“sharedInstance”类中的事实....

代码:

    @interface LoginProcessListener()
@property (nonatomic,weak)id<UserSettingsDelegate>userSettings;
@property (nonatomic,weak)id<DisclaimerDelegate>disclaimerDelegate;
@end

@implementation LoginProcessListener


-(instancetype)initWithUserSettings:(id<UserSettingsDelegate>)userSettings andDisclaimerDelegate:(id<DisclaimerDelegate>)disclaimerDelegate{
    self = [super init];
    if (self){
        [self setUserSettings:userSettings];
        [self setDisclaimerDelegate:disclaimerDelegate];
    }
    return self;
}

-(void)onLoginAuthenticationProcessFinished{
    User *user = [_userSettings getUserDetails];
    if(user && [_disclaimerDelegate isConfirmedDisclaimer:[user disclaimerInfo]]){
        [_disclaimerDelegate confirmedDisclaimer];
    }else {
        [_disclaimerDelegate needDisplayDisclaimer];
    }
}

-(void)onLoggedInUserDetailsReceived:(User *)user{
    [_userSettings saveUserDetails:user];  <== here my _userSettings is already nil;
}

5 个答案:

答案 0 :(得分:1)

  

当我调试应用程序时,应用程序不想继续,当我调用委托时,因为我的委托已经在它到达该方法时被释放

但这是你的错误,你必须追查。代表的全部意义在于,您必须不允许它在其代表所在的事物之前死亡。一般来说,如果一个代表在另一个事情发生之前去世,那么你做错了什么;只要需要,代表的工作就是生活。

另一方面(总有一个&#34;另一只手&#34;)你所谓的委托可能不是真正的委托。如果它是一个主要存在自己的对象,它或多或少是一个委托。如果它只是一个值包或一个纯粹的辅助对象,其唯一目的是与保持对它的引用的对象相关联,那么它不是委托,强引用是正确的。

答案 1 :(得分:0)

你的架构是错误的。它应该是:

@interface UserSettings
@property (nonatomic,weak)id<UserSettingsDelegate>userSettingsDelegate;
@end

@interface Disclaimer
@property (nonatomic,weak)id<DisclaimerDelegate>disclaimerDelegate;
@end

实际上你的userSettings不是真正的委托,它只是LoginProcessListener的成员,所以你可以使用强引用。

通常id<UserSettingsDelegate>创建UserSettings并在其上创建自己的引用。如果UserSettings将在id<UserSettingsDelegate拥有引用,那么将会保留循环。

答案 2 :(得分:0)

  

当我调试应用程序时,应用程序不想继续,当我打电话给   委托,因为我的代表已被解除分配   它达到了那个方法

您在寻找的是一个弱定义属性的行为方式。

通过文档:https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html#/apple_ref/doc/uid/TP40011210-CH5-SW30

  

因为弱引用不会使对象保持活动状态,所以引用它是可能的   在引用仍在使用时要释放的对象。避免危险   悬挂指向最初由现在释放的对象占用的内存,一个弱点   当对象被解除分配时,引用自动设置为nil。

简单地说,如果不再分配代表,那么它将在其他地方发布。

话虽如此,你的bug根本不存在,但在其他地方。您将不得不回溯_userSession来验证它的创建位置以及哪些对象具有强引用权。

答案 3 :(得分:0)

允许使用对代理的强引用,但是您需要了解正在发生的事情,因为它有您应该注意的风险。

Apple的文档掩盖了这一点,只是说委托用户保持与代表的弱链接,一般情况下这是真的,但有些情况,你的可能是其中之一,保持有意义强烈的参考。通常,这发生在异步代码中,否则代理可能会在使用之前被释放。

挥手太多:这是一个具体的例子。 NSURLConnection需要委托来执行异步IO。为了使其可靠并大大简化其使用,NSURLConnection只要需要,就会保留对其委托的强引用。以下是NSURLConnection文档的引用: -

“注意:在请求期间,连接保持对其委托的强引用。当连接完成加载,失败或被取消时,它会释放该强引用”

简单来说,在ARC下,“发布强引用”可能意味着将属性设置为nil。

摘要:如果您知道自己在做什么,请在必要时使用强引用,并记录好以便下一个看到该代码的人理解您为什么这样做。

答案 4 :(得分:0)

你可以对委托对象进行强有力的引用,这很酷的是NSURLConnectionDelegate协议所做的事情。我是这样做的:

我们说我有一个下载图像的协议。

@implementation AsyncImageLoadManager <NSURLConnectionDelegate>

static char delegateStrongReferenceKey;
.......
-(void)startDownload {
    ....
    objc_setAssociatedObject(self, &delegateStrongRefernceKey, _delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    _imageConnection = [NSURLConnection connectionWithRequest:request delegate:self];

    objc_setAssociatedObject(self, &delegateStrongRefernceKey, _delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    _downloadTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ //I use tihs instead of [connection start]; so that download continues if app goes to background
        [_imageConnection cancel];
        [[UIApplication sharedApplication] endBackgroundTask:_downloadTaskID];
        _downloadTaskID = UIBackgroundTaskInvalid;
        objc_setAssociatedObject(self, &delegateStrongRefernceKey, nil, OBJC_ASSOCIATION_ASSIGN);
    }];

}

..../// and in connectionDidFinishLoading and connectionDidFailWithError you release the strong reference with:
[[UIApplication sharedApplication] endBackgroundTask:_downloadTaskID];
_downloadTaskID = UIBackgroundTaskInvalid;
objc_setAssociatedObject(self, &delegateStrongRefernceKey, nil, OBJC_ASSOCIATION_ASSIGN);