美文网首页
[iOS] __weak 和 WKWebView 在 relea

[iOS] __weak 和 WKWebView 在 relea

作者: BudSwift | 来源:发表于2018-11-08 08:44 被阅读17次

    __weak

    因为在业务开发中的需求需要几个局部变量,考虑到:

    • 局部变量需要先声明在一个 block 变量内使用后再构造实例赋值,因此变量声明为 __block
    • 局部变量构造完成实例后会持有此 block,为避免循环引用,将局部变量声明为 __weak
    • 变量最终被装入数组被控制器强引用。

    先定义对象 XRow 的基本信息,内部有一个字典,用于保存 block 。

    static NSString *const kBlockKey = @"kBlockKey";
    @interface XRow : NSObject
    @property (nonatomic, strong) NSMutableDictionary *value;
    @end
    
    @implementation XRow
    - (NSMutableDictionary *)value {
        if (!_value) {
            _value = [NSMutableDictionary dictionaryWithCapacity:1];
        }
        
        return _value;
    }
    
    - (void)dealloc {
        NSLog(@"dealloc %@", self);
    }
    
    @end
    

    接着定义测试的逻辑:

    • 创建对象
    • 传入 block
    • 保存到数组

    示例代码分三种情况测试如下:

    • 直接赋值 __weak,查看赋值结果
    • 声明 __block __weak 装入数组
    • 声明 __block 再进行 __weak 引用,装入数组
      分别进行 debug 和 release 的 build mode 运行:
    @interface ViewController ()
    @property (nonatomic, strong) NSMutableArray *array;
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self test__block__weak_1];
        NSLog(@"tested");
    }
    
    - (void)testAssignWeak {
        __weak id obj = nil;
        __weak id obj2 = nil;
        __weak id obj3 = nil;
        obj = [self makeRow];
        obj2 = [self makeRow];
        obj3 = [[XRow alloc] init]; // 直接收到编译器警告
        NSLog(@"obj %@", obj);
        NSLog(@"obj2 %@", obj2);
    }
    
    - (void)test__block__weak_1 {
        __block __weak XRow *row1 = nil;
        __block __weak XRow *row2 = nil;
        
        void(^block)(void) = ^{
            // 处理 row1 row2 的一些逻辑
            NSLog(@"row1 %@ row2 %@", row1, row2);
        };
        
        row1 = [self makeRow];
        row1.value[kBlockKey] = block;
        
        row2 = [self makeRow];
        row2.value[kBlockKey] = block;
        
        [self.array addObject:row1];
        [self.array addObject:row2];
        NSLog(@"array -> %@", self.array);
    }
    
    - (void)test__block__weak_2 {
        __block XRow *row1 = nil;
        __block XRow *row2 = nil;
        
        // 这样 处理 在 block 内部是 nil,没有 __block 效果
        __weak typeof(XRow *) weakRow1 = row1;
        __weak typeof(XRow *) weakRow2 = row2;
        void(^block)(void) = ^{
            // 处理 row1 row2 的一些逻辑
            NSLog(@"row1 %@ row2 %@", weakRow1, weakRow2);
        };
        
        row1 = [self makeRow];
        row1.value[kBlockKey] = block;
        
        row2 = [self makeRow];
        row2.value[kBlockKey] = block;
        
        [self.array addObject:row1];
        [self.array addObject:row2];
        NSLog(@"array -> %@", self.array);
    }
    
    - (XRow *)makeRow {
        return [[XRow alloc] init];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        NSLog(@"touch to excute");
        for (XRow *some in self.array) {
            void(^block)(void) = some.value[kBlockKey];
            if (block) block();
        }
        [self.array removeAllObjects];
        NSLog(@"removed");
    }
    
    - (NSMutableArray *)array {
        if (!_array) {
            _array = [NSMutableArray arrayWithCapacity:2];
        }
        
        return _array;
    }
    
    @end
    

    可以发现:

    • 直接 alloc] init] 的对象在复制后立刻销毁,并会收到编译器警告

    • 在 release mode 下 __weak 的表现和 debug 有所差异,,通过 makeRow 返回的对象,在 debug mode 下并不会立刻释放,而是可以在方法体内持续持有引用,从而达到了预期的目的

    • 然而在 release mode 下,即便是通过 makeRow 返回的对象在赋值给 weak 的引用时也立即释放导致异常。

    • 解决方法
      解决方法是先使用强引用的局部变量持有新构造的对象,再赋值给 __block __weak 的变量,并在新构造的实例失去强引用之前,立刻对该变量进行一次变量的强引用(比如加入到数组中),如下:

    - (void)test__block__weak_3 {
        __block __weak XRow *row1 = nil;
        __block __weak XRow *row2 = nil;
        
        void(^block)(void) = ^{
            // 处理 row1 row2 的一些逻辑
            NSLog(@"row1 %@ row2 %@", row1, row2);
        };
        
        XRow *strong = nil;
        strong = [self makeRow];
        row1 = strong;
        row1.value[kBlockKey] = block;
        [self.array addObject:row1];
        // 使用一个强引用的局部变量
        //并在下一步失去 strong 的强引用之前立即装入数组进行强引用。
        
        strong = [self makeRow];
        row2 = strong;
        [self.array addObject:row2];
        row2.value[kBlockKey] = block;
        
        NSLog(@"array -> %@", self.array);
    }
    

    WKWebView

    再基于 WKWebView 封装的控制器中,为 WebView 加载了一个自定义了 Post 请求体的 NSURLRequest 对象,这是接入一个第三方支付 URL 的场景,接入完成后,在 debug 模式下 各个系统和设备运行良好,但是在 release 模式下,iOS 8/9/10 的设备显示空白,提示错误

    WKErrorDomain code 102 帧框加载失败

    这种情况很可能是请求体传输不完整导致的,但是针对不同系统的差异原因在哪里,暂时无法直接解决。
    间接的解决方法,使用 UIWebView,是的,使用 UIWebView 在 debug/ release 下均表现正常。

    小结

    不论怎么样,给测试同学的包一定要给 release 的,有些情况即便没有手动判断是否 DEBUG 宏也会表现异常,测试同学发现了异常的情况要引起重视。

    PS:关于在 Xcode 切换 debug / release

    • 第一步,在左上角的程序名称,选择 edit_scheme

    相关文章

      网友评论

          本文标题:[iOS] __weak 和 WKWebView 在 relea

          本文链接:https://www.haomeiwen.com/subject/llsixqtx.html