So *_* It 19 delegates memory-management objective-c objective-c-blocks automatic-ref-counting
我有一个类从URL检索JSON并通过协议/委托模式返回数据.
MRDelegateClass.h
#import <Foundation/Foundation.h>
@protocol MRDelegateClassProtocol
@optional
- (void)dataRetrieved:(NSDictionary *)json;
- (void)dataFailed:(NSError *)error;
@end
@interface MRDelegateClass : NSObject
@property (strong) id <MRDelegateClassProtocol> delegate;
- (void)getJSONData;
@end
请注意,我正在使用strong我的委托属性.以后更多关于......
我正在尝试编写一个"包装"类,它以基于块的格式实现getJSONData.
MRBlockWrapperClassForDelegate.h
#import <Foundation/Foundation.h>
typedef void(^SuccessBlock)(NSDictionary *json);
typedef void(^ErrorBlock)(NSError *error);
@interface MRBlockWrapperClassForDelegate : NSObject
+ (void)getJSONWithSuccess:(SuccessBlock)success orError:(ErrorBlock)error;
@end
MRBlockWrapperClassForDelegate.m
#import "MRBlockWrapperClassForDelegate.h"
#import "MRDelegateClass.h"
@interface DelegateBlock:NSObject <MRDelegateClassProtocol>
@property (nonatomic, copy) SuccessBlock successBlock;
@property (nonatomic, copy) ErrorBlock errorBlock;
@end
@implementation DelegateBlock
- (id)initWithSuccessBlock:(SuccessBlock)aSuccessBlock andErrorBlock:(ErrorBlock)aErrorBlock {
    self = [super init];
    if (self) {
        _successBlock = aSuccessBlock;
        _errorBlock = aErrorBlock;
    }
    return self;
}
#pragma mark - <MRDelegateClass> protocols
- (void)dataRetrieved:(NSDictionary *)json {
    self.successBlock(json);
}
- (void)dataFailed:(NSError *)error {
    self.errorBlock(error);
}
@end
// main class
@interface MRBlockWrapperClassForDelegate()
@end
@implementation MRBlockWrapperClassForDelegate
+ (void)getJSONWithSuccess:(SuccessBlock)success orError:(ErrorBlock)error {
    MRDelegateClass *delegateClassInstance = [MRDelegateClass new];
    DelegateBlock *delegateBlock = [[DelegateBlock alloc] initWithSuccessBlock:success andErrorBlock:error];
    delegateClassInstance.delegate = delegateBlock; // set the delegate as the new delegate block
    [delegateClassInstance getJSONData];
}
@end
我最近才进入了客观世界(仅生活在ARC时代,并且仍然与块一起使用)并且不可否认,我对内存管理的理解是在较为薄弱的一面.
这段代码似乎工作正常,但只有我有我的委托strong.我知道我的代表应该weak避免潜在的保留周期.查看工具,我发现随着持续呼叫,分配不会继续增长.但是,我认为"最佳实践"是有weak代表.
问题
Q1)拥有strong代表是否"没问题"
Q2)我怎样才能实现基于块的包装器,将底层类的weak委托作为委托(即阻止*delegateBlock在接收协议方法之前被解除分配)?
CRD*_*CRD 15
Q1  - 是的.正如您所指出的那样,自己的委托属性很弱是建议帮助避免保留周期.因此,拥有一个强大的代表本身并没有什么不妥,但如果你班级的客户希望它很弱,你可能会给他们带来惊喜.更好的方法是保持委托较弱,并使服务器端(具有委托属性的类)在内部为其需要的时间段保留强引用.正如@Scott所指出的那样,苹果公司正在为此做准备NSURLConnection.当然,这种方法无法解决您的问题 - 您希望服务器为您保留代理...
Q2  - 从客户端看,问题是如何保持委托活着,只要具有弱引用的服务器需要它.这个问题有一个标准的解决方案叫做关联对象.简而言之,Objective-C运行时实质上允许对象的密钥集合与另一个对象相关联,以及关联策略,该策略指出该关联应该持续多长时间.要使用此机制,您只需选择自己的唯一键,void *即类型- 即地址.以下代码大纲显示了如何使用它NSOpenPanel作为示例:
#import <objc/runtime.h> // import associated object functions
static char myUniqueKey; // the address of this variable is going to be unique
NSOpenPanel *panel = [NSOpenPanel openPanel];
MyOpenPanelDelegate *myDelegate = [MyOpenPanelDelegate new];
// associate the delegate with the panel so it lives just as long as the panel itself
objc_setAssociatedObject(panel, &myUniqueKey, myDelegate, OBJC_ASSOCIATION_RETAIN);
// assign as the panel delegate
[panel setDelegate:myDelegate];
关联策略OBJC_ASSOCIATION_RETAIN将保留传入的object(myDelegate),只要与它关联的对象(panel)然后释放它.
采用此解决方案可避免使委托属性本身变强,并允许客户端控制是否保留委托.如果您还在实现服务器,您当然可以提供一种方法来执行此操作,可能是associatedDelegate:?,以避免客户端需要定义密钥并调用objc_setAssociatedObject自身.(或者您可以使用类别将其添加到现有类中.)
HTH.
new*_*cct 13
它完全取决于对象的体系结构.
当人们使用弱委托时,这是因为委托通常是某种"父"对象,它保留了具有委托的东西(让我们称之为"委托人").为什么它必须是父对象?它不一定是; 然而,在大多数使用案例中,它被证明是最方便的模式.由于委托是保留委托者的父对象,因此委托人不能保留委托,或者它将具有保留周期,因此它拥有对委托的弱引用.
但是,这不是唯一的使用情况.举个例子来说,UIAlertView和UIActionSheetiOS中.使用它们的常用方法是:在函数内部,创建带有消息的警报视图并向其添加按钮,设置其委托,执行任何其他自定义,调用-show它,然后忘记它(它不存储在任何地方) .这是一种"火与忘"的机制.一旦你show,你不需要保留它或任何东西,它仍然会显示在屏幕上.在某些情况下,您可能希望存储警报视图,以便以编程方式关闭它,但这种情况很少见; 在绝大多数用例中,您只需显示并忘记它,并只处理任何委托调用.
所以在这种情况下,正确的样式将是一个强委托,因为1)父对象不保留警报视图,因此保留周期没有问题,2)委托需要保持在周围,以便当在警报视图上按下某个按钮时,有人会在周围响应它.现在,很多时候,#2不是问题,因为委托(父对象)是某种视图控制器或其他东西保留的东西.但情况并非总是如此.例如,我可以简单地使用一个不属于任何视图控制器的方法,任何人都可以调用该方法来显示警报视图,如果用户按下是,则将某些内容上传到服务器.由于它不是任何控制器的一部分,它可能不会被任何东西保留.但它需要保持足够长的时间,直到警报视图完成.理想情况下,警报视图应该有一个强烈的参考.
但正如我之前提到的,这并不总是你想要的警报视图; 有时你想保留它并以编程方式解雇它.在这种情况下,您需要弱委托,否则将导致保留周期.那么警报视图是否应该有强或弱的代理?那么,来电者应该决定!在某些情况下,来电者想要强大; 在其他人中,来电者想要弱.但这怎么可能呢?警报视图委托由警报视图类声明,并且必须声明为强或弱.
幸运的是,有一个解决方案可以让调用者决定 - 基于块的回调.在基于块的API中,块基本上成为委托; 但该块不是父对象.通常,块在调用类中创建并捕获,self以便它可以对"父对象"执行操作.委托人(在这种情况下为警报视图)始终具有对该块的强引用.但是,块可能具有对父对象的强引用或弱引用,具体取决于块在调用代码中的写入方式(捕获对父对象的弱引用,不要self直接在块中使用,而是,创建一个弱版本self在块之外,让块使用它代替).通过这种方式,调用代码可以完全控制委托者是否具有强引用或弱引用.
Sco*_*ets 10
你是正确的,代表通常被弱引用.但是,有些用例需要强引用,甚至是必要的.Apple在NSURLConnection中使用它:
在下载期间,连接保持对代理的强引用.当连接完成加载,失败或被取消时,它会释放该强引用.
一个NSURLConnection实例只能使用一次.完成后(失败或成功),它释放委托,并且由于委托是readonly,它不能(安全地)重用.
你可以做类似的事情.在你dataRetrieved和dataFailed方法,将您的委托nil.readonly如果要重用对象,则可能不需要创建委托,但是必须再次分配委托.
| 归档时间: | 
 | 
| 查看次数: | 9388 次 | 
| 最近记录: |