tag:blogger.com,1999:blog-90147427823233991762008-07-16T13:26:40.840+01:00NSMakePoint()Milohttp://www.blogger.com/profile/15153841246644976734noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-9014742782323399176.post-84656619832577798292007-10-30T10:34:00.000Z2007-10-30T11:18:55.601ZMBFileEvents<span class="Apple-style-span" style="font-family:verdana;"><span class="Apple-style-span" style="font-size:medium;">Version 1.0<br /></span></span><a href="http://www.milobird.com/blog/MBFileEvents.zip"><span class="Apple-style-span" style="font-family:verdana;"><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Download</span></span></span></a><span class="Apple-style-span" style="font-family:verdana;"><span class="Apple-style-span" style="font-size:medium;"><br /><br />MBFileEvents is a category on NSString which allows you to register to receive notifications when a file is modified. It provides an extremely light-weight interface wrapped around a kqueue/kevent implementation.<br /><br />You can add a file observer with a single line of code:<br /><br /><code>BOOL success = [string addFileEventsObserver:self];</code><br /><br />Provided that string is a valid path and there was no error opening the file, this method will return YES. If this is the first time you are adding a file observer, a kqueue will be created and a background thread will be spun off to monitor the queue for events.<br /><br />When an event is detected the observer will receive the following callback, declared as an informal protocol:<br /><br /><code>- (void)observeFileEvent:(MBFileEvent)event forFilePath:(NSString *)path;</code><br /><br />The event is simply a bitfield, and can be masked to determine the components of the event. For example:<br /><br /><code>if ((event &amp; MBDeleteFileEventMask) == MBDeleteFileEventMask)<br /><span class="Apple-tab-span" style="white-space:pre"> </span>DLog(@"File at path '%@' was deleted", path);</code><br /><br />Note that you are monitoring the file itself, not the path, so once the file is deleted you won't receive any further callbacks. If you anticipate that the file has been replaced by a new file at the same path, you must add the observer again to begin monitoring the new file.<br /><br />To remove an observer, simply call the following:<br /><br /><code>[string removeFileEventsObserver:self];</code><br /><br />This isn't necessary if you are using garbage collection.<br /><br />MBFileEvents is released under the modified BSD license, with no warranty of any kind. Leopard is required, I'm sorry to say, because I'm using the new NSPointerArray class for weak referencing of observers under garbage collection.</span></span>Milohttp://www.blogger.com/profile/15153841246644976734noreply@blogger.comtag:blogger.com,1999:blog-9014742782323399176.post-63144489277945827692007-10-30T01:05:00.000Z2007-10-30T11:16:16.089ZMBThreadingProxies<span class="Apple-style-span" style="font-family:verdana;"><span class="Apple-style-span" style="font-size:medium;">Version 1.0</span></span><span class="Apple-style-span" style="font-family:verdana;"><a href="http://www.milobird.com/blog/MBThreadingProxies.zip"><span class="Apple-style-span" style="font-size:medium;"><span class="Apple-style-span" style="font-weight: bold;"></span></span></a></span><div><span class="Apple-style-span" style="font-family:verdana;"><a href="http://www.milobird.com/blog/MBThreadingProxies.zip"><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Download</span></span></a></span><div><span class="Apple-style-span" style="font-family:verdana;"><span class="Apple-style-span" style="font-size:medium;"><br />MBThreadingProxies is a category on NSObject which allows you to send a message to an object and have it delivered asynchronously on another thread. The following proxies are available:<br /><br />– An </span><span class="Apple-style-span" style="font-style: italic;"><span class="Apple-style-span" style="font-size:medium;">operation queue proxy</span></span><span class="Apple-style-span" style="font-size:medium;">, which makes use of Leopard's NSOperation API to deliver the message on one or more background threads which are created, managed, and reused by the system. If the return value of the method you are calling is an object you will get back a </span><span class="Apple-style-span" style="font-style: italic;"><span class="Apple-style-span" style="font-size:medium;">future proxy</span></span><span class="Apple-style-span" style="font-size:medium;">.<br /><br />– A </span><span class="Apple-style-span" style="font-style: italic;"><span class="Apple-style-span" style="font-size:medium;">main thread proxy</span></span><span class="Apple-style-span" style="font-size:medium;">, which delivers the message on the main thread.<br /><br />– A </span><span class="Apple-style-span" style="font-style: italic;"><span class="Apple-style-span" style="font-size:medium;">background thread proxy</span></span><span class="Apple-style-span" style="font-size:medium;">, which delivers the message on a new background thread which exits after the message send completes.<br /><br /><br /></span><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Operation queue proxy</span></span><span class="Apple-style-span" style="font-size:medium;"><br /><br /><code>[[object operationQueueThreadingProxy] message];</code><br /><br /></span></span><span class="Apple-style-span" style="font-size:medium;">The message is delivered using a single shared instance of NSOperationQueue. You can control the degree of concurrency used by the queue if you wish. For instance, you can force all your message sends to be executed on a single worker thread like this:<br /><br /></span><code><span class="Apple-style-span" style="font-size:medium;">[[NSObject sharedOperationQueue] setMaxConcurrentOperationCount:1];</span></code><span class="Apple-style-span" style="font-size:medium;"><br /><br />For methods with non-object return types, the return value is undefined. (Don't count on it being 0!) Methods that return an object will always return a future proxy.<br /><br />Exceptions raised during the message send are logged to the console.<br /><br /><br /></span><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Future proxy</span></span><span class="Apple-style-span" style="font-size:medium;"><br /><br /></span><code><span class="Apple-style-span" style="font-size:medium;">id futureProxy = [[object operationQueueThreadingProxy] message];<br />//  Do other expensive stuff here.<br />NSLog(@"%@", futureProxy);</span></code><span class="Apple-style-span" style="font-size:medium;"><br /><br />A future proxy is a placeholder for an object returned by an operation queue proxy method call. The message send will proceed asynchronously as normal, until you attempt to use the future proxy. At that point the thread you're on will block (if necessarily) until the message send has completed and the result can be determined.<br /><br />The future proxy is extremely transparent - you can treat it as if it actually were the result. The only exception to this is if you need to test whether or not the result is nil. You'll always have a valid future proxy object, even if the result that it acts as a proxy for eventually turns out to be nil, so I've added a method to NSProxy which allows you to test whether or not the target is nil by sending a message to the proxy, like so:<br /><br /></span><code><span class="Apple-style-span" style="font-size:medium;">BOOL isNotNil = [futureProxy isNotNil];</span></code><span class="Apple-style-span" style="font-size:medium;"><br /><br />The performance of future proxies is quite good. NSInvocation is not required to forward messages to the target, and once the result is resolved for the first time subsequent forwards don't even involve a lock.<br /><br />In addition to the logging of exceptions mentioned above, if you attempt to use a future proxy and an exception was raised during the operation queue proxy message send, that exception will be thrown. In addition, if you use the shared operation queue to cancel the operation and then attempt to access the future proxy, an NSInvocationOperationCancelledException will be thrown.<br /><br /><br /></span><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Main thread proxy</span></span><span class="Apple-style-span" style="font-size:medium;"><br /><br /></span><code><span class="Apple-style-span" style="font-size:medium;">[[object mainThreadThreadingProxy] message];</span></code><span class="Apple-style-span" style="font-size:medium;"><br /><br />Simply uses </span><code><span class="Apple-style-span" style="font-size:medium;">-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]</span></code><span class="Apple-style-span" style="font-size:medium;"> to deliver the message asynchronously on the main thread. This is similar to other third party offerings such as Uli Kusterer's UKMainThreadProxy, although his version appears to be synchronous for some reason.<br /><br /><br /></span><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Background thread proxy</span></span><span class="Apple-style-span" style="font-size:medium;"><br /><br /></span><code><span class="Apple-style-span" style="font-size:medium;">[[object backgroundThreadThreadingProxy] message];</span></code><span class="Apple-style-span" style="font-size:medium;"><br /><br />This works in exactly the same way as the main thread proxy, except that it uses </span><code><span class="Apple-style-span" style="font-size:medium;">-[NSObject performSelectorInBackground:withObject:]</span></code><span class="Apple-style-span" style="font-size:medium;"> to deliver the message on a one-shot worker thread.<br /><br />Exceptions seem to be totally swallowed, which is a shame.<br /><br /><br /></span><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-size:medium;">Notes</span></span><span class="Apple-style-span" style="font-size:medium;"><br /><br />– All proxies retain their targets. If you're not using garbage collection, having an object retain a proxy to itself will lead to a retain cycle.<br /><br />– As you might imagine, you can use these proxies from any thread. You can even use the same proxy from more than one thread at once.<br /><br />– Methods declared by NSProxy or the NSObject protocol are for the most part passed directly to the target on the current thread, and so have valid return values. The only exceptions are the methods </span><code><span class="Apple-style-span" style="font-size:medium;">retain</span></code><span class="Apple-style-span" style="font-size:medium;">, </span><code><span class="Apple-style-span" style="font-size:medium;">retainCount</span></code><span class="Apple-style-span" style="font-size:medium;">, </span><code><span class="Apple-style-span" style="font-size:medium;">release</span></code><span class="Apple-style-span" style="font-size:medium;">, </span><code><span class="Apple-style-span" style="font-size:medium;">autorelease</span></code><span class="Apple-style-span" style="font-size:medium;">, and </span><code><span class="Apple-style-span" style="font-size:medium;">self</span></code><span class="Apple-style-span" style="font-size:medium;">, which are handled by the proxy itself.<br /><br />– For obvious reasons, this is Leopard only.<br /><br />– This code is released under the modified BSD license, with no warranty of any kind.</span></div></div>Milohttp://www.blogger.com/profile/15153841246644976734noreply@blogger.comtag:blogger.com,1999:blog-9014742782323399176.post-62669960320692858732007-09-10T22:05:00.007+01:002008-03-27T10:49:10.458ZSecuritySpy Screensaver<span class="Apple-style-span" style="font-family: verdana;">Version 1.1 (Leopard only)</span><div><span class="Apple-style-span" style="font-weight: bold;"><a href="http://www.milobird.com/blog/SecuritySpy.saver.zip"><span class="Apple-style-span" style="font-family: verdana;">Download</span></a></span><span class="Apple-style-span" style="font-family: verdana;"><br /></span><div><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span></div><span class="Apple-style-span" style="font-family: verdana;">Version 1.0 (Tiger only)</span></div><div><span class="Apple-style-span" style="font-weight: bold;"><a href="http://www.milobird.com/blog/OldSecuritySpy.saver.zip"><span class="Apple-style-span" style="font-family: verdana;">Download</span></a></span></div><div><div><div><span class="Apple-style-span" style="font-family: verdana;"><br />A Mac OS X screensaver which displays live video from one or more servers running Ben Software's </span><a href="http://www.securityspy.com/"><span class="Apple-style-span" style="font-family: verdana;">SecuritySpy</span></a><span class="Apple-style-span" style="font-family: verdana;">.</span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.milobird.com/blog/uploaded_images/SecuritySpyScreensaver-790486.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.milobird.com/blog/uploaded_images/SecuritySpyScreensaver-790481.png" border="0" alt="" /></a><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span></div><div><span class="Apple-style-span" style="font-family: verdana;">You can configure the screensaver to connect to as many SecuritySpy servers as you like. The servers can be running over the internet, over your local network, or running on your own Mac (using the "localhost" address shown above).</span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br />Click on the "Cameras" tab to preview the cameras and hide any that you don't wish to see.</span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br />To setup SecuritySpy to act as a server, please follow the advice on </span><a href="http://www.bensoftware.com/ss/installationmanual/setupguide.html"><span class="Apple-style-span" style="font-family: verdana;">this page</span></a><span class="Apple-style-span" style="font-family: verdana;">. If SecuritySpy is running on the same Mac as the screensaver, you only need to follow the first bullet point on the page, which describes how to enable SecuritySpy's web server.</span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br />That's it. Enjoy!</span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span></div><div><span class="Apple-style-span" style="font-family: verdana;">If you have any questions, feel free to drop me a line. The address you want is milo @ this domain.</span></div><div><span class="Apple-style-span" style="font-weight: bold;"><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span></div><div><span class="Apple-style-span" style="font-family: verdana;"><br /></span></div><span class="Apple-style-span" style="font-family: verdana;">Boring stuff</span></span></div><div><span class="Apple-style-span" style="font-weight: bold;"><span class="Apple-style-span" style="font-family: verdana;"><br /></span></span><span class="Apple-style-span" style="font-family: verdana;">This Screensaver is freeware.<br />I am not responsible for any havoc this software may wreak. Enjoy responsibly.</span></div></div></div></div></div>Milohttp://www.blogger.com/profile/15153841246644976734noreply@blogger.com