[已解决,提供解决方案] 异步请求时block递归的问题,求帮忙看下。

freecunix 发布于 2013年11月08日 | 更新于 2013年11月09日
无人欣赏。

需求:

从后台请求数据,一共多少条不固定。每次最多可以请求10条(请求时传递本次请求的数据启始位置,本次请求的数据个数),每次请求会返回(数据总数,本次返回的数据启始位置,本次返回的数据个数)。我现在想请求所有数据。有什么好的解决思路吗?

我的方法:

每次请求10条数据,请求成功后继续请求下10条。直到请求完所有数据。

数据请求成功后通过block回调通知,所以使用了block递归。用动画写了一个模仿的代码,如下:

#import "ViewController.h"
typedef  void (^myBlock)(int );
@interface ViewController ()
//测试用的视图,用闪烁此视图模仿请求。
@property (strong, nonatomic) IBOutlet UIView *testView;
@end
@implementation ViewController
//一个按钮,点击开始模仿请求
- (IBAction)blockTest:(id)sender
{
    //假如请求10次
    [self blockRecursion:0];
}
//模仿请求,n-请求数据开始位置
- (void)blockRecursion:(NSInteger)n
{
    __weak myBlock __block block = ^(int bn)
    {
        NSLog(@"%d", bn);
        //大于10次就退出
        if(bn < 10)
        {
            //请求,成功后回调自己(block)
            [self getData:block num:++bn];
        }
    };
    block(0);
}
//模仿一次网络请求,成功后通过block回调
- (void)getData:(myBlock)block num:(NSInteger)bn
{
    self.testView.alpha = 1;
    [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.testView.alpha = 0;
    } completion:^(BOOL finished) {
        block(bn);
    }];
}
@end

问题:

[self getData:block num:++bn]; 这一行有一个警告:Capturing 'block' strongly in this block is likely to lead to a retain cycle

怎么消除?(是因为Blocks 会自动 retain,所以才报retain cycle吧?加__weak?)

如果我在定义block时:myBlock __weak __block block = ^(int bn)... 警告会消失,但是getData回调时候程序会崩溃。(是因为异步请求所以block被释放了吗?)

求指点。

解决方案

(http://stackoverflow.com/questions/19853878/objective-c-uiview-animate-block-recursion)

The problem with your code is that there is no strong reference to the block object. block is a weak reference. So after the statement where the block object is created, the block object can potentially be deallocated, which will set the __weak variable block to nil. Invoking a block with a nil block pointer crashes.

When you remove the __weak, you create a retain cycle -- the block retains a strong reference to itself.

What you need is two variables, one strong and one weak. The block captures the weak one. The one the block captures needs to be __block to reflect the assignment after the block was created.

myBlock blockStrong;
__block __weak myBlock blockWeak;
blockWeak = blockStrong = ^(int bn)
{
    NSLog(@"%d", bn);
    if(bn < 10)
    {
        [self getData:blockWeak num:++bn];
    }
};
共13条回复
nickel 回复于 2013年11月08日

有点钻牛角尖了,没仔细看你的代码,感觉简单问题复杂化了。

1)是不是真的只能每次10条数据。如果数据量不大,尽可能一次取完客户端分批处理,尽量不要这样反复取,流程复杂,而且服务器请求次数多。如果数据不大,尽量先改服务端的限制。

2)假设真的数据大,那为什么又需要循环连续取完,而不是根据客户端需要的时候再取?不理解啊,这种思路很奇怪,自己找自己麻烦。

3)就算真的是数据大而且又有特殊原因要连续取完,也不该用递归那么愚蠢复杂的逻辑,你以为在做本地算法啊?!第一次请求获取总的数据条数,然后将条数除于每次最高限额的条数等于请求数,然后生成这么多个请求按顺序丢到请求队列里,如果队列是单任务的,那不需要排序,收到一条才会发下一条,顺序处理接可以。如果不确定是单任务还是并行多任务的话也很简单,每条请求里假如一个顺序号(HTTP协议里本来就有一个header可以用利用作顺序号用途,可以利用而不需要自己弄),接受到回复时按照顺序号来排列就可以了,如果数据本身就有顺序号那更简单。

总结,你的想法太混乱了,感觉是用本地逻辑来处理网络通讯。还是尽量用简单思路来解决吧。

freecunix 回复于 2013年11月08日

1楼 @nickel

1:服务器改不了,不是我的,用的开放api。

2:MD不提供一次取的api,只能一次最多取下100条。。

3:谢谢,我也在改成这样,不过如果能这样递归请求其实代码简单不少。。。但是我自己也觉得你的方法3这样好一些。

** 数据量不大,最多也就几百上千条,而且一条1K不到。

freecunix 回复于 2013年11月08日

1楼 @nickel 但是我还是想知道上面为什么警告以及为什么崩溃。。

nickel 回复于 2013年11月08日

网络通讯流程不要用递归!这不是简单与否的问题,我刚才已经说了,你是用本地逻辑去做网络通讯,不好!

我根本对你用的递归方法没兴趣,这在我看来是不能接受的最烂思路之一,所以抱歉,我不会帮你看代码细节。

freecunix 回复于 2013年11月08日

4楼 @nickel 没关系。。我现在也觉得不大能接受~ 代码不是网络的,是个例子,用递归的ui动画。

关于本地逻辑去做网络通讯,就只能这样了。这事我左右不了,一个大公司的开放api。人家不鸟我。。

freecunix 回复于 2013年11月08日

4楼 @nickel ^^ 在不考虑这个需求的前提下,我想知道 __weak __block 对 block的影响,以及为什么retain cycle,为什么会崩溃。 我觉得我应该去看看oc中block的实现。。。

出问题我就总是想知道原因,搞不明白就睡不着。。。

清醒疯子 回复于 2013年11月08日

4楼 @nickel

或者你告诉他个文档,让他去啃:)

6楼 @freecunix

然后你啃完整个笔记出来共享一下:):):)

vikiliu0310 回复于 2013年11月08日

我现在也用的一个开源库,刚好也是网络请求里面递归- -!不过我的比你这里简单,我哪里是下载的时候递归,局部变量递归次数过多导致栈溢出崩溃。你这个感觉也是差不多的东西了,它没完成递归,内存没释放- -!

nickel 回复于 2013年11月08日

我非常反对网络通讯采用递归设计。为什么?因为递归流程很可能过分依赖服务器的回复,但是网络通讯最根本一条原则就是你不能信任网络传输的可靠性,也许网络断了,也许服务器的数据不完整或者出错,这样就有可能造成递归里出现莫名其妙的问题。所以最好的方法还是保险,一切发起请求的处理是在客户端完全可控的前提下进行的,但递归有很多边界条件需要考虑,不完全可控。

至于retain cycle,还是自己看吧,弄明白你会增进不少,学东西就是得一边猜一边摸索。不过关键是其实我还没大量使用ARC,所以不好回答,呵呵。

freecunix 回复于 2013年11月08日

9楼 @nickel 恩。已经很感谢了,之前下不了决心写成并发的,觉得请求回来数据还要排序,而且还要用下载队列管理。。很麻烦。

看你说完我就改了,现在已经ok了,非常好用。^^

至于retain cycle,我还在看。。现在用block的地方越来越多,好好了解下。。 ^^

nickel 回复于 2013年11月09日

10楼,“而且还要用下载队列管理”?不需要啊,你用什么库做的啊,一般的http库都包含的队列管理了,这是基本功能,我用的是ASI那个。除非你自己些通讯库。至于排序,那是很简单的事。

freecunix 回复于 2013年11月09日

11楼 @nickel 恩,我也用的asi,里面带下载队列。排序确实很简单,完全出乎想想之外。。~~ 可能以前没做过类似的,就觉得很难,做完发现简单的不行。。。

freecunix 回复于 2013年11月09日

11楼 @nickel 搞定了!

myBlock blockStrong;
__block __weak myBlock blockWeak;
blockWeak = blockStrong = ^(int bn)
{
    NSLog(@"%d", bn);
    //大于10次就退出
    if(bn < 10)
    {
        //请求,成功后回调自己(block)
        [self getData:blockWeak num:++bn];
    }
};

本帖有13个回复,因为您没有注册或者登录本站,所以,只能看到本帖的10条回复。如果想看到全部回复,请注册或者登录本站。

登录 或者 注册
[顶 楼]
|
|
[底 楼]
|
|
[首 页]