美文网首页Mac开发云集Mac开发iOS之MAC端开发
纯代码创建macOS应用,及仿写[NSApp run]

纯代码创建macOS应用,及仿写[NSApp run]

作者: STrawberryer | 来源:发表于2017-08-28 16:07 被阅读129次

    一、前言

    写该篇文章的缘由是:我想手动控制macOS的主程序循环,用来添加自己想要的处理。
    正文 I ~ IV 部分 讲解我理解macOS应用结构。
    正文 V 部分 仿写[NSApp run],添加自己想要的处理

    二、正文

    Ⅰ、我理解的应用

    我把OS应用抽象出两个概念:

    1、视图显示(当然是对于有界面的应用)

    2、事件处理机

    Ⅱ、视图显示

    先从窗口(NSWindow)开始谈起。窗口可以分为标题头,和内容。标题头包括几个通用按钮和标题头。分别可以通过如下方式进行设置:

    
    NSWindowStyleMask style = NSWindowStyleMaskClosable | 
                              NSWindowStyleMaskTitled | 
                              NSWindowStyleMaskMiniaturizable | 
                              NSWindowStyleMaskResizable;
    
    //在窗口初始化的时候 设置窗口坐标和大小、窗口类型、缓冲类型、是否延迟创建窗口。
    id window = [[NSWindow alloc]initWithContentRect : 
                                 NSMakeRect(10, 10, 100, 100) 
                                 styleMask : style 
                                 backing:NSBackingStoreBuffered 
                                 defer:NO];
    
    //设置标题
    [window setTitle:[NSString stringWithUTF8String : "hello world"]];
    
    

    窗口被看成一种特殊的视图,意思是它还是一个视图,只不过它是基本视图。

    这也就意味着你把窗口全关了也不会导致应用的终止。

    Ⅲ、事件处理机

    一个应用的事件可以分为很多种。具体可以在NSEvent里面找到。

    一个应用离不开事件的接收和处理。我觉的事件的最大作用体现在人机交互方面。鼠标、键盘等等输入设备被相应的时候,会形成一个事件包,由底层逐步向应用层传递,并且存放在应用的事件队列中。接下来我们应用层开发者只需要获取到相应的事件,并处理响应它。这样一个事件的生命就结束了。

    理基本就这么一个理。至于代码,就接着看吧。

    Ⅳ、代码实现

    [NSApplication sharedApplication]

    首先必须创建初始化一个应用对象。

    应用的初始化的过程中,会创建一个事件池,用来存放事件。有了事件池还是不够的,需要一个事件接收端口(port),用来接收从底层传来的事件。(其实是从Window Server那传来的)

    接下来就创建一个窗口,假如你的应用需要窗口。

    窗口的创建十分简单,只要调用NSWindow 的 初始化方法就行了,在初始化方法中进行各种窗口设置。个人认为窗口在初始化时会自动与创建的应用进行关联。不需要将它添加进NSApplication中。

    同样也可以给窗口添加一个其他视图(NSView)

    //设置window的内容视图。
    [window setContentView : cView];
    
    //将该视图设置为第一响应者。
    [window makeFirstResponder : cView];
    

    关于事件响应部分我也做了一点点的记录
    Cocoa Event Overview

    万事俱备,只欠一个run了。

    调用[NSApp run],让程序跑起来。这个方法里面会有一个循环,因为这个循环程序才会一直运行,不会结束。除非调用终止方法,才会有返回值。继续进行之后的语句。

    
    #import <AppKit/AppKit.h>
    
    #import <Foundation/Foundation.h>
    
    int main(int argc , const char* argv[])
    
    {
    
    [NSApplication sharedApplication];
    
    //设置应用的显示策略(ActivationPolicy)。可以在我的另一篇文章中找到具体说明。
    [NSApp setActivationPolicy : NSApplicationActivationPolicyRegular];
    
    NSWindowStyleMask style = NSWindowStyleMaskClosable | 
                              NSWindowStyleMaskTitled | 
                              NSWindowStyleMaskMiniaturizable | 
                              NSWindowStyleMaskResizable;
    
    id window = [[NSWindow alloc]initWithContentRect : 
                                 NSMakeRect(10, 10, 100, 100) 
                                 styleMask : style 
                                 backing:NSBackingStoreBuffered 
                                 defer:NO];
    
    [window setTitle:[NSString stringWithUTF8String:"hello world"]];
    
    //如下没有这句话,会导致窗口不会显示出来
    [window makeKeyAndOrderFront : nil];
    
    //使得应用打开时就获取到用户焦点
    [NSApp activateIgnoringOtherApps:YES];
    
    [NSApp run];
    
    }
    
    

    代码中提及的显示策略链接 Activation Policy

    对于一个“正常”的应用来说这已经足够了。但是如果想在程序的每次循环中干点自己想要的事,那么就需要写一个类似[NSApp run]的循环,然后在每个循环中添加自己的处理。

    Ⅴ、仿写[NSApp run]

    首先得有个循环,使得程序一直跑下去。用以下代码,代替[NSApp run];

    
    while(true)
    
    {
    
    PollEvents();
    //do something you like . :D
    
    }
    
    

    之前说了事件是至关重要的,所以在循环中需要接收每个事件。然后对事件进行相应的处理。这样就达到了[NSApp run]的基本要求。

    
    void PollEvents()
    
    {
    
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
    
    for(;;)
    
    {
    
    NSEvent* event = [NSApp nextEventMatchingMask : NSEventMaskAny
    
     untilDate : [NSDatedistantPast] inMode : NSDefaultRunLoopMode
    
    dequeue:YES];
    
    if(event == nil)
    
    break;
    
    [NSApp sendEvent : event];
    
    }
    
    [pool release];
    
    }
    
    

    三、传送门

    阅读导向

    Mac科技相关阅读导向

    笔者相关文档

    Cocoa 事件处理大纲 Event Overview
    应用显示策略Activation Policy

    相关文章

      网友评论

      • _菜菜:有个错误
        NSEvent* event = [NSApp nextEventMatchingMask : NSEventMaskAny untilDate : [NSDate distantPast] inMode : NSDefaultRunLoopMode dequeue:YES];
        应为
        NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
        用distantPast的NSDate的对象的话,无论是否收到事件都会获得一个NSEvent对象,这样会导致循环中长期空转严重占用系统资源,而distantFuture的NSDate会一直阻塞直到接收事件,这样是符合预期效果的。
        GNUStep的-[NSApplication run]方法就是这样设计的。

      本文标题:纯代码创建macOS应用,及仿写[NSApp run]

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