必赢的网址登录 > 必赢网址 > 但不光只是解答难题,日常说的block无法改变一些

原标题:但不光只是解答难题,日常说的block无法改变一些

浏览次数:54 时间:2019-10-01

学习block帖子ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify

http://www.jianshu.com/p/f4fa7aeb2035

在阅读那篇作品以前,首先思量如下难题:

Objective CBlock是二个很实用的语法,极其是与GCD结合使用,能够很有益地促成产出、异步职分。不过,即便使用不当,Block 也会孳生一些循环援引难题(retain cycle)—— Blockretain ‘self’,而 ‘self‘retainBlock。因为在 ObjC 中,直接调用三个实例变量,会被编写翻译器处理成‘self->theVar’’self’ 是一个 strong 类型的变量,援引计数会加 1,于是,self retains queuequeue retains blockblock retains self

相似说的block不只怕转移部分变量,必需加__block才可以

  1. 为什么 weak strong dance 可避防止循环援用?
  2. 干什么不直接利用weak?
  3. 接纳 weak strong dance 的没有错姿势?

Apple 官方的提出是,传进Block之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 Block 中就不会现出对self 的强援引。若是在Block施行到位从前,self 被假释了,weakSelf 也会化为nil

确切的身为不恐怕改观部分变量的地址,假使该有的变量是四个Student类的靶子,你不可能再block中校其=nil,但是足以修改.name属性.因为=nil时其实是更换了他的内部存款和储蓄器地址,而修改.name不会转移内部存款和储蓄器地址

本文从 weak strong dance 的** 由来 用途 原理 扩展** 稳步深入分析解答上述难点,但不光只是解答标题。

亲自去做代码:

__weak typeof(self) mv=self;符合规律是如此


 __weak __typeof__ weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [weakSelf doSomething]; });

=================================

由来

在iOS开辟中,无论objective-c依然swift都以运用援引计数来管理内部存款和储蓄器。而巡回引用毕竟接纳引用计数法管理内部存款和储蓄器中相比为难的三个主题素材。在MRC时期,因为对象的援用计数是由程序员本人来调节,优良的技师能够熟习的把控对象之间的有所关系;到了ARC和swift中,由于编写翻译器接手了内存管理的干活,为了有协理程序猿调整“对象时期的保有关系”,苹果于2011 WWDC Session #322中建议weak strong dance ,官方示例代码如下

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:_observer];
}

- (void)loadView
{
  [super loadView];

  __weak TestViewController *wself = self;
  _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
                                                                object:nil
                                                                 queue:nil
                                                            usingBlock:^(NSNotification *note) {
      TestViewController *sself = wself;
      [sself dismissModalViewControllerAnimated:YES];
  }];
}

clang 的文书档案表示,在doSomething 内,weakSelf不会被放走。但,上边包车型地铁情事除了:

MASonry中利用self不须要弱援用 互联网封装中也不需求弱引用

用途

在运用block时,因为block会捕获外界变量,并将其拷贝到block中。ARC 下这里的变量要是是指针变量,会将指针变量所指向的目的的援引计数加一。 由此如若不是对block有自然领会很轻便生出循环引用,在那篇小说里有介绍会发生循环援用的光景。
在RAC及swift中,为了幸免block带来的巡回援用,官方推荐 weak strong dance ,即 在block外界声惠氏(WYETH)个弱援用对象,在block内部宣称叁个部分变量强持有那一个弱援引,通过运用新生成局部变量来制止循环援引。


 __weak __typeof__ weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [weakSelf doSomething]; [weakSelf doOtherThing]; });

但不光只是解答难题,日常说的block无法改变一些变量。MJRefresh中必定要弱援引)

原理

聊到原理,得从block的内部结构谈起:

图片 1

block内部结构

当中invoke指向block的达成代码,variables保存block捕获到的外表变量。

今昔来剖判引言中的示例代码:

  1. block 会将wself捕获到variables中,因为是weak修饰的,由此block不会对self进行强援引;同时block 中的 invoke (函数指针)会指向 block 中的完成代码。
  2. 在执行block时(即调用invoke),TestViewController *sself = wself; 才会实施,固然实施那行代码时self还未释放,那么这里会将TestViewController实例的援用计数加一,幸免在调用self时期self已经被放出。当以此法子实行完,ARC会自动将援用计数减一。
  3. 就算在施行block时,self已经放出,即wself被置为nil。那么 TestViewController *sself = wself; 实行时sself获得的也是贰个nil,因而 [sself dismissModalViewControllerAnimated:YES]; 将不会被实施。
  4. 即便具有的时候候都和3中一律只是“不实行”,那该有多好。不过结果往往不如人意。不相信? 测量试验代码如下:
///WeakTestObject.h
typedef void(^test_block)(void);
@interface WeakTestObject : NSObject
/**
 *  <#summary#>
 */
@property (copy,nonatomic) test_block block;
@end

///ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];

    WeakTestObject *obj = [[WeakTestObject alloc]init];
    obj.block = ^{
        NSLog(@"execute block in obj");
    };
    __weak __typeof(obj) wObj = obj;
    test_block VcBlock = ^{
        WeakTestObject *sObj = wObj;
        sObj.block();
    };
    obj = nil;
    VcBlock();    
}

VcBlock 在实行从前,obj已经释放,导致实施 VcBlock 过程中 sObj 以及 sObj.block 均为nil。程序进而crash在 sObj.block(); 这里。 实际情况往往比这里的模仿代码复杂非常多,恐怕会透过一回时空的跨度;那么如何避免这种crash呢?

三种管理:

  • 1 、对 sObj.block 进行剖断
test_block block = ^{
        WeakTestObject *sObj = wObj;
        if (sObj.block) {
            sObj.block();
        }
    };
  • 2、 对 sObj 举行推断
test_block block = ^{
        WeakTestObject *sObj = wObj;
        if (sObj) {
            sObj.block();
        }
    };

公开地方第三种管理更优。首先饿哦们不须求对sObj的每贰个痛快进行判别,其实 在采纳sObj 时 ,往往亦非独有推行它的三个block属性,何况会涉嫌到block嵌套或别的种种坑爹意况,其次依据接口密封原则大家也不应有过多去关怀类的贯彻。

最后 weak strong dance 的科学姿势如下:

__weak __typeof(obj) wObj = obj;
    test_block block = ^{
        WeakTestObject *sObj = wObj;
        if (sObj) {
            /// do ...
        }
    };

doSomething中,weakSelf 不会化为 nil,不过在doSomething 推行到位,调用第一个艺术 doOtherThing 的时候,weakSelf有望被假释,于是,strongSelf 就派上用场了:

ATTNetWork中successBlock和faildBlock出现Self没有须求弱援用.. 正确是ATTNetWork强引用Self,所以当在VC中呼吁还没做到,然后pop退出当前VC,会促成VC先不自由,等到Success或许faild的时候,实行完successBlock或许faildBlock之后VC才会销毁 假使

扩展

 __weak __typeof__ weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __strong __typeof strongSelf = weakSelf; [strongSelf doSomething]; [strongSelf doOtherThing]; });

=================================

1. RAC中的宏

因为RAC中山高校量采纳block语言,为了有帮助开采者RAC中定义了一对宏 @weakify() @strongify() ,对于那对宏的具体解析可观察哀殿的 那篇小说 ,文中涉及“Xcode 错过了不当提示的力量”这一难题
别的YYKit中也定义了近乎的宏,同期防止了上述难题,如下

#ifndef weakify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
        #endif
    #endif
#endif

#ifndef strongify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
        #endif
    #endif
#endif

这里补充另二个坑(请在乎如下二种调用的不一致)

//1
[self.viewModel.resignSubject subscribeNext:^(id x) {
        @strongify(self)
        [self.keyBoard keyboardDown];
    }];
//2
[self.viewModel.resignSubject subscribeNext:^(id x) {
        @strongify(self)
        [_keyBoard keyboardDown];
    }];

在 1 中 self.keyBoard 中的self其实是被重定义的有的的“self”, 而大家由此 _keyBoard 调用的话,表面上就算看起来连self都不曾调用,更不会有哪些难题了。但,第两种写法其实是存在相当的大隐患的,系统在“寻觅” _keyBoard 那几个实例对象时,是由此对 self 指针举办地址偏移得到的,在那边编写翻译器可不会对那个self举办宏替换。

在RAC源码中还应该有对另一个宏 @unsafeify() 的使用

RACCompoundDisposable *selfDisposable = self.disposable;
    [selfDisposable addDisposable:otherDisposable];

    @unsafeify(otherDisposable);

    // If this subscription terminates, purge its disposable to avoid unbounded
    // memory growth.
    [otherDisposable addDisposable:[RACDisposable disposableWithBlock:^{
        @strongify(otherDisposable);
        [selfDisposable removeDisposable:otherDisposable];
    }]];

@unsafeify() 就是 __unsafe_unretained 在RAC中的宏,然而这种用法就算在RAC源码中出现的都极少,可是 __unsafe_unretained 对比 __weak 来讲在质量会相比较好。

__strong 确保在 Block内,strongSelf 不会被保释。

如果在myBlock中嵌套my2Block
self.testView.myBlock = ^{

2. 接口设计条件

先是,并不是关系到block援引外界对象的标题都会拉动循环援用;其次,假若是大家温馨陈设二个类的时候,应该尽量的幸免或者发生循环援用的标题(比如施行完block后将其置nil),假设实在无法幸免应该在接口里详细表明。
例如:苹果框架中UIView封装的 animateWithDuration 方法、GCD等都
不会拉动循环援引(注:NSTimer方法只怕会拉动循环引用); 还大概有局部家弦户诵的三方框架比方 Masonry 也不会发出循环援用。

  • Block 内如果急需拜谒 self的法门、变量,提议使用 weakSelf
  • 如果在Block 内须要频仍 访问self,则需求利用 strongSelf

NSLog(@"shuchu1");

3. swift 中的 weak strong dance
testFunc(closure:{ [weak self] in
            if let strongSelf = self {
                // do something
                print(strongSelf)
            }
        })

testFunc(closure:{ [weak self] in
            guard let strongSelf = self else {return}
            ///do something
            print(strongSelf)
        })

下边是依据可选绑定格局得来的较常规的写法,还也许有如下这种办法,可制止其它显示生成strongSelf

testFunc(closure:{ [weak self] in
            withExtendedLifetime(self, {
               print(self ?? 0)
            })
        })

只是经 withExtendedLifetime 管理后的self 变为了多个可选类型。

如上内容知道了小编们怎么要用weakSelfstrongSelf, 为了轻巧实用平日会定义成宏weaklystrongly,如下:

test2View.my2Block = ^{

方法一: ARC和MRC通用
 #ifndef weakify #if __has_feature #define weakify  _Pragma("clang diagnostic push")  _Pragma("clang diagnostic ignored "-Wshadow"")  autoreleasepool{} __weak __typeof__ __weak_##x##__ = x;  _Pragma("clang diagnostic pop") #else #define weakify  _Pragma("clang diagnostic push")  _Pragma("clang diagnostic ignored "-Wshadow"")  autoreleasepool{} __block __typeof__ __block_##x##__ = x;  _Pragma("clang diagnostic pop") #endif #endif #ifndef strongify #if __has_feature #define strongify  _Pragma("clang diagnostic push")  _Pragma("clang diagnostic ignored "-Wshadow"")  try{} @finally{} __typeof__ x = __weak_##x##__;  _Pragma("clang diagnostic pop") #else #define strongify  _Pragma("clang diagnostic push")  _Pragma("clang diagnostic ignored "-Wshadow"")  try{} @finally{} __typeof__ x = __block_##x##__;  _Pragma("clang diagnostic pop") #endif #endif

动用过RAC的同室应该都驾驭@weakify@strongify,这多个宏在RAC中是现已定义好的,能够直接用,属于相比牛逼的写法。那三个宏一定成对出现,先@weakify@strongify.能够很好的保管Block内部对self的引用。能够一步步点支出现其实使用到了C语言中的组合运算符。

 @weakify; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @strongify; [self doSomething]; [self doOtherThing]; });

self;

第二种: 比如RAC
#define weakify  autoreleasepool {}  metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)#define strongify  try {} @finally {}  _Pragma("clang diagnostic push")  _Pragma("clang diagnostic ignored "-Wshadow"")  metamacro_foreach(rac_strongify_,, __VA_ARGS__)  _Pragma("clang diagnostic pop")

来分解一下RAC怎么落到实处这种装X的写法。

他俩的功力至关心敬服如果在block内处对self的援引:

@weakify(); 定义了一个__weak的self_weak变量 [RACObserve subscribeNext:^(NSString *name) { @strongify(); 局域定义了一个__strong的指针指向self_weak .outputLabel.text = name; }]; 

这些宏为啥这么吊,前面加@,其实正是一个吗都没干的@autoreleasepool {}前方的十三分@,为了鲜明罢了。 还应该有metamacro_foreach_cxt, 大家一层一层的往里点

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)  metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) 

三番五次点下来

#define metamacro_concat  metamacro_concat_ #define metamacro_concat_ A ## B 

到终极, 才开采, 那不就是个C语言中组成运算符的么, 把2个运算符组合成为1个运算符。 然后回转眼睛, 他正是吧weak 以及第二步骤中#define rac_weakify_(INDEX, CONTEXT, VAR) CONTEXT typeof__ metamacro_concat(VAR, weak) = ;typedef东拼西凑进去- -

NSLog(@"shuchu2");

};

};
那么self.testView会强引用test2View,也会强援用self,test2View会强援引self

==============================

解决 retain circle

Apple 官方的提出是,传进 Block 在此之前,把 ‘self’ 转变来 weak automatic 的变量,那样在 Block 中就不会出现对 self 的强引用。假诺在 Block 实施到位从前,self 被放走了,weakSelf 也会化为 nil。

亲自过问代码:

1 __weak __typeof__(self) weakSelf = self;

2    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

3    [weakSelf doSomething];

4 });

clang 的文书档案表示,在 doSomething 内,weakSelf 不会被放走。但,上面包车型大巴场合除了:

1 __weak __typeof__(self) weakSelf = self;

2    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

3    [weakSelf doSomething];

4    [weakSelf doOtherThing];

5 });

在 doSomething 中,weakSelf 不会成为 nil,不过在 doSomething 施行到位,调用第1个章程 doOtherThing 的时候,weakSelf 有望被放走,于是,strongSelf 就派上用场了:

1 __weak __typeof__(self) weakSelf = self;

2    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

3    __strong __typeof(self) strongSelf = weakSelf;

4    [strongSelf doSomething];

5    [strongSelf doOtherThing];

6 });

__strong 确定保障在 Block 内,strongSelf 不会被放飞。

总结

在 Block 内假诺须要探访 self 的艺术、变量,建议使用 weakSelf。

要是在 Block 内须求一再 访问 self,则需求利用 strongSelf。

-------------------------------分割线---------------------------------------------

上述内容知道了我们为什么要用weakSelf和strongSelf, 为了简单实用日常会定义成宏weakify和strongify,如下:

View Code

运用过RAC的校友应该都通晓@weakify和@strongify,那四个宏在RAC中是一度定义好的,能够一贯用,属于相比牛逼的写法。这多少个宏一定成对出现,先@weakify再@strongify.能够很好的管理Block内部对self的引用。能够一步步点花费现其实使用到了C语言中的组合运算符。

1 @weakify(self);

2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

3      @strongify(self);

4      [self doSomething];

5      [self doOtherThing];

6  });

block中一旦用了下划线_的花样,其实用@weakify(self) 和@strongify(self)其实仍然拾分的

=====================================

看上边包车型客车例证:block中套block要这么写才不会循环援用

@weakify(self);
[[ATTNetwork shareInstance]getWithURL:GetHotListURL token:nil params:params success:^(id response) {

    @strongify(self);

} failure:^(NSError *error) {
    @strongify(self);

        @weakify(self);
        [self.backView showNoNetWorkWithTryAgainBlock:^{
            @strongify(self);
            [self loadDataFromCache:NO];
        }];
    }

}];

本文由必赢的网址登录发布于必赢网址,转载请注明出处:但不光只是解答难题,日常说的block无法改变一些

关键词:

上一篇:weapp-quick - 微信小应用示例代码,不定期更新必赢

下一篇:没有了