Saturday, March 02, 2013

Gollum

Wikis with Gollum

Gollum is a simple wiki system built on top of Git that powers GitHub Wikis. Super easy to starting working in your wiki.

Installation

gem install gollum redcarpet rdiscount
gollum --help

Usage

git clone https://github.com/allending/Kiwi.git
cd Kiwi.wiki/
gollum
Alternatively
git clone https://github.com/allending/Kiwi.git
gollum --page-file-dir Kiwi.wiki/ TestWiki

Editing changes

As documented, changes done directly in the source should be committed to trigger gollum to reflect them

Coda

If coda is your editor then below markdown mode might become handy: Markdown.mode by bobthecow - Github

Monday, April 23, 2012

Installing GLEW in MacOS X

The old way

The easy way is at the bottom of the post :)
  1. Download the sources from SourceForge:
  2. http://glew.sourceforge.net/

  3. Unzip/Untar the file (Some browsers will do this automatically)

  4. Before compiling ...
  5. Usually you would run `make` but the shell script will fail to find/guess "$system" variable so we need to set it manually (as of in version 1.7.0 [08-26-11]). So open the makefile with some editor and change system to be `darwin` as shown in the image.
  6. Compile, Install and Clean
  7. make
    sudo make install
    make clean
    
    Some harmless warnings appear but ... let them be.

  8. We are done!
  9. Glew's headers are in /usr/include/GL and the dynamic and static libraries are in /usr/lib/ as usual :)

The easy way

  1. Use homebrew and save you all these little hazels ... :)
    brew install glew
    

Links

  1. Installing GLEW in Mac OS X (Leopard) - Julian Villegas
  2. GLEW on Mac OS - 175 CS Forum

Tuesday, March 20, 2012

CoreImage and UIKit coordinates

Recently I found a very nice CoreImage tutorial for iOS: Easy face detection with core animation in iOS5 and after downloading the source I found that this quite popular tutorial was teaching something wrong, coordinates system conversions!.

I can't remember how many bugs/problems I had because of not converting the coordinates well or using the incorrect coordinate system when I was at the uni. So I decided to write this little post so my great audience (approx. 6 viewers per day) can benefit from it :)

CoreImage coordinates are different from UIKit

CoreImage Coordinate system


(From CoreImage Programming Guide)


Even-though is not very clear in the image, each image processed has its own coordinate space and its origin in at the left bottom corner of the image. Each images's coordinate systems is device independent.

UIKit Coordinate System


(From View Programming Guide for iOS)


UIViews's frame's origin is at the top left corner and its coordinates are in their superview coordinate space. They are not independent.

Converting coordinates


We should convert CoreImage coordinates to UIKit coordinates system, not the other way around, because chances are that most of your code will be in UIKit coordinates and because other iOS devs will expect {0,0} to be at the top left corner, be nice and don't change everything just because of CoreImage. :)

This is easily done with an affine transform:
Where ui and ci subindexes mean UIKit and CoreImage coordinates respectively and h is the height of the image in regard.

We could do this manually but happily there are a bunch of functions for this task like: CGAffineTransformMakeScale, CGAffineTransformTranslate, CGPointApplyAffineTransform and even CGRectApplyAffineTransform!. Thanks to Apple for making our life easier.

The code


// Create the image and detector
CIImage* image = [CIImage imageWithCGImage:imageView.image.CGImage];
CIDetector* detector = [CIDetector detectorOfType:CIDetectorTypeFace 
                                          context:... options:...];

// CoreImage coordinate system origin is at the bottom left corner
// and UIKit is at the top left corner. So we need to translate
// features positions before drawing them to screen. In order to do
// so we make an affine transform
CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
transform = CGAffineTransformTranslate(transform,
                                    0, -imageView.bounds.size.height);

// Get features from the image
NSArray* features = [detector featuresInImage:image];
for(CIFaceFeature* faceFeature in features) {

  // Get the face rect: Convert CoreImage to UIKit coordinates
  const CGRect faceRect = CGRectApplyAffineTransform(faceFeature.bounds, transform);

  // create a UIView using the bounds of the face
  UIView* faceView = [[UIView alloc] initWithFrame:faceRect];

  ...

  if(faceFeature.hasLeftEyePosition) {
    
    // Get the left eye position: Convert CoreImage to UIKit coordinates
    const CGPoint leftEyePos = CGPointApplyAffineTransform(faceFeature.leftEyePosition, transform);
    ...

  }

  ...
}


You can download the sample from here and see the result is pretty much the same as the original tutorial. Only this time we didn't scramble with the coordinate system :)

In case, you didn't notice, the original example changes the whole window coordinate system causing its origin to be at the bottom left (like Cocoa in the Mac) hence the imageView appears at the bottom.


Friday, March 02, 2012

Another twilight theme for Xcode

I just created my ...


err ...


actually just tweaked Twilight theme a bit and named it "Twilight Console" It resembles Twilight but it uses font Monaco size 10 and the most important part (at least to me) is the console is also customized:


If you play with the console very often (gdb, llvm debugger, etc) you might like this theme.

Download TwilightConsole.dvtcolortheme or get if from Github :)


Tuesday, February 07, 2012

Shake animation using CAKeyFrameAnimation

Animation that shakes a toolbar (UIToolbar or any generic UIView object)

I put this here so I have a quick and simple example to refer to in the future :)

- (void)shakeToolbar
{
    static BOOL performingAnimation = NO;
    if (!performingAnimation) {
        performingAnimation = YES;

        // Make an image view to animate
        // Probably not necessary when animation only "position"
        // property, but if animating its size, transform or some
        // property that will need its subviews to layout is more
        // efficient to do it like this.
        UIGraphicsBeginImageContext(toolbar.frame.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        [self.view.layer renderInContext:context];
        UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageView *imageToAnimate;
        imageToAnimate = [[UIImageView alloc] initWithImage:img];
        imageToAnimate.frame = toolbar.frame;
        
        // Replace the toolbar with the image view temporarily
        [toolbar.superview addSubview:imageToAnimate];
        toolbar.hidden = YES;
        [imageToAnimate release];

        // Build the animation
        CGPoint pos = toolbar.layer.position;
        CGMutablePathRef shakePath = CGPathCreateMutable();
        CGPathMoveToPoint(shakePath, NULL, pos.x, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 5, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 4, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 3, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 2, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 1, pos.y);
        CGPathCloseSubpath(shakePath);
        const float durationOfShake = 0.5;
        CAKeyframeAnimation *shakeAni;
        shakeAni = [CAKeyframeAnimation animationWithKeyPath:
                                                        @"position"];
        shakeAni.path = shakePath;
        shakeAni.duration = durationOfShake;
        CFRelease(shakePath);

        // Perform the animation
        [imageToAnimate.layer addAnimation:shakeAni 
                                    forKey:@"shakeAnimation"];

        // Restore the toolbar to its original state when finished
        // (Probably is better to do this in the delegate method ... )
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                      durationOfShake * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            toolbar.hidden = NO;
            [imageToAnimate removeFromSuperview];
            performingAnimation = NO;
        });
    }
}

Sunday, January 29, 2012

Customized Blogger search box

I write this here so I have a place to refer so when some good day I decide to change current I don't have to write this again :)

The implementation is far from optimal but I couldn't find a better option than polling until the search box is loaded to fully customize it. Some properties are overwritten by defaults and CSS seems to be not enough. If somebody know how to do this in a cleaner/better way, I want to hear it ( read : write a comment please :) )

The CSS code that will make the search field look nicer and hides the search button:
/* customization of search box - nacho4d (css)*/
.gsc-search-box{
}
table.gsc-search-box td{
 border: 0px;
}
input.gsc-input { /* This is the real search field*/
background-image:url('http://web.me.com/nacho4d/toolbar_find24.png');
background-repeat:no-repeat;
background-position:0% 50%;

border: 1px solid gray;
-webkit-border-radius: 5px ;
-moz-border-radius: 5px ;
border-radius: 5px ;

-moz-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
-webkit-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
box-shadow: inset 0 5px 15px rgba(0,0,0,.15);

font-size: 1.2em;
color: gray;
padding-left:30px;
padding-right:7px;
height: 24px;
}
input.gsc-input:focus { 
-moz-box-shadow: inset 0 3px 25px rgba(0,0,0,.0);
-webkit-box-shadow: inset 0 3px 25px rgba(0,0,0,0);
box-shadow: inset 0 3px 25px rgba(0,0,0,0);
color:black;
}
input.gsc-search-button { /* this is the search button */
display:none;
}
/* end of customization of search box - nacho4d (css)*/
Remember to put this somewhere inside <style> tag :)

The js code that will poll looking for the search field (which is loaded dynamically) and set a couple of attributes to do the final touch.
<!-- Start of customization of search box - nacho4d (js) -->
  <script type='text/javascript'>
function fixSearchField() {
  var searchFields = document.getElementsByTagName('input');
  for (var i = 0; i < searchFields.length; i++) {
    var searchField = searchFields[i];
    if (searchField.getAttribute('class', 0) == ' gsc-input'){
      // found it!
      searchField.style.paddingLeft = '30px';
      searchField.style.width = '120px';
      searchField.setAttribute('placeholder', 'Search ...');
      return;
    }
  }
  setTimeout('fixSearchField()', 1000);
}
fixSearchField();
</script>

<!-- End of customization of search box - nacho4d (js) -->

Remember to put this somewhere after </style> :)

I guess this would be much easier if I were hosting my own blog ... just saying.

Thursday, January 26, 2012

scrollViewDidEndScrolling ?

Last time I tried this I think it was in iOS2.x, 3.x ... so instead of looking for an old code I decided to implement this again. I want to know when the text view did end scrolling. I mean when the text end moving (regardless regular dragging or drag + deceleration).


First I created my regular UITextView, set the delegate and implemented the delegate methods:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    if (scrollView.isDecelerating) {
    NSLog(@" ... restarting");
    } else {
    NSLog(@"Scroll will begin");
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(CGPoint *)targetContentOffset
{
    NSLog(@"will end dragging: velocity: %f", velocity.y);
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (decelerate) {
    } else {
        NSLog(@"Scroll did end (Dragging)");
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSLog(@"Scroll did end (Decelerating)");
}

So if I ...

scroll without deceleration I get:

Scroll will begin
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


scroll with deceleration I get:

Scroll will begin
will end dragging: velocity: 0.321089
Scroll did end (Decelerating)


start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get:

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


start scrolling with desceleration and while its decelerating do another scrolling with deceleration I get:

Scroll will begin
will end dragging: velocity: 1.995404
... restarting
will end dragging: velocity: 0.442658
Scroll did end (Decelerating)


Everything goes as expected :)


Why do I need scrollViewWillEndDragging:withVelocity:targetContentOffset: ?. This is my little finding: If that method is **not** implement and then I ...

start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get :

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)
Scroll did end (Decelerating)

Huh?? The textview ended dragging and just after decelerating? Did I find a bug?
So, in order to get notified only once when the text did end scrolling I need to implement scrollViewWillEndDragging:withVelocity:targetContentOffset: ?? ??
I hope I am not missing something.


In the mean time I uploaded a sample project that show the issue here.

Monday, January 23, 2012

Installing Node.js for the third time

I think this is the third time I want to use node.js in a different computer and I **always** get stuck in the same place.
I know there are several "How do I install node.js" tutorials, blogs, etc.
Here is what it worked for me:

  • In theory you install node using brew: brew install node (or brew upgrade node to upgrade current node installation) and then npm : curl http://npmjs.org/install.sh | sh.
    curl https://npmjs.org/install.sh | sh

  • But in practice, At least me, I always get permission errors, link errors, it looks pretty bad. It is because I need to set adjust my permissions first : sudo chown -R $USER /usr/local.

  • Then, If you got: "Error: The linking step did not complete successfully." finish node installation with brew link node.

  • Now installing npm should work flawless too: curl http://npmjs.org/install.sh | sh

Hope it helps... :)

Other References

Saturday, January 21, 2012

Adding Linkedin Badge to Blogger

I write this here so If I ever decide to change the template of this blog I have a reference to go back and set this again :)

Basically I simply replaced :
<a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a>

with Linkedin's badge. Something like this :)
 <b:if cond='data:aboutme != ""'><dd class='profile-textblock'><data:aboutme/></dd></b:if>
 </dl>
   <a href='http://jp.linkedin.com/pub/guillermo-ignacio-enriquez-gutierrez/31/b53/173'><img id='linkedinImg' border='0' src='http://www.linkedin.com/img/webpromo/btn_myprofile_160x33.png'/></a>
   <!-- <a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a> -->
 </b:if>

optionally, remove the border and shadow of img tag. (Put this somewhere before ]]></b:skin>)
#linkedinImg{
-webkit-box-shadow: none; 
-moz-box-shadow: none; 
box-shadow: none;
border: 0px;
}

In my opinion this looks much better than Google+ :)


Saturday, January 14, 2012

Catching Keyboard Events in iOS


Some notes before starting ...

Things explained here are NOT possible using public APIs so this definitely violates AppStore's rules. However, if you want to take your chances and implement this for the AppStore or perhaps for Jailbroken iPhones ... keep on reading :) I think this time it is harder to get caught since this doesn't need to link against private frameworks or add private headers, etc.

The question is: How?

The short answer

The trick is in accessing GSEventKey struct memory directly and check certain bytes to know the keycode and flags of the key pressed. Below code is almost self explanatory and should be put in your UIApplication subclass.

#define GSEVENT_TYPE 2
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE 15
#define GSEVENT_TYPE_KEYUP 11

NSString *const GSEventKeyUpNotification = @"GSEventKeyUpHackNotification";

- (void)sendEvent:(UIEvent *)event
{
    [super sendEvent:event];

    if ([event respondsToSelector:@selector(_gsEvent)]) {

        // Key events come in form of UIInternalEvents.
        // They contain a GSEvent object which contains 
        // a GSEventRecord among other things

        int *eventMem;
        eventMem = (int *)[event performSelector:@selector(_gsEvent)];
        if (eventMem) {

            // So far we got a GSEvent :)
            
            int eventType = eventMem[GSEVENT_TYPE];
            if (eventType == GSEVENT_TYPE_KEYUP) {
                
                // Now we got a GSEventKey!
                
                // Read flags from GSEvent
                int eventFlags = eventMem[GSEVENT_FLAGS];
                if (eventFlags) { 

                    // This example post notifications only when 
                    // pressed key has Shift, Ctrl, Cmd or Alt flags

                    // Read keycode from GSEventKey
                    int tmp = eventMem[GSEVENTKEY_KEYCODE];
                    UniChar *keycode = (UniChar *)&tmp;

                    // Post notification
                    NSDictionary *inf;
                    inf = [[NSDictionary alloc] initWithObjectsAndKeys:
                      [NSNumber numberWithShort:keycode[0]],
                      @"keycode",
                      [NSNumber numberWithInt:eventFlags], 
                      @"eventFlags",
                      nil];
                    [[NSNotificationCenter defaultCenter] 
                        postNotificationName:GSEventKeyUpNotification 
                                      object:nil
                                    userInfo:userInfo];
                }
            }
        }
    }
}


If you are asking yourself "Where all those #defines come from?", "Application subclass?, I don't have such a thing in code" then please read the long answer, I hope you find if helpful :)

The long answer:

UIEvents are simple wrappers of GSEventRefs, which contain a GSEventRecord struct in it. Usually we only treat UIEvents representing touch events because the UIApplication will not dispatch other events to our views or objects we create. These are UIInternalEvents and can represent accelerometer events, volume events, keyboard events, etc.

  1. In order to intercept all events our application receives we need to override sendEvent: method in our UIApplication subclass.
  2. We need to access the GSEvent and check it is a key event and then check its flags (Shift, Cmd, Ctrl, Alt). Its easy as that, the problem is we don't know how to get the type and flags of GSEvent, it is a private API :(

- GSEvent objects -
So I did the homework and according to Kenny TM in here and here, GSEvents looks like:
typedef struct __GSEvent {
    CFRuntimeBase _base;
    GSEventRecord record;
} GSEvent;
typedef struct __GSEvent* GSEventRef;
typedef struct GSEventRecord {
    GSEventType type; // 0x8 //2
    GSEventSubType subtype;    // 0xC //3
    CGPoint location;     // 0x10 //4
    CGPoint windowLocation;    // 0x18 //6
    int windowContextId;    // 0x20 //8
    uint64_t timestamp;    // 0x24, from mach_absolute_time //9
    GSWindowRef window;    // 0x2C //
    GSEventFlags flags;    // 0x30 //12
    unsigned senderPID;    // 0x34 //13
    CFIndex infoSize; // 0x38 //14
} GSEventRecord;
typedef struct GSEventKey {
 GSEvent _super;
 UniChar keycode, characterIgnoringModifier, character; // 0x38, 0x3A, 0x3C
 short characterSet;  // 0x3E
 Boolean isKeyRepeating; // 0x40
} GSEventKey;
Headers seems to be a bit old (iOS3~4) but things haven't changed so much. What is for sure is that keycode is right next to infoSize. (Isn't this the fun of private APIs?).
Everything we have to do now is to count bytes from the start of the memory of GSEvent
int *eventMem;
eventMem = (int *)[event performSelector:@selector(_gsEvent)];
GSEventType is at 2
GSEventFlags at 12 and
Unichar keycode at index 15.
I do some checks in the way but that is basically all. :)

Yes, Apple might change GSEvent at anytime so if you are a challenger and would like to submit this to the AppStore, at least do do this conditionally:
// I am lazy to do the proper check here in the post :)
[super sendEvent:event];
if ([[[UIDevice currentDevice] systemVersion] intValue] == 5) {
    ...
}

If you use it or tried to, it would be awesome you drop a line in the comments. (^-^)/
I still have not completed a sample for this little hack but in the mean you can check this this gist out where you can find some keycodes and masks.

References

Thursday, December 15, 2011

Share buttons

Added buttons: Finally added tweet, like, share, burp and fart buttons (LOL) to this humble blog in blogger. So much info around there but not all of them are up to date, it was hard to find the correct information.
Got some info from here, here, here and here put the code inside my blog in the footer :)
Which one was the easiest ?
The winner is LinkedIn besides it looks great :) but GooglePlus was almost as easy as the winner.

Which one was the most difficult ... ?
Facebook, Ohh how I hate Facebook!

Twitter is OK :)
<div class='post-footer'>

 <!-- Tweeter -->
 <div style='float:left;padding:4px;'>
   <a class='twitter-share-button' data-count='none' data-lang='en' data-via='nacho4d' expr:data-text='data:post.title' expr:data-url='data:post.url' href='http://twitter.com/share' rel='me'/>
   <b:if cond='data:post.isFirstPost'><script src='http://platform.twitter.com/widgets.js' type='text/javascript'/></b:if>
 </div>
 <!-- Google plus -->
 <div style='float:left;padding:4px;'>
   <g:plusone annotation='none' expr:href='data:post.url' size='medium'/>
 </div>
 <!-- LinkedIn -->
 <div style='float:left;padding:4px;'>
    <b:if cond='data:post.isFirstPost'><script src='http://platform.linkedin.com/in.js' type='text/javascript'/></b:if>
    <script expr:data-url='data:post.url' type='in/share'/>
 </div>
 <!-- Facebook -->
 <div style='float:left;padding:5px;'>
   <a expr:share_url='data:post.url' name='fb_share' rel='nofollow' type='share'/>  
   <b:if cond='data:post.isFirstPost'><script src='http://static.ak.fbcdn.net/connect.php/js/FB.Share' type='text/javascript'/></b:if>
 </div>
 <br/>
 <br/>

Any kind of comments or criticism is welcomed here :)

Wednesday, December 07, 2011

CATimingFunction with bezier path


CoreAnimation is awesome! specially because it lets you define a timing function for you animation. But, somehow I always find myself googling for a interactive bezier curve creator site or something that lets me draw the curve.

Really, I don't know how many times I've done the same search and sometimes I can't find the page I used in the past.
So this time I wrote a simple but interactive HTML (using the canvas) that helps me define a bezier path I can use to create a custom timing function for CoreAnimation:
Here is the source:
$ git clone git@github.com:nacho4d/BezierJS.git


Monday, August 01, 2011

Versions in OSX 10.7 : Half tutorial, half just notes

Foreword
Last week I spent some days implementing one of the new features in MacOSX 10.7 Lion: Versions and autosave.
It was not so straight forward, APIs are too new that there is no samples yet. Documentation is not enough for newbies like me. (hehe)

I thought that watching Apple's session 107 of WWDC 2011 was going to be enough but it turns out that the video/pdfs dont show all the code you need. Besides I couldn't find the sample code specifically for that session!. TextEdit autosave implementation is just too simple, not very educational in this regard.

I think my case is not trivial. Why? Because I am using a NSWindow subclass with NSBorderlessWindowMask and I have to craft close/minimize/maximize buttons, handle window resizing by my own. I even have to add the autosave button by my own and I still don't get the "--Edited" or "--Locked" string right next to the versions button (NSWindowDocumentVersionsButton)

So here are my notes:
(I assume the app is a document-based cocoa application)

First:
The easy part, override +[NSDocument autosaveInPlace] which will enable versions by default
//MyDocument.h
+ (BOOL)autosavesInPlace{
 return YES;
}
Or override +[NSDocument preservesVersions] directly.

Now, without much effort you will get something like this:

Second:

Still easy, implement the following NSWindowDelegate methods. If you are using a subclass of NSWindowController that is a good place, if not then the NSDocument subclass should be fine :)

To customize the size of the window in the version browser:
//Bug? : In NSBorderlessWindowMask windows below method is called but has no effect!
- (NSSize)window:(NSWindow *)aWindow willResizeForVersionBrowserWithMaxPreferredSize:(NSSize)maxPreferredSize maxAllowedSize:(NSSize)maxAllowedSize{
    //Create a custom size for the window entering version browser
    //This is just an example of a size you might use. This is quite big
    float ratio = maxPreferredSize.height/maxPreferredSize.width;
    NSSize winSize = [aWindow frame].size;
    winSize.height = winSize.width * ratio;
    return winSize;
}

To customize the window when entering/exiting version browser
- (void)windowWillEnterVersionBrowser:(NSNotification *)notification
{

    //Suggested by the docs:
    //disable UI elements that will have no effect in version browser.
    //Simplify the UI, as well

    //Example a textView:
    MyWindow *win = (MyWindow *)[notification object];
    [win hideTitlebarAndStatusBar];     //method of MyWindow : simply the window
    [win setUserIntaractionEnabled:YES];//method of MyWindow : disable UI
    [myTextView setEditable:NO];        //disable UI
}

- (void)windowDidEnterVersionBrowser:(NSNotification *)notification
{
    //If needed, do your thing here
}
- (void)windowWillExitVersionBrowser:(NSNotification *)notification
{
    //If needed, do your thing here too
}

- (void)windowDidExitVersionBrowser:(NSNotification *)notification
{
    //Suggested by the docs
    //Set the window to its original settings

    MyWindow *win = (MyWindow *)[notification object];
    [win showTitlebarAndStatusBar];     //method of MyWindow : restore the window
    [win setUserInteractionEnabled:YES];//method of MyWindow : restore UI
    [myTextView setEditable:YES];       //restore UI
}

Third
In fact this step is easy too but not very well documented, I believe.

In normal cases (When using regular windows with non NSBorderLessWindowMask) doing step 1 and 2 is enough. Cocoa will coordinate buttons in the window and resized to not to work while in the version browser.

In case of NSBorderLessWindowMask windows we need to do more.
As suggested by Apple docs UI/input should be disabled and in the window should be simplified to show only relevant information.

Something that is not clear in the docs is that above methods are only called for the current document (The document in the left side of the version browser).
So in order to disable UI in windows of past versions's documents (the documents in the right side of the browser) we need other APIs:

-[NSDocument isInViewingMode] will return YES for all the documents in the right side of the browser. NO for the current document
And -[NSDocument windowForSheet] will return the windows for all these documents.

So something like this should work:
- (NSWindow *)windowForSheet{
    MyWindow *win = (MyWindow *)[super windowForSheet];
    MyDocument *doc = (MyDocument *)[[win windowController] document];
    if ([self isInViewingMode]) {
        //disable UI temporarily
        [win hideTitlebarAndStatusBar];
        [win setUserInteractionEnabled:NO];
     [doc->myTextView setEditable:NO];
    }else{
        [win showTitlebarAndStatusBar];
        [win setUserInteractionEnabled:YES];
        [doc->myTextView setEditable:YES];
    }
    return win;
}

However, in version browser, each document is created in a different thread, so if you are handling big documents it should be much more efficient to above check in the same a method that run in the same thread. (-[NSDocument windowForSheet] is called in current document thread)

So, we better write above content inside windowControllerDidLoadNib: to avoid thread changing. :)

BTW. If you wan't to debug your app's version browser set flag NSDocumentRevisionsDebugMode to YES. Each window/document in the version browser is loaded in one different thread so without this flag is difficult to debug


Fourth:
These are not mandatory but recommended to implement.
Enabling writing/saving asynchronously might improve dramatically application responsiveness, specially when working with big documents.

- (void)autosaveWithImplicitCancellability:(BOOL)autosavingIsImplicitlyCancellable completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
- (BOOL)canAsynchronouslyWriteToURL:(NSURL *)url ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation;
- (void)continueActivityUsingBlock:(void (^)(void))block;
- (void)continueAsynchronousWorkOnMainThreadUsingBlock:(void (^)(void))block;
- (NSDocument *)duplicateAndReturnError:(NSError **)outError;
etc


Window restoration:
I wish Apple showed this part too in their examples so my life would have been easier but NO! - they like me to suffer! (Well not really...)

Implement these NSWindowDelegate methods too to save/restore some info into/from the coded window.

- (void)window:(NSWindow *)window willEncodeRestorableState:(NSCoder *)state
{
     //any additional info to save 
     //Example:
     [state encodeObject:myObj forKey:@"myObject"];
}
- (void)window:(NSWindow *)window didDecodeRestorableState:(NSCoder *)state
{
     //any additional info to restore
     //Example:
     MyObject *myObj = [state decodeObjectForKey:@"myObject"];
}

Similarly you can override below methods of NSWindow
I am saving the state of the title bar because that is not done by default by Cocoa
- (void)restoreStateWithCoder:(NSCoder *)coder
{
    [super restoreStateWithCoder:coder];
    titleBarHidden = [super decodeBoolForKey:@"titleBarHidden"];
}
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeBool:titleBarHidden forKey:@"titleBarHidden"];
}

Basically that is all.
More info can be found in the following links and surely everywhere very soon as Lion and these features becomes more and more popular.

References

Monday, July 25, 2011

Custom Window Widgets:

This is tricky and didn't really think it would work because all the info found was more than 3 years old. But it did!
I could get a window with custom widgets (Close/Miminize/Maximize and theorically fullscreen buttons), :) . I hope someday I know photoshop as much as objc and make my own cool buttons, for now I have to play with iCal's buttons.



Normal Cocoa NSWindowButtons


Customized NSWindowButtons

The left picture shows the normal cocoa NSWindowButtons, right picture on the right shows darker and yellowish buttons, not very nice on the gray titlebar but ... Is because I don't know photoshop! (涙) But with great potential huh?! (笑)

Introduction:

In cocoa you get the buttons of a window :
NSButton *button = [window standardWindowButton:NSWindowZoomButton];

But button is not really of kind NSButton is in fact an instance of _NSThemeWidget. Furthermore, it does not use a normal NSButtonCell but it has its own _NSThemeWidgetCell. Furthermore, the close button is a subclass of them: _NSThemeCloseWidget and _NSThemeCloseWidget because it has a dirty state of the document

In this post I show how to customize the images of these private classes because setImage: does nothing :(

How this works
I tried to make a new class and pose it as an _NSThemeWidgetCell but posing is being deprecated and is not possible anymore, at least not that I know.

Crayson told me that I should better try the method swizzling approach. This was a huge hint!.
So using a couple of objective-c runtime functions I added a method dynamically to _NSThemeWidgetCell class.

Then it was very easy to exchange methods : the one newly added alt_drawWithFrame:inView: with the other that does the drawing as usual drawWithFrame:inView:.

This part the code and the whole source + the images I borrowed from Lion's iCal can be downloaded from github :)

void drawWithFrameInView(id self, SEL _cmd, NSRect frame, id view)
{   
    NSLog(@"hacking drawWithFrameInView ...");
 
    NSString *imageName = @"titlebarcontrols_regularwin";

    //Get button ID
    int buttonID = (int)[self buttonID];
    NSLog(@"%d", buttonID);
    switch (buttonID)
    {
        case 127: // Close button
            imageName = [imageName stringByAppendingFormat:@"_close"];
            break;
        case 128: // Minimize button
            imageName = [imageName stringByAppendingFormat:@"_minimize"];
            break;
        case 129: // Zoom button
            imageName = [imageName stringByAppendingFormat:@"_zoom" ];
            break;
        case 130: // Toolbar button
            imageName = [imageName stringByAppendingFormat:@"_toolbar_button" ];
            break;
    }

    //Get System preferences: Window style: (Aqua or graphite)
    NSString * const kAppleAquaColorVariant = @"AppleAquaColorVariant";
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults addSuiteNamed:NSGlobalDomain]; 
    NSNumber *color = [userDefaults objectForKey:kAppleAquaColorVariant];
    if ([color intValue] == 6) {//graphite is 6 
        imageName = [imageName stringByAppendingFormat:@"_graphite"];
    }else{//defaults to aqua, (aqua is 1)
        imageName = [imageName stringByAppendingFormat:@"_colorsryg"];
    }

    //Get button state
    if ([self respondsToSelector:@selector(getState:)]) {
        int state = (int)[self getState:view];
        //NSLog(@"state %d", state);
        switch (state) {
            //Known states
            //active = 0
            //activenokey = not used?
            //disabled = not used?
            //inactive = 3
            //pressed = 2
            //rollover = 1
            case 0: 
                imageName = [imageName stringByAppendingFormat:@"_active"];
                break;
            case 1:
                imageName = [imageName stringByAppendingFormat:@"_rollover"];
                break;
            case 2:
                imageName = [imageName stringByAppendingFormat:@"_pressed"];
                break;
            case 3:
                imageName = [imageName stringByAppendingFormat:@"_inactive"];
                break;
            case 4:
                break;//disabled? activenokey?
            case 5:
                break;//disabled? activenokey?
     default:
                break;
        }

        NSImage *img = [NSImage imageNamed:imageName];
        if (img){
            [img dissolveToPoint:NSMakePoint(frame.origin.x, frame.origin.y + frame.size.height) fraction:1.0];
        }else{
            [(NSButtonCell*)self alt_drawWithFrame:frame inView:view];//original implementation 
        }
    }
}

And the part of code that does the objective-c runtime magic:

@implementation TestAppDelegate
@synthesize window;
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{

    Class class = NSClassFromString(@"_NSThemeWidgetCell");
    SEL new_selector = @selector(alt_drawWithFrame:inView:);
    SEL orig_selector = @selector(drawWithFrame:inView:);

    //Add a new method dinamically because _NSThemeWidgetCell is a private class
    BOOL success = class_addMethod(class, new_selector, 
                (IMP)drawWithFrameInView, 
                "v@:{CGRect={CGPoint=dd}{CGSize=dd}}@");
    if (success) {
        //Get the methods to exchange
        Method originalMethod = class_getInstanceMethod(class, orig_selector);
        Method newMethod = class_getInstanceMethod(class, new_selector);

        // If both are found, swizzle them
        if ((originalMethod != nil) && (newMethod != nil)){
            method_exchangeImplementations(originalMethod, newMethod);
        }
    }

//TEST:a new method should appear "alt_drawWithFrame:inView:" in the console
//uint methodCount = 0;
//class = NSClassFromString(@"UIWebDocumentView");
//Method *mlist = class_copyMethodList(class, &methodCount);
//for (int i = 0; i < methodCount; ++i){
// NSLog(@"%@", NSStringFromSelector(method_getName(mlist[i])));
//}

//TEST: "hacking drawWithFrameInView ..." should appear in the console
//NSButton *but = [window standardWindowButton:NSWindowZoomButton];
//[[but cell] drawWithFrame:NSZeroRect inView:nil];
}
References and useful Links

Saturday, July 16, 2011

Instrospection in Objective-C
Sometimes public APIs in cocoa/cocoa-touch frameworks is simply not enough or maybe a private API could save you some work.

So, this is not something new: If you read Objective-c runtime reference you will find really handy and hacky functions there.
These are some snippets:

List up methods of a class
uint methodCount = 0;
Class class = NSClassFromString(@"UIWebDocumentView");
Method *mlist = class_copyMethodList(class, &methodCount);
for (int i = 0; i < methodCount; ++i){
    NSLog(@"%@", NSStringFromSelector(method_getName(mlist[i])));
}
List up protocols conformed by a class
Class class = [UITextView class];
Protocol **p1 = class_copyProtocolList(class, NULL);
for (int i = 0; p1[i]; i++) {
    printf(@"%s\n", protocol_getName(p1[i]));
}
free(p1);
List up ivars of a class
Class class = [UITextView class];
uint ivarsNum = 0;
Ivar *ivars = class_copyIvarList(class, &ivarsNum);
for (int i = 0; i < ivarsNum; i++) {
    printf(@"%s\n", ivar_getName(ivars[i]));
}
Introspect arguments of a method
Class class = [UITextView class];
SEL selector = @selector(keyboardInput:shouldInsertText:isMarkedText:);
Method method = class_getInstanceMethod(class, selector);
char *arg = method_copyArgumentType(method, 0);
printf(@"_%s_\n", arg);
free(arg);

Here you will get: @ for objects, i for integers, f for floats. That is all you get.

Using GDB In Xcode set a symbolic break point to:
-[UITextView keyboardInput:shouldInsertText:isMarkedText:]
or in gdb type:
b -[UITextView keyboardInput:shouldInsertText:isMarkedText:]
so the debugger will stop at that method. When stopped is possible to show the registers values hence is possible to inspect the arguments passed :) This is a more complete list of how to call the registers in different architectures. Since the iOS Simulator is in i386 (after prolog) I can inspect this particular method doing:
(gdb) po *(id*)($ebp + 8)
<MyTextView: 0x5911270; baseClass = UITextView; frame = (80 70; 240 323); text = 'Lorem ipsum dolor sit er ...'; clipsToBounds = YES; autoresize = RM+BM; layer = <CALayer: 0x5c0c7d0>; contentOffset: {0, 0}>

(gdb) p *(SEL*)($ebp + 12)
$1 = (SEL) 0xbd19

(gdb) po *(id*)($ebp + 16)
<UIWebDocumentView: 0xa02f000; frame = (0 0; 240 457); text = 'Lorem ipsum dolor sit er ...'; opaque = NO; userInteractionEnabled = NO; layer = <UIWebLayer: 0x5c35070>>

(gdb) po *(id*)($ebp + 20)
t

(gdb) p *(id*)($ebp + 24)
$2 = (id) 0x0
and finally to get the selector name from a SEL in gdb
(gdb) p (char*)$1
$1 = 0xbd19 "keyboardInput:shouldInsertText:isMarkedText:"

I found this super useful because now I can get the list of method of any class and dig in. Yippeee!

For example I wanted to see if I can customize some shortcuts in the iOS when using a hardware keyboard. But it turns out that the UITextView is not the one who controls that. UITextView seems to be a mere client of UIWebDocumentView which is the one who handles text input and also keyboard events. So overriding private methods of UITextView is just not enough. This is not as simple as I thought to I will save this for another post :)

Links

Thursday, June 16, 2011

NSWindow private methods

So, this week I tried Lion almost for the first time, is getting really cool :)

My motivation to do this is because I want to make my non-standard window enter fullscreen (although now I now this is not the way of doing it) it was interesting. So in OSX 10.6 and earlier NSWindow has: performZoom:, performMiniaturize:, performClose:, etc. So I thought there should be something like:performFullscreen: No?

I dug into 10.7 SDK documentation hoping to find something. Unfortunately no, there seems to be no new things in NSWindow?, at least not that I have noticed. Weird... Maybe I am not looking at the correct docs?

I just thought of this:
#import <objc/runtime.h>

unsigned int methodCount = 0;
Method *mlist = class_copyMethodList([NSWindow class], &methodCount);
for (int i = 0; i < methodCount; ++i){
    NSLog(@"%@", NSStringFromSelector(method_getName(mlist[i])));
}
which will print a list of all methods implemented by NSWindow and it worked! I got the whole list. Including the private methods :)

These Objective-C runtime functions must be the Swiss knife of cocoa-hackers. I still have to try this in 10.7 but This is definitely a good start.

PS: Off-course there was no performFullscreen: method. Later I realized that toggleFullScreen: will do the job :)

Saturday, May 07, 2011

Bezier Paths : making rectangles with rounded corners

Learning at school Bezier paths was fun for me because is when I finally got to know where all those curves come from. I like the maths, and there are a lot of them behind Bezier paths ;) .

Once the theory is known is not complicated to imagine how a path would look like just by knowing its control points position. But, off-course a normal human like me can only make an approximation inside of his/her head. In real life, you don't approximated paths, specially if you are the kind of person who care about small details like me. So, I decided to do a small research and find out the exact position the control points should have in order to approximate an arc or circle or a rounded rectangle.

My choices were: 1. get a computer graphics book or go to wikipedia, remember the maths and then get a pencil and paper and try to solve it mathematically. 2. Trial and error approach. Even though I like maths I like even more programming so decided to go for number 2 and program test my results in javascript(and put the results here)


In order to make a rounded top-left corner I need to...

First trial:

In first place, I thought: I need a cubic bezier path with one control point in the top-left corner and the other at the end of the curve. This is what I got:

Canvas not supported.

That curve starts at point P0 and ends at P1 and use a couple of control points pc0 and pc1. I obtained a nice curve ... nothing new. However the corner I have just drawn is not exactly overlapping the circle in the picture. It's actually outside.

Second trial:

Having the control points in the middle makes sense as well :
Canvas not supported.

This also looks OK. But Why now it looks like the corner was cut or cropped. It seems logical to think that control points should be at the middle like in this case.

Third Trial:

What about quadratic bezier? This seems to be fine but since we have a circle in there we notice this is not correct too.

Canvas not supported.

Fourth Trial:

So I google it and found approximation of a circle using cubic bezier curve There is a paper that has the maths I decided to skip in first place ... In page 3 Dr. Murtaza found the value of kappa: 0.55... which is the (approximated) distance of control point 1 and 2 should have from start and end point respectively. Control points should be placed as follows:

Canvas not supported.

So, if we want to have a rounded rectangle with corner radius r we have to place pc0 and pc1at (0,k)and (k,0) where k is kappa = 0.552228474...

Here we have an almost perfect rounded corner! ;)

I have implemented this in javascript with the canvas element and all the graphics here were rendered using that function. And also I have made an extension for NSBezierPath class so now I can have paths like:

Here are the sources:
NSBezierPath+Extensions.h
NSBezierPath+Extensions.m

And BezierPaths:
Canvas+BezierPaths.js

References:
Mathworks - Approximation of circle using cubic bezier
Wikipedia - Bezier Paths
MDC Docs - Drawing shapes
And maybe this too:Cocoa Dev Central - Intro to Quartz



Wednesday, March 30, 2011

Notes on git + github

I've been using SVN for quite a long time. Recently, started using git and github and really like it! I still need to take notes on some commands so this is what this post is about.
There is an excellent book but is too long to get it every time I need something. So here, I summarize what I believe are the most important/frequent things.

Installation

Download the installer from here or use MacPorts or Homebrew and issue:
# for MacPorts
$ sudo port install git-core +svn +doc +bash_completion +gitweb
# for Homebrew
$ brew install git-core

Setup

Required configuration:
#required:
$ git config --global user.name "firstname lastname" 
$ git config --global user.email nacho4d@nacho4d.com
Specially when working with github, these strings must be the same strings as the ones appearing in github Settings
Optional configurations that I find handy
#optional
$ git config --global core.editor emacs
$ git config --global merge.tool vimdiff
$ git config --global color.ui true 
$ git config format.pretty oneline

To see settings inside /etc/gitconfig and ~/.gitconfig do:
$ git config --list

Creating a repo from scratch

$ cd My/LocalRepo/Directory/
$ git init
$ git add *.m 
$ git add README.txt 
$ git commit -m 'This is the message for the first commit'

Creating a remote repo in github

Follow the instructions in github.
(**Warning**: Github needs a README file to show the project correctly on the web. Make sure you have one)

Cloning a repo from github

Get the address of the repo from github and enter like so:
$ git clone git://github.com/schacon/grit.git

File lifecycle

Explaining the lifecycle is not easy (specially if you come from SVN). But I think below explanation is not incorrect :)
  • Untracked: when a file is not version controlled by git
  • Unmodified: when the file is controlled by git and is not modified since the last staging
  • Modified: when the files is controlled by git and it was modified since the last staging
  • Stagged: when the file is in the index, git commit only commits stagged files

Staging a file:

The add command also is for staging not only for tracking files
$ git add filename.txt
$ git status

Ignoring file:

$ cat .gitignore 
*.[oa]     #ignores .o and .a files
*~         #ignores files that end in ~
build/     #ignores build directory
doc/*.txt  #ignores txt files inside of doc directory, is not recursive
(Glob patterns are accepted)

Seeing stagged and unstagged changes
$ git status
$ git diff
$ git diff --cached
$ git diff --stagged

Committing

$ git commit
and enter the message in the editor. Or:
$ git commit -m "message for this commit" 
Or: just commit everything (including not staged files)
$ git commit -a -m "message for my commit with -a option"

If a file was forgotten you can always amend current staged files to the last commit
$ git add forgotten_file.txt 
$ git commit --amend
# and to change the commuter date too:
$ GIT_COMMITTER_DATE="`date`" git commit --amend --date "`date`"

Removing files

$ rm fileToRemove.txt
$ git rm fileToRemove.txt
and remove a file only from the stagged are
$ git rm --cached readme.txt

Working with Branches

# list of local branches
$ git branch

# list of all (local and remote) branches
$ git branch -a

# list of remote branches only
$ git branch -r
Create a local branch named uiFixes (from current branch). Then rename it to ui-fixes, check it out, commit something to it, publish it and finally delete it from the remote
$ git branch uiFixes
$ git branch -m uiFixes ui-fixes
$ git branch ui-fixes
$ git checkout ui-fixes
$ git branch
... some changes happened here ...
$ git commit -am "fixes where done"
$ git push origin ui-fixes
... now others can see/use/merge,etc this branch
$ git push origin :ui-fixes

Tagging

Create a tag v1.0 and push it to the remote. Then push all tags.
Delete tag v1.0 from local repository and finally delete it from the remote.
If a branch is not merged it cannot be deleted so in that case you can force it with -D option
$ git tag v1.0
$ git push origin v1.0          
$ git push --tags               
$ git tag -d v1.0
$ git push origin :refs/tags/v1.0

Renaming a branch:
Create a new tag with that references to the old tag then delete the old one.
# create the new tag
$ git tag new_tag old_tag
# delete the old tag locally
$ git tag -d old_tag
# delete the old tag from the remote
$ git push origin :refs/tags/old_tag

Checking out a tag:
# Create a branch and checkout
git checkout -b 1.6 refs/tags/v1.6
# Just checkout out the tag
# (Useful when looking at it and no changes are planned to be done)
$ git checkout refs/tags/v1.6

Logging

Shows a list of commits:
# Show last 10 commits
$ git log -n 10

# Show commits with a graph
$ git log --graph

# Compact 
# git log --graph --pretty=oneline --abbrev-commit

Diffs

compare a file with current HEAD
$ git diff file.h

Stash

# stash current changes without a label
$ git stash
# stash current changes with a label
$ git stash save "Put a message here"
#
# stash only unstaged (or non-added) changes
# Note that newly created (and non-added) files will remain in
# your working directory. Also, your working directory must be
# clean (i.e. all changes need to be added) when you `git stash
# pop` later on.
$ git stash -k

$ ...

# list of all stashed things
$ git stash list
# will show something similar to:
stash@{0}: On feature/52: Put a message here
 
$ ...


# Apply the stash to the current branch
$ git stash apply 
# Apply and remove it from the stash list
$ git stash pop

$ git stash clear

Misc:

# show info about remote branches
$ git remote show origin
# export last commit in the current branch
$ git archive -o latest.zip HEAD

Rebase

# interactively rebase (change/modify) the last 4 commits:
$ git rebase -i HEAD~4

# Mainly I use it for:
# "squash" various commits in a single one
# "reword" the message of the commit

# forced push of a branch (not merging)
$ git push origin feature/new-feature -f



Patches

# Make a patch of a certain commit:
$ git format-patch <SHA>^..<SHA>
# It will create a file, let's say: file.path.

# Check the changes that are in the path:
$ git apply --stat file.path

# Apply the patch
$ git apply file.path
# or
$ git am file.path

Update your fork from Original repo

# Add (additional) remote
$ git remote add --track master mleung git://github.com/mleung/feather.git
# 'master' is the tracking branch and 'mleung' is the tracked branch from the given repo

# verify 
$ git remote

# get lattest changes from mleung
$ git fetch mleung

# merge those changes (mleung/master) into your current branch
$ git merge mleung/master

Gitflow, etc

# check out "develop" remote branch and then start git flow
$ git fetch
$ git checkout -t origin/develop
$ git branch

# initialize git flow
$ git flow init

# publish and track "develop" manually
$ git push origin develop
$ git branch --set-upstream develop origin/develop
$ git remote show origin

# feature start and finish
$ git flow feature start markdown-links
$ git commit -am "markdown now shows links in blue color"
$ git flow finish markdown-links

# hotfix start and finish
$ git flow hotfix start my-hotfix
$ git commit -am "I have solved a really serious bug"
$ git flow hotfix finish my-hotfix

TODO:

creating and merging local branches (?)
merging and pushing to remote repository (?)
git merge (?) not only to master branch but to any branch
git revert (?) to any point in the past
git reset (?)
git remote (?)
git help **commmand**(?)


References:


Wednesday, December 15, 2010

Grand Central Dispatch

最近、研究のPCのプログラムをiPhoneに移植することに挑戦していてマルチスレッドプログラミングを授業とかでやったが実践で使うのは初めてで、色々と勉強になったから 参考になったもののまとめとしてこの記事を書くことにした。
色々と忘れそうで...

以下GCDと呼ぶGrand Central DispatchはMacOS 10.6から使えてブロックを使いまくるライブラリです( 笑)WWDC2009に参加したときブロックの話が多くてイメージしていたが実際に使う機会がなくて やはり研究の様な重い処理や時間が掛かる様なものじゃないと使うまでもないし-[NSObject performSelectorOn...]NSOperationなどがあって低レベルのブロックを普段使うまでもありませんね。
WWDC2010ではGCDで盛り上がった.GCDはオプンソースになって現時点でMacOSとiOS以外に実装したOSはFreeBSDのみ

特に参考になったサイトは:
などなど


では、

インポート

まず、GCDはフレームワークではない、dylibなので
#import <dispatch/dispatch.h>
と書くだけでGCDのAPIが使える。

キューの作成

  • メインキュー : メインスレッドで実行
    dispatch_queue_t main = dispatch_get_main_queue();

  • グローバルキュー : バックグラウンドで実行
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    //priories:
    //DISPATCH_QUEUE_PRIORITY_HIGH
    //DISPATCH_QUEUE_PRIORITY_DEFAULT
    //DISPATCH_QUEUE_PRIORITY_LOW

  • プライベートキュー : バックグラウンドで実行、名前付きのキュー
    dispatch_queue_t queue = dispatch_queue_create("com.nacho4d.myApp.myQueue",NULL);
    デフォルトではプライオリティは DISPATCH_QUEUE_PRIORITY_DEFAULT だが変えることが出来る:
    dispatch_queue_t high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,NULL);
    dispatch_set_target_queue(queue,high);
    

    他のタイプのキューとの大きな違いはブロックや関数が逐次的に実行されていくこと。他のキューではタスクをエンキューしても実際にどの順番で実行されるかが分からないが、プライベートなキューでは必ずキューの頭からタスクを実行していくのでシリアルなキューを実現できる。
    プライベートなキューを作成するとRetainカウントが1なので、 最後にリリースをすることを忘れずに
    dispatch_release(high);
    

キューの停止

停止できるが、実行中のブロックを止めることができません。停止がその次のブロックから適用される、つまりNSOperationQueueのsetSuspended:と全く同じ
dispatch_suspend(queue);

非同期と同期

実行するキューを指定し、ブロックを非同期的にまたは同期的に実行
dispatch_async(queue,^{/* 非同期 */});
dispatch_sync(queue,^{/* 同期 */});
例:
dispatch_queue_t queue = dispatch_queue_create(“com.app.task”,NULL)
dispatch_queue_t main = dispatch_get_main_queue();

dispatch_async(queue,^{
 CGFLoat num = [self doSomeMassiveComputation];

 dispatch_async(main,^{
  [self updateUIWithNumber:num];
 });
});
または
dispatch_queue_t queue = dispatch_queue_create(“com.app.task”,NULL);

__block CGFloat num = 0;

dispatch_sync(queue,^{
 num = [self doSomeMassiveComputation];
});

[self updateUIWithNumber:num];

ブロックを後で実行

-[NSObject performSelector:afterDelay]などのメソッドの代わりに使えるdispatch_after関数がある.利点:メソッドは不要

void dispatch_after(
   dispatch_time_t when,
   dispatch_queue_t queue,
   dispatch_block_t block);

例:0.3秒後にメインスレッドでブロックを実行する(UIKitのUITextViewを扱うのでメインスレッドに変えている).
dispatch_after(300, dispatch_get_main_queue(), ^{
        UITextView *textView = (UITextView *)[controller.view viewWithTag:12345];
        [textView removeFromSuperview];
        textView = nil;
    });

Forループの並列化

重い処理を分けて行って高速化を!
void dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
例えば、CoreVideoのイメージバッファーの中から特定なチャネルだけコピーしたい場合は普段このように行うでしょう:
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
size_t width = CVPixelBufferGetWidth(imageBuffer); 
size_t height = CVPixelBufferGetHeight(imageBuffer); 

char *imageData = llahProcessor.inputImageDataBaseAddress;
int stepWidth = width*3; //stepWith of output image
for (int j = 0; j < height; j++) {
 for (int i = 0; i < width; i++) {
  int out_pixIndex = j*stepWidth + i*3;
  int in_pixIndex = j*bytesPerRow +i*4;  
  imageData[out_pixIndex]   = baseAddress[in_pixIndex]; //B
  imageData[out_pixIndex+1] = baseAddress[in_pixIndex+1]; //G
  imageData[out_pixIndex+2] = baseAddress[in_pixIndex+2]; //R
 }
}

しかし、dispatch_apply関数を用いて並列化を行うことができます:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_apply(height, queue, ^(size_t j) {
   for(int i = 0; i < width; ++i ) {
    int out_pixIndex = j*stepWidth + i*3;
    int in_pixIndex = j*bytesPerRow +i*4;
    imageData[out_pixIndex]   = baseAddress[in_pixIndex]; //B
    imageData[out_pixIndex+1] = baseAddress[in_pixIndex+1];//G
    imageData[out_pixIndex+2] = baseAddress[in_pixIndex+2];//R
   }
});
GCD並列処理を行っているので理論上では早いですが、例えば640x480の画像でforループの480個を同時に走らせとうしている!つまり実際に並列処理をするには480個のキューが必要だがそんな膨大な数のキューを作るには少し時間が掛かるしおそらく全部作れないのでエンキューされるforループのあるでしょうし 640x480の画像を処理を一瞬で終わるのでこの例は現実的ではないですえ。dispatch_apply関数の使い方だけに着目してください。マルチスレッドの話は難しくて長過ぎて教科書を読むが一番ですね

シングルトン

Colin Wheeler氏が指摘するようにブロックを使ったシングルトンの方がマルチスレッドセフティでその為に dispatch_onceを使います. dispatch_onceはアプリケーションが実行している間に一回のみ実行されると保証してくれる関数で便利ですね。
+(MyClass *)singleton {
 static dispatch_once_t pred;
 static MyClass *shared = nil;
 
 dispatch_once(&pred, ^{
  shared = [[MyClass alloc] init];
 });
 return shared;
}
まぁ〜 これでGCDのことをちょっと忘れたときにちらっと見れば思い出すかと ... うむ

Friday, December 10, 2010

怠け者の為のObjective-Cのメモリ管理

私はメモリ管理関連のの記事を沢山読います.特にiPhone開発初めてからリークなきクラッシュなきアプリの開発に心掛けた。(モバイルデバイスだから下手に扱うとすぐメモり不足の警告が出されたりするから)

そして、最近ツイッターでこの記事(英文)を見つけたので、簡潔で非常に分かりやすくて日本の方に是非読み頂ければという意思でこの記事を翻訳することにした.(簡潔で非常に分かりやすいですよ!)
私はネイティブではないので、おそらく間違っているところがあるだろうが、エッセンスを理解して頂ければと思う.(→最近友人に見てもらって色々と修正をしてくれたので大感謝!)



James Gregoryさんが想像しているよりObj-Cでメモリの管理は簡単

メモリ管理はということばを聞くとスクリプティングする若者たちとJava世界の人が怖がることを私は知っています。でも、実はそんな怖くないんだよ。詳細を言う前にJames Gregoryさんはどう思うかを見てみよう.


iPhoneにないの?!。マジ、アップルよ 一体なんだよ!? ガーベージコレックションがなぜそんなに凄いかと説明しようとするギークが沢山いるから、ここは短く言う。すばらしい理由が基本的に2つある.
一つ目、さらに他の言語のメモリのオーナーシップ(所有権)、いつリリースするベキかとかを理解する暇はないから.
二つ目、開発言語の特徴の中で 開発のプロセスを最も加速するガーベージコレックション(GC)の他に思い当たらないからだだ.
アンドロイドにはあるし、ハードウエアに直接関係しているプログラム以外(つまり、ほど全て)の場合は効率的だ。初代のiPhoneにはなかった理由が分かるかもしれないが 最近のiPhoneにない訳ないだろう。メモリ管理があまりにも大変でこれだけで初心者の開発者にiPhoneよりはアンドロイドを始めることを薦めると思うよ.



えっ!? それは違うよ。メモリ管理について怠け者たちが、大変だと誤解しているんじゃないかなぁ〜。

「さらに他の言語のメモリのオーナーシップ(所有権)、いつリリースするベキかとかを理解する暇はないから」というのは、デベロッパーがめんどくさいな と思っているだけじゃないかなぁ〜。

オブジェクト指向的なメモリ管理の基本は一緒。C++やらObjectPascalでもGCなしの言語なら みんな一緒だ. コンピュータサイエンスの基本の概念でObjective-Cに使うのはほぼささいなことだ.

もし、私は面接官で面接受けている人がそういうこと言ったら、その時点で面接が終了したなぁ、きっと.

しかし、私はGCがあるべきだと思うよ. GCの利点が知らなかったり、分っていないと勘違いしないでください。だから「手動のメモリ管理が大した苦労ではない」というときに あなたはそれを聞いた方がいいと思う。特にObjective-Cでは.

デスクトップのCocoaで開発する(私は結構書くなぁ)ときにCGを使うときがある. プログラムが少し大きくなって、ちょっと調べたり研究したりしているときにCGを使うことが多い。一旦自分が調べたことや試していたことが証明できたらCGを無効にして、全てをリファクタリングするから.

だから、時々GCを使うなら、いつも使えばいいじゃ〜. メモリの管理しなくてもいいから. それにプライベートフレームワークを作って再利用する傾向があるから、それらを動かす為にCGが無効になっているか有効になっているかを見なくてもよくなるし.

Objective-Cのretain/releaseの仕組みは 根本的なメモリ管理とGCの中間的な仕組みだから. C++とかと違って実際にオブジェクトをfreeするわけではない.代わりに「僕は このオブジェクトを使い終えたよ!」と言ってやって あとランライムに任せばいい.




基本の4つのルール

Objective-Cを学ぶときに覚えなければルールが4つだけ.
  • オーナーシップを持つなら、リリースするベキ
  • オーナーシップを持たないなら、リリースしちゃダメ
  • 自分のクラスの実装でdeallocをオバーライドし、オーナーシップを持ったものをリリースするベキ
  • deallocを直接呼び出すベキでない
それは全てだ. 上の2つが最も重要で オーナーシップの基本を次に説明する.

ルール1: オーナーシップを持つとき

Objective-Cでは オブジェクトをallocか、initか、newをしたらオーナーシップを持つころになる.例えば、


簡単だね、これより詳細な説明も思いつかないぐらい。しかし、確認の為に
allocしたらオーナーシップを持つことになる.
copyしたらオーナーシップを持つことになる.
newしたらオーナーシップを持つことになる.(newはalloc/initのショットカットだけ)
〜〜〜Jamesさん、もしまだ読んでいたら 既に4分の1を終えているから、もう少し読んだらソフト掛けるようになるゼ〜〜〜

ルール2:オーナーシップを持っていないとき

これはちょっとややこしいんだ. もっていないときはもっていないんだよ. だから、もしalloc/init/newいずれもしていなかったら 持っていないのだ. 次の例を見てみよう:

そのNSStringのオーナーシップを持っている?いいえ、alloc/copy/newもしなかったから持っていない.
そのNSImageは?はい、持っている.allocしたから.
そして、そのNSData? これも、いいえ. alloc/copy/newどれもしなかったからだ.
ちなみに、本当はこう直すベキ:

〜〜〜そうだね、GCより遥かに大変だわ! 普段ココアの開発者はどういうふうにやってんの?〜〜〜

ルール3と4: dealloc

これは一番大変なところだ. クラスの中にretainしているインスタンス変数があったら、オブジェクトがdeallocされるときにそれらのインスタンス変数をreleaseしないといけない.これを示すのは次の例:

上記の例では、SomeObjectは二つのインスタンス変数がある. thingssomeOtherThingsだ. initメソッドを見て分かるのは 二つをオブジェクトを生成して、それらをインスタンス変数にアサインしていること.
thingsに関しては [NSMutableArray arrayWithObjects]を使っているからretainを呼び出す必要がある. alloc/copy/newいずれもしない. autoreleaseしたオブジェクトを返すコンビニエンスメソッドを使ったから 明示的にretainをしなければいけない. autoreleaseしたオブジェクトとは何かと思っているかもしれないが それはその次に説明する. とりあえず、上記に説明したメモリ管理の基本だけを頭に入れておいてください.
deallocメソッドに 生成したオブジェクトをリリースしているだけです。それ以上はなにもありません.
(ちなみに、コンビニエンスメソッドとは:alloc/init/newで始まらないオブジェクトを生成してくれるメソッド。例: [NSArray array]はコンビニエンスメソッド、[[NSArray alloc] init]はコンビニエンスメソッドではない。ご存知の通り、コンビニエンスというのは便利の意味を持っていて、releaseしなくてもいいから便利という理由で名前持っているらしい. @nacho4d)


プローパーティ

Objective-C 2.0はプローパティーを提供してくれる.その詳細はここでは話さないが プローパーティのライフサイクルの中で混乱が生じやすいところがあるので 少しだけ触れたいと思う. ルールは2つのみ
プローパティーの属性はretainまたはcopyだったら deallocnilをセットするベキ.
initメソッドなどでプローパーティを初期化(init)しているならば autoreleaseにするベキ.
例を見てみよう.


titleプローパーティに関して NSStringのコンビニエンスメソッドを使って生成しているからautoreleaseしたオブジェクトが返ってきて 何もする必要がありません.
subtitleプローパティーに関して 私たち、自分で、オブジェクトを新しく(alloc/initを使って)生成しているのでautoreleaseをする必要があります.
理由はこれから説明します. alloc/initを使ってretainカウントが1になり、subtitleにアサインすると一個増えるので2になってしまう. autorelease無しで行うとリテインカウントが決して1に戻らなず メモリリークが発生してしまう. だからautoreleaseを入れることで リテインカウントが1個減らしている.

オートリリースとは?

実は完全に嘘をついた、完全に.これは実は結構複雑な仕組みで その複雑さはオートリリースプール(Autorelease pool)という形で来ている。それは一体なんだ? オートリリースプールはreleaseメッセージを送られる対象オブジェクトのリストを持っている。いつ送られるかというとプール
は破壊される(destroyed)また排出させた(drained)ときです。 しかし、それはいつ起きるかを気にしなくても大丈夫です. 正しくautoreleaseメッセージを送っておければ オブジェクトがちゃんとリリースされるからだ.

さて、基本のルールに従って行えば オブジェクトを返すメソッドについての疑問がある筈だなぁ。このオブジェクトのオーナーシップを誰が持っているの?ここはオートリリースプールの出番だ.
例えば:

そのNSImageのオーナーシップを誰が持っている? ここはねぇ、この例では、メッセージを送ったオブジェクトはオーナーシップを持つことになる.よって送られた側にreleaseメッセージを送る必要がある。しかし、これは悪い例です.正しいのは次のよう:

Autoreleaseメッセージが最後にあることをに着目しよう.autoreleaseをすると そのオブジェクトがオートリリースプールに追加され poolが破壊されるときに releaseメッセージが送られます.
(なぜ一個上のが悪い例でこれは良い例かという説明を次にします。
実はメモリ管理の観点から見ると両方とも問題はありません。後でreleaseするか、さきにautoreleaseをするかどっちも一緒です。
しかし、前者の方がCocoaのコンベンションの一つに違反しています。 
私は上に書いたコンビニエンスメソッドの定義によると、alloc/init/newで始まるメソッドを送るとオーナーシップを持つことになります。
逆に言えば、そうで始まらないメソッドを送るとオーナーシップを持たないことになりますね。そして、getAnImageは何かの形でオブジェクトを生成しています。かつ、alloc/init/newで始まらないから コンビニエンスメソッドですよね?こう言ったコンビニエンスメソッドが CocoaのコンベンションによるとAutoreleasedオブジェクトを返す筈だよね?、だから コンベンションに従って書くようにしましょう. @nacho4d)

オートリリースプールの中にさらに別のオートリリースプールはあってもいいので、大量の一時的なオブジェクトを生成するときに便利. そして、もしマルチスレッドの処理をしていれば、新しいスレッド用のオートリリースプールを作る必要があります.

まとめ

思ったより、そんな怖くないし、複雑ではなかろう. 勿論GCを全然簡単だが、例えGCがあっても手動メモリ管理に向いている処理が沢山があって、手動メモリ管理がメーディアやゲームなどのアプリケーションでもっとも使われている。
このようなアプリケーションにはメモリ管理は極めて重要な役割で、知らないとうまくいかない場合が沢山あるよ. ありがたいことに CocoaとObjective-Cを使うとそんなに苦労しなくて済む.

参考文献

実はアップルのドキュメンテーションで十分な筈だが、すばらしい記事が沢山かあって...
などなど...

Saturday, October 23, 2010

More Web OS programming - Adding Models, Views, Controllers to your project.


In WebOS programming everything is about conventions, you create a  certain assistant in the predefined folder with a predefined name. The same for models, views, stylesheets, etc.
This makes the app structure very simple ;)

This is the continuation of the last post so here I go again...

Creating Assistant (or Controllers), Scenes (Views) and Models

In WebOS each scene needs an assistant. Hence creating a scene creates a view and its controller at the same time:

$pwd
/Users/nacho4d/Documents/palmWorkspace
$palm-generate -t new_scene -p "name=myView" Superapp

which means: generate a new scene for Superapp project using the template new_scene and its name is going to be myView.

palm-generate will create a myView-assistant.js file inside Superapp/app/assistants directory and a myView-scene.html file inside a new myView directory inside app, like so:
Superapp/app/myView/myView-scene.html
and finally will add the path of both new files to Superapp/sources.json file. If you prefer it, you can do this process manually and everything will work and give you the possibility of customizing names and places of your new files ;) But that is beyond this post.

There are 4 templates for palm-generate (-t option) and the default is new_app

hello_app        Generates a new application containing a "Hello World" scene.
hello_scene     Generates a "Hello World" scene.
new_app         Generates a new (empty) application.
new_scene      Generates a new (empty) scene.

We do the same process for Models, but manually since there is no template in palm-generate for this.

Create a directory models inside app.
Create a myModel.js and add its path to sources.json file.

So if you create a boxView and its assistant (or controller):
$palm-generate -t new_scene -p "name=boxView" Superapp

and created a box model:
$cd Superapp/app
$mkdir models
$cd models
$touch box.js
$cd ../../
$open sources.json -a Coda.app
then add box.js path like so:

[
    {
     "source": "app/assistants/stage-assistant.js"
    },
    {
        "scenes": "boxView",
        "source": "app/assistants/boxView-assistant.js"
    },
    {
     "source": "app/models/box.js"
    }
]

finally your project should have the following structure:


Be carefull and not make mistakes specially in the paths. (It took me one hour to realize the reason my app was not loading correctly was I missed adding the ','  after the second element of the array in sources.json!! lol)