美文网首页iOS 坑的集中营iOS DeveloperIOS
创建的UIWindow为什么不显示

创建的UIWindow为什么不显示

作者: 踩坑小分队 | 来源:发表于2016-10-14 10:19 被阅读2205次

    创建了一个window却不显示,怎么个情况。
    相关代码如下:
    创建一个按钮,通过按钮的单击事件来创建window,创建的window是不需要添加到相关的控件的,只要创建就会添加到界面上。
    1、但是这里也创建window了,怎么没有显示?

    - (void)test2
    {
        UIWindow *myWindow3 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        myWindow3.backgroundColor = [UIColor greenColor];
        myWindow3.windowLevel = 100;
        myWindow3.hidden = NO;
        [myWindow3 makeKeyWindow];
        
        NSLog(@"1当前所有的window %@",[UIApplication sharedApplication].windows);
    }
    

    2、但是将window设置成员变量就能够如期的看到window,代码如下

    @interface ViewController ()
    @property(nonatomic,strong)UIWindow *myWindow1;
    @end
    

    通过按钮响应事件调用test1方法,会发现创建的window显示出来了

    // 将window设置成全局变量
    - (void)test1
    {
        self.myWindow1 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.myWindow1.backgroundColor = [UIColor redColor];
        self.myWindow1.windowLevel = -1;
        self.myWindow1.hidden = NO;
    }
    

    也就是说window必须要设置成成员变量才能被显示出来吗?为什么?继续进行以下相关的测试:
    3、没有声明成成员变量,单独创建的window类,直接调用没有显示

    // 单独创建UIWindow类
    - (void)test3
    {
        MyWindow1 *myWindow = [[MyWindow1 alloc] initWithFrame:[UIScreen mainScreen].bounds];
        myWindow.backgroundColor = [UIColor greenColor];
        myWindow.windowLevel = 100;
        myWindow.hidden = NO;
        [myWindow makeKeyWindow];
    }
    

    4、单独创建UIWindow类,然后设置成全局变量,创建window之后有window显示

    @interface ViewController ()
    @property(nonatomic,strong)MyWindow1 *myWindow;
    @end
    
    - (void)test4
    {
        self.myWindow = [[MyWindow1 alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.myWindow.backgroundColor = [UIColor greenColor];
        self.myWindow.windowLevel = 100;
        self.myWindow.hidden = NO;
        [self.myWindow makeKeyWindow];
    }
    

    5、创建单例window

    + (ShowWindow *)shareShowWindow
    {
        static ShowWindow *window = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (window == nil) {
                window = [[ShowWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
            }
        });
        
        return window;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.backgroundColor = [UIColor redColor];
            // 在window上面添加相关控件
            UIButton *tempBtn = [UIButton buttonWithType:UIButtonTypeSystem];
            tempBtn.frame = CGRectMake(100, 200, 100, 100);
            [tempBtn setTitle:@"点我消失" forState:UIControlStateNormal];
            tempBtn.backgroundColor = [UIColor greenColor];
            [tempBtn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
            [self addSubview:tempBtn];
        }
        return self;
    }
    
    - (void)clickBtn:(UIButton *)sender
    {
        self.hidden = YES;
    }
    
    - (void)show
    {
        [self makeKeyWindow];
        self.hidden = NO;
    }
    

    在ViewController中调用,创建的window显示了

    // 创建window的单例进行实验
    - (void)test5
    {
        ShowWindow *window = [ShowWindow shareShowWindow];
        [window show];
    }
    

    总结:通过以上的实验可以总结出,并不是只有成员变量才可以,单例也可以,他们的共同点就是生命周期足够长。
    这个时候产生一个疑问,单独创建的一个局部变量的button,添加在view上面怎么能够显示?UIWindow的父类虽然也是UIView,但是UIWindow的显示方式和view不一样。一般的view创建完了是需要添加到父控件上面的,对,就是这个"添加",父控件强引用了button,也就是button的引用计数+1了。
    刚刚上面也说了,"创建的window是不需要添加到相关的控件的,只要创建就会添加到界面上",window并没有添加到任何的地方。
    那么创建完了window可以找到吗?当然可以。通过以下代码可以找到创建的window。

    NSLog(@"2当前所有的window %@",[UIApplication sharedApplication].windows);
    

    我测试的时候也是这么观察的

    - (void)clickBtn:(UIButton *)sender
    {
        // 为了观察是不是新创建的window被当前的window挡住了
        AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
        app.window.alpha = 0.3;
        
        // 可以显示创建的window
        [self test1];
        
        // 不能正常显示创建的window
    //    [self test2];
        
        // 不能正常的显示创建的window
    //    [self test3];
        
        // 可以正常的显示创建的window
    //    [self test4];
        
        // 可以正常显示创建的window
    //    [self test5];
        
        NSLog(@"2当前所有的window %@",[UIApplication sharedApplication].windows);
    }
    

    在以上的实验中不同的方式添加window,添加完毕之后就可以查看windows中的情况了。
    比如调用完了test1,打印[UIApplication sharedApplication].windows就会发现多一个window,并且可以和test1创建的window比较一下,确认是一个window。调用完test3,会发现没有添加新的window。
    相关的Demo可以参考:https://github.com/RunOfTheSnail/UIWindowDemo

    这是为什么?难道创建完了window没有被添加进[UIApplication sharedApplication].windows中吗?猜想,没有被添加进去,新创建的window没有被[UIApplication sharedApplication].windows强引用,很可能仅仅是弱引用。
    做如下实验:
    还是通过按钮的点击事件调用的。

    // 测试创建的window和[UIApplication sharedApplication].windows的关系
    - (void)test6
    {
        self.myWindow2 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.myWindow2.backgroundColor = [UIColor greenColor];
        self.myWindow2.windowLevel = 100;
        self.myWindow2.hidden = NO;
        [self.myWindow2 makeKeyWindow];
        NSLog(@"查看1   %@",[UIApplication sharedApplication].windows);
        self.myWindow2 = nil;
        NSLog(@"查看2   %@",[UIApplication sharedApplication].windows);
    }
    

    打印结果如下:这个和想要的结果不一样,想要的结果是第一次打印两个window,第二次打印一个window,有点演砸了。。。。。。。。

    2016-10-14 10:06:57.459 UIWindowDemo[83896:1915917] 查看1   (
        "<UIWindow: 0x7fef83713620; frame = (0 0; 375 667); alpha = 0.3; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fef83714b40>; layer = <UIWindowLayer: 0x7fef83710f80>>",
        "<UIWindow: 0x7fef85010e30; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fef850122a0>; layer = <UIWindowLayer: 0x7fef85010570>>"
    )
    2016-10-14 10:06:57.460 UIWindowDemo[83896:1915917] 查看2   (
        "<UIWindow: 0x7fef83713620; frame = (0 0; 375 667); alpha = 0.3; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fef83714b40>; layer = <UIWindowLayer: 0x7fef83710f80>>",
        "<UIWindow: 0x7fef85010e30; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fef850122a0>; layer = <UIWindowLayer: 0x7fef85010570>>"
    )
    

    但是得考虑一个问题。短时间window可能会不释放的问题,进行再一次的实验
    创建另外一个测试按钮,通过点击事件来查看windows中的情况

     // 这个是配合着test6来测试的,查看windows数组的情况
        UIButton *myTempBtn = [UIButton buttonWithType:UIButtonTypeSystem];
        [myTempBtn setTitle:@"再点我,测试test2" forState:UIControlStateNormal];
        myTempBtn.frame = CGRectMake(100, 300, 300, 100);
        myTempBtn.backgroundColor = [UIColor greenColor];
        [myTempBtn addTarget:self action:@selector(clickMyTempBtn:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:myTempBtn];
    
    - (void)clickMyTempBtn:(UIButton *)sender
    {
        NSLog(@"查看3 %@",[UIApplication sharedApplication].windows);
    }
    
     查看3 (
        "<UIWindow: 0x7faa09439ec0; frame = (0 0; 375 667); alpha = 0.3; autoresize = W+H; gestureRecognizers = <NSArray: 0x7faa0943af80>; layer = <UIWindowLayer: 0x7faa094369e0>>"
    

    运行完test6之后,查看1和查看2打印的结果都是两个window,点击测试按钮之后,再次测试发现打印的是一个window,这下放心了。验证了自己的猜想。

    总结:
    1、创建的window不需要添加到任何的控件上就能显示,显示的规律是通过windowLevel的等级来显示的,相关参考:http://www.jianshu.com/p/f60471a7d935
    2、新创建的window没有被[UIApplication sharedApplication].windows强引用,只是能通过[UIApplication sharedApplication].windows找到创建的window。所以想要创建的window显示,那就必须保证其生命周期。

    以上是我自己根据相关的运行效果总结的,如果有哪位大神觉得有地方描述的不准确,欢迎指正哈,在下感激不尽!!!

    相关文章

      网友评论

      • MemoryReload:你的第一次置空``self.myWindow2 = nil;``之所以会失败,是因为self.myWindow2所指的对象在自动释放池里,如果你换用手动管理内存编写代码,明确的release掉,应该打印的windows数组就会是只有一个window了。:relieved:
      • 莪笨善良:同样遇到了问题,已经解决,最后的总结就是window在创建完毕后,如果不是局部变量的话,就直接释放掉了,所以没有显示
        踩坑小分队:@莪笨善良 局部变量被释放了

      本文标题:创建的UIWindow为什么不显示

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