欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

模擬NSRunLoop

系統 2470 0

Fundamentals
Most of the mysteriousness in? NSRunLoop ?is in its various? run ?methods. What goes on in there? How does it all work?

The? -run ?method is pretty simple, since the documentation describes it in terms of? -runMode:beforeDate: :

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.

Its implementation must therefore look something pretty close to:

      
        -
      
      
        (
      
      
        void
      
      
        )
      
      
        run
      
      
        {
      
      
        while
      
      
        ([
      
      
        self
      
      
        hasSourcesOrTimers
      
      
        ])
      
      
        [
      
      
        self
      
      
        runMode:
      
      
        NSDefaultRunLoopMode
      
      
        beforeDate:
      
      
        [
      
      
        NSDate
      
      
        distantFuture
      
      
        ]];
      
      
        }
      
    

The ? -runUntilDate: ? method is similar:

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate: until the specified expiration date.

Its implementation would then be like this:

      
        -
      
      
        (
      
      
        void
      
      
        )
      
      
        runUntilDate:
      
      
        (
      
      
        NSDate
      
      
        *
      
      
        )
      
      
        limitDate
      
      
        {
      
      
        while
      
      
        ([
      
      
        self
      
      
        hasSourcesOrTimers
      
      
        ])
      
      
        {
      
      
        [
      
      
        self
      
      
        runMode:
      
      
        NSDefaultRunLoopMode
      
      
        beforeDate:
      
      
        limitDate
      
      
        ];
      
      
        // check limitDate at the end of the loop to ensure that
      
      
        // the runloop always runs at least once
      
      
        if
      
      
        ([
      
      
        limitDate
      
      
        timeIntervalSinceNow
      
      
        ]
      
      
        <
      
      
        0
      
      
        )
      
      
        break
      
      
        ;
      
      
        }
      
      
        }
      
    

That was easy enough. How about ? -runMode:beforeDate: , then? Well, that's where all the complication lies.

Input Sources
As described in? Apple's Run Loops programming guide , a run loop contains two types of sources: inputs and timers. An input source is basically some kind of external signal from outside the runloop itself.

In Mac OS X, input sources are mach ports. While things like NSFileHandle and CFFileDescriptor may give the appearance of connecting non-mach-port things into the runloop, this is actually fake! They monitor their file descriptor sources on a dedicated thread, then signal back to the runloop over a mach port.

(This may have changed in 10.6, which now has APIs which are capable of monitoring both mach ports and file descriptors at the same time. However, the fundamental fact remains that mach ports are the major input source used on OS X.)

Most people's eyes glaze over when they hear about mach ports. They're not very well known, nor well documented. And personally, I don't know them all that well myself. Because of this, I'm going to explore an alternate? NSRunLoop ?which uses file descriptors as its input sources instead. The fundamentals are the same, and file descriptors are more readily understandable to most people.

Quick refresher: what is a file descriptor, or FD for short? An FD is an object (not in the Objective-C sense, but in the conceptual sense) which you can either read from, write to, or both read and write. An FD can have data available for reading, space available for writing, or neither. This particular state of an FD can change over time. For example, imagine an FD which represents a socket communicating with another application. When that other application writes to the socket, the FD in your application will have data available for reading. If that FD is an input source for a run loop, that run loop will wake up and process that source. Likewise, if the other application reads from the socket, the FD in your application will have space available for writing, and this will also wake up the run loop and process that source. This is one of the fundamental tasks of a run loop.

A run loop needs to monitor multiple input sources at a time. There are several APIs for doing this on OS X, but the one I'm going to use here is? select(2) .

I won't go into details on how to use? select ?(pseudocode, remember?), but the basics are pretty easy: you give it three sets of FDs, which you want to monitor for reading, writing, and errors. It then returns whenever there's activity, and the three sets contain those FDs which have had that sort of activity on them.

Thus, we can see the first pass at how? -runMode:beforeDate: ?would work. I'm going to simplify things a bit further and ignore the fact that? select ?takes three different sets of FDs, and just use one. The idea is just that we're interested in activity on these input sources.

And remember, I'm doing pseudocode, so don't expect this to look 100% like real Objective-C.

The first thing is to check if there are any sources. According to the documentation, this method immediately returns? NO ?if not:

      
        -
      
      
        (
      
      
        BOOL
      
      
        )
      
      
        runMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        beforeDate:
      
      
        (
      
      
        NSDate
      
      
        *
      
      
        )
      
      
        limitDate
      
      
        {
      
      
        if
      
      
        (
      
      
        !
      
      
        [
      
      
        self
      
      
        hasSourcesOrTimers
      
      
        ])
      
      
        return
      
      
        NO
      
      
        ;
      
    

Next, create an empty FD set:

      
        fd_set
      
      
        fdset
      
      
        ;
      
      
        FD_ZERO
      
      
        (
      
      
        &
      
      
        fdset
      
      
        );
      
    

Then, set each input source's FD within the set. I assume that the input source class has a ? -fileDescriptor ? method that returns the FD it wants to monitor:

      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [
      
      
        self
      
      
        inputSources
      
      
        ])
      
      
        FD_SET
      
      
        ([
      
      
        inputSource
      
      
        fileDescriptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        );
      
    

Now call ? select . Remember, to simplify, I'm pretending that it only takes one file descriptor set rather than three. I'm also ignoring all error checking:

      
        select
      
      
        (
      
      
        fdset
      
      
        ,
      
      
        NULL
      
      
        );
      
    

Once it returns, check each input source to see if it's ready for processing now. I iterate over a copy of the input sources because the code that the input source executes may modify the set of input sources:

      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [[[
      
      
        self
      
      
        inputSources
      
      
        ]
      
      
        copy
      
      
        ]
      
      
        autorelease
      
      
        ])
      
      
        if
      
      
        (
      
      
        FD_ISSET
      
      
        ([
      
      
        inputSource
      
      
        fileDescrptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        ))
      
      
        [
      
      
        inputSource
      
      
        fileDescriptorIsReady
      
      
        ];
      
    

The documentation states that this method returns ? YES ? the runloop was run in any way, so that's the last thing to do here:

      
        return
      
      
        YES
      
      
        ;
      
      
        }
      
    

Modes
So far so good, but it has a way to go. This method completely ignores its parameters! First, we'll look at the ? mode ? parameter.

Just what is the? mode ?parameter, anyway? A mode is essentially a grouping of input and timer sources. Different sources are active in different modes.? NSRunLoop ?has NSDefaultRunLoopMode , which as the name would expect is where most sources are added. In Cocoa, you also have secondary modes like? NSEventTrackingRunLoopMode , which is used when the mouse is held down on a control. By switching to this mode, sources which were only added to the default mode will not fire, which prevents unwanted code from running while the user is in the middle of making a menu selection or moving a slider. Sources which need to fire during event tracking can be added to that mode. Sources which need to fire in both circumstances can be added to both.

You can then imagine? NSRunLoop ?containing an instance variable for input sources like this:

      
        NSMutableDictionary
      
      
        *
      
      
        _inputSources
      
      
        ;
      
      
        // maps modes to NSMutableSets
      
    

NSRunLoop 's method to add an input source is called ? -addPort:forMode: , and its implementation would then look like this:

      
        -
      
      
        (
      
      
        void
      
      
        )
      
      
        addPort:
      
      
        (
      
      
        NSPort
      
      
        *
      
      
        )
      
      
        aPort
      
      
        forMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        {
      
      
        NSMutableSet
      
      
        *
      
      
        sourcesSet
      
      
        =
      
      
        [
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ];
      
      
        if
      
      
        (
      
      
        !
      
      
        sourcesSet
      
      
        )
      
      
        {
      
      
        // this is the first time anything has used this mode
      
      
        // so create a new set for it
      
      
        sourcesSet
      
      
        =
      
      
        [
      
      
        NSMutableSet
      
      
        set
      
      
        ];
      
      
        [
      
      
        _inputSources
      
      
        setObject:
      
      
        sourcesSet
      
      
        forKey:
      
      
        mode
      
      
        ];
      
      
        }
      
      
        [
      
      
        sourcesSet
      
      
        addObject:
      
      
        aPort
      
      
        ];
      
      
        }
      
    

Similarly for the removal method:

      
        -
      
      
        (
      
      
        void
      
      
        )
      
      
        removePort:
      
      
        (
      
      
        NSPort
      
      
        *
      
      
        )
      
      
        aPort
      
      
        forMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        {
      
      
        NSMutableSet
      
      
        *
      
      
        sourcesSet
      
      
        =
      
      
        [
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ];
      
      
        [
      
      
        sourcesSet
      
      
        removeObject:
      
      
        aPort
      
      
        ];
      
      
        // this isn't strictly necessary, but keeps us from leaking
      
      
        // sets if the caller uses a lot of one-time "throwaway" modes
      
      
        // (which it probably never would)
      
      
        if
      
      
        (
      
      
        !
      
      
        [
      
      
        sourcesSet
      
      
        count
      
      
        ])
      
      
        [
      
      
        _inputSources
      
      
        removeObjectForKey:
      
      
        mode
      
      
        ];
      
      
        }
      
    

And then the run method needs to be changed to match:

      
        -
      
      
        (
      
      
        BOOL
      
      
        )
      
      
        runMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        beforeDate:
      
      
        (
      
      
        NSDate
      
      
        *
      
      
        )
      
      
        limitDate
      
      
        {
      
      
        if
      
      
        (
      
      
        !
      
      
        [
      
      
        self
      
      
        hasSourcesOrTimersForMode:
      
      
        mode
      
      
        ])
      
      
        return
      
      
        NO
      
      
        ;
      
      
        fd_set
      
      
        fdset
      
      
        ;
      
      
        FD_ZERO
      
      
        (
      
      
        &
      
      
        fdset
      
      
        );
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ])
      
      
        FD_SET
      
      
        ([
      
      
        inputSource
      
      
        fileDescriptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        );
      
      
        select
      
      
        (
      
      
        fdset
      
      
        ,
      
      
        NULL
      
      
        );
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [[[
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ]
      
      
        copy
      
      
        ]
      
      
        autorelease
      
      
        ])
      
      
        if
      
      
        (
      
      
        FD_ISSET
      
      
        ([
      
      
        inputSource
      
      
        fileDescrptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        ))
      
      
        [
      
      
        inputSource
      
      
        fileDescriptorIsReady
      
      
        ];
      
      
        return
      
      
        YES
      
      
        ;
      
      
        }
      
    

Timeout
This code still ignores one parameter, ? limitDate . The purpose of this parameter is to force the method to return even if no input sources were ready. It functions as a timeout. To make this work, the code simply computes the timeout and passes it as the last parameter to ? select ? (which in reality requires a more complicated timeout structure, not just an ? NSTimeInterval , but remember, pseudocode!):

      
        -
      
      
        (
      
      
        BOOL
      
      
        )
      
      
        runMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        beforeDate:
      
      
        (
      
      
        NSDate
      
      
        *
      
      
        )
      
      
        limitDate
      
      
        {
      
      
        if
      
      
        (
      
      
        !
      
      
        [
      
      
        self
      
      
        hasSourcesOrTimersForMode:
      
      
        mode
      
      
        ])
      
      
        return
      
      
        NO
      
      
        ;
      
      
        fd_set
      
      
        fdset
      
      
        ;
      
      
        FD_ZERO
      
      
        (
      
      
        &
      
      
        fdset
      
      
        );
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ])
      
      
        FD_SET
      
      
        ([
      
      
        inputSource
      
      
        fileDescriptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        );
      
      
        NSTimeInterval
      
      
        timeout
      
      
        =
      
      
        [
      
      
        limitDate
      
      
        timeIntervalSinceNow
      
      
        ];
      
      
        select
      
      
        (
      
      
        fdset
      
      
        ,
      
      
        timeout
      
      
        );
      
      
        // if the timeout was hit, there may not be
      
      
        // any active input sources, but this loop
      
      
        // will simply do nothing if that's the case
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [[[
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ]
      
      
        copy
      
      
        ]
      
      
        autorelease
      
      
        ])
      
      
        if
      
      
        (
      
      
        FD_ISSET
      
      
        ([
      
      
        inputSource
      
      
        fileDescrptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        ))
      
      
        [
      
      
        inputSource
      
      
        fileDescriptorIsReady
      
      
        ];
      
      
        return
      
      
        YES
      
      
        ;
      
      
        }
      
    

Timer Sources
This implementation deals with input sources and the timeout parameter well enough, but completely ignores timers.

As with input sources, I'll assume an instance variable which holds timers. And like input sources, timers are grouped into modes:

      
        NSMutableDictionary
      
      
        *
      
      
        _timerSources
      
      
        ;
      
      
        // maps modes to NSMutableSets
      
    

I'll skip over the implementation of ? -addTimer:forMode: , as it should be pretty obvious and is basically identical to ? -addPort:forMode: .

Adding timer support to the above code is relatively straightforward. The list of timers can be consulted to find the one that fires earliest. If that time is earlier than limitDate , then it gets to be the timeout instead of? limitDate . After? select ?runs, check the list of timers to see if any of them are ready to fire, and fire the ones that are.

There's one wrinkle, which is that a timer firing does? not ?make? -runMode:beforeDate: ?return. If a timer fires, it should be processed, and then control should return back to select . This continues until an input source fires. If an input source does fire, the method still needs to check the list of timers and fire any that are ready, because otherwise a busy input source could prevent timers from ever running.

Given all of that, here's what the code looks like with timer support:

      
        -
      
      
        (
      
      
        BOOL
      
      
        )
      
      
        runMode:
      
      
        (
      
      
        NSString
      
      
        *
      
      
        )
      
      
        mode
      
      
        beforeDate:
      
      
        (
      
      
        NSDate
      
      
        *
      
      
        )
      
      
        limitDate
      
      
        {
      
      
        if
      
      
        (
      
      
        !
      
      
        [
      
      
        self
      
      
        hasSourcesOrTimersForMode:
      
      
        mode
      
      
        ])
      
      
        return
      
      
        NO
      
      
        ;
      
      
        // with timer support, this code has to loop until an input
      
      
        // source fires
      
      
        BOOL
      
      
        didFireInputSource
      
      
        =
      
      
        NO
      
      
        ;
      
      
        while
      
      
        (
      
      
        !
      
      
        didFireInputSource
      
      
        )
      
      
        {
      
      
        fd_set
      
      
        fdset
      
      
        ;
      
      
        FD_ZERO
      
      
        (
      
      
        &
      
      
        fdset
      
      
        );
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ])
      
      
        FD_SET
      
      
        ([
      
      
        inputSource
      
      
        fileDescriptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        );
      
      
        // the timeout needs to be set from the limitDate
      
      
        // and from the list of timers
      
      
        // start with the limitDate
      
      
        NSTimeInterval
      
      
        timeout
      
      
        =
      
      
        [
      
      
        limitDate
      
      
        timeIntervalSinceNow
      
      
        ];
      
      
        // now run through the list of timers and set the
      
      
        // timeout to the smallest one found in them and
      
      
        // in the limitDate
      
      
        for
      
      
        (
      
      
        timer
      
      
        in
      
      
        [
      
      
        _timerSources
      
      
        objectForKey:
      
      
        mode
      
      
        ])
      
      
        timeout
      
      
        =
      
      
        MIN
      
      
        (
      
      
        timeout
      
      
        ,
      
      
        [[
      
      
        timer
      
      
        fireDate
      
      
        ]
      
      
        timeIntervalSinceNow
      
      
        ]);
      
      
        // now run select
      
      
        select
      
      
        (
      
      
        fdset
      
      
        ,
      
      
        timeout
      
      
        );
      
      
        // process input sources first (this choice is arbitrary)
      
      
        for
      
      
        (
      
      
        inputSource
      
      
        in
      
      
        [[[
      
      
        _inputSources
      
      
        objectForKey:
      
      
        mode
      
      
        ]
      
      
        copy
      
      
        ]
      
      
        autorelease
      
      
        ])
      
      
        if
      
      
        (
      
      
        FD_ISSET
      
      
        ([
      
      
        inputSource
      
      
        fileDescrptor
      
      
        ],
      
      
        &
      
      
        fdset
      
      
        ))
      
      
        {
      
      
        didFireInputSource
      
      
        =
      
      
        YES
      
      
        ;
      
      
        [
      
      
        inputSource
      
      
        fileDescriptorIsReady
      
      
        ];
      
      
        }
      
      
        // now process timers
      
      
        // responsibility for updating fireDate for repeating timers
      
      
        // and for removing the timer from the runloop for non-repeating timers
      
      
        // rests in the timer class, not in the runloop
      
      
        for
      
      
        (
      
      
        timer
      
      
        in
      
      
        [[[
      
      
        _timerSources
      
      
        objectForKey:
      
      
        mode
      
      
        ]
      
      
        copy
      
      
        ]
      
      
        autorelease
      
      
        ])
      
      
        if
      
      
        ([[
      
      
        timer
      
      
        fireDate
      
      
        ]
      
      
        timeIntervalSinceNow
      
      
        ]
      
      
        <=
      
      
        0
      
      
        )
      
      
        [
      
      
        timer
      
      
        fire
      
      
        ];
      
      
        // see if we timed out, if so, abort!
      
      
        // this is checked at the end to ensure that timers and inputs are
      
      
        // always processed at least once before returning
      
      
        if
      
      
        ([
      
      
        limitDate
      
      
        timeIntervalSinceNow
      
      
        ]
      
      
        <
      
      
        0
      
      
        )
      
      
        break
      
      
        ;
      
      
        }
      
      
        return
      
      
        YES
      
      
        ;
      
      
        }
      
    

And that covers all of the necessary functionality. The final code is pretty straightforward and understandable.

Conclusion
What does this exercise tell us? We have to be careful not to take? too ?much away from this pseudocode, as there's no guarantee that it matches Apple's. In fact, I know of one case that recently bit me where it does not: Apple's implementation will only fire? one ?pending timer for each pass through the run loop, even if multiple timers are ready to fire, whereas this code will fire all pending timers once before returning. And of course there's the major difference that Apple's code uses mach ports, not file descriptors, although their semantics are similar.

Despite this problem, a lot can be learned from this sort of exercise. For example, run loop modes are a common point of confusion among Cocoa programmers, and writing all of this stuff out helps to make it clear just what a mode is and how it works.

It can also inform speculation about the implementation of other parts of Cocoa. For example, we can deduce how the? -performSelector:withObject:afterDelay: ?method works on the inside. Since a run loop only handles sources and timers, it must use one of those two. Since it activates after a delay, it must use a timer. Watching how it behaves in the debugger will confirm this to be correct. As another example, we can conclude that? -performSelectorOnMainThread:withObject:waitUntilDone: ?must use a mach port, since it can't manipulate a timer on the main thread from a secondary thread. ( NSRunLoop ?is not thread safe.)

All in all, this kind of technique is really useful in general. I don't usually take it as far as writing out detailed pseudocode like this, but thinking about? how ?some Apple code might be implemented can really help further understanding of how it works and what the documentation says, as well as what it implies but does not say directly. You have to be careful to ensure that your conclusions are ultimately based on documentation and real-world constraints, and not the peculiarities of your particular idea of how it might work, but that just takes a bit of care.

It's also helpful just to demystify a class. It's easy to get into magical thinking, where you see a class as being incomprehensible and elevated above the mortal plane. The fact is, while the particular implementations of some of these classes can be pretty sophisticated (the? CFRunLoop ?source will make your eyes bleed), the basics of what they do and how they do it are usually very straightforward. For 99.9% of the APIs in Cocoa, they aren't there because they're doing something amazing that you could never achieve, but rather they simply exist to save you the time and trouble of having to write it all yourself.

That's it for this week. Check back in seven days for another exciting edition. Until then,? keep sending in your ideas for topics . Friday Q&A is reader-driven, and the more topic ideas I get, the better this series will become.

模擬NSRunLoop


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美精品在线观看视频 | 欧美电影在线观看网站 | 久久99精品久久 | 成人在线观看免费视频 | 91精品国产综合久久久久蜜臀 | 亚洲成a人片在线看 | 蜜桃av人人夜夜澡人人爽 | 69av在线视频| 国产在线精品成人一区二区三区 | 成人免费一区二区三区视频网站 | 久草精品视频在线观看 | 99热首页| 毛片毛片毛片毛片毛片毛片 | 国产毛片不卡 | 国产日韩中文字幕 | 久久亚 | 香港三级午夜理伦三级 | 国产成人综合在线观看 | 亚洲三级在线 | 久久久这里有精品999 | 九七婷婷狠狠成人免费视频 | 亚洲一区二区免费视频 | 国产99精品 | 污染版的拳皇 | 国产午夜免费一区二区三区 | 精品欧美 | 欧美成人全部费免网站 | 一级毛片一级毛片一级毛片一级毛片 | 波多野一区二区三区在线 | 国产精品一区二区三区在线播放 | 久久观看 | 久久久久无码国产精品一区 | 凹凸日日摸日日碰夜夜爽孕妇 | 国产亚洲精品久久久久久久久动漫 | 欧美精品成人a多人在线观看 | 婷婷丁香综合 | 亚洲成年网站在线777 | 日韩欧美一区二区三区免费观看 | 亚洲精品午夜国产va久久成人 | 久久99综合国产精品亚洲首页 | 大蕉香蕉久久爱 |