Rails Development Service from China - Home tag:www.nibirutech.com,2009:mephisto/ Mephisto Drax 2009-01-04T13:22:18Z lingyun tag:www.nibirutech.com,2009-01-04:78 2009-01-04T13:20:00Z 2009-01-04T13:22:18Z userdefault and plist <p>在目前,我们已经经常用到userdefault了。通过[NSUserDefaults standardUserDefaults],我们便可以得到程序的默认userdefault。 在这里面,我们可以存储一些数据,让程序在启动的获取,来改变一些设置,比如说程序窗口的大小,程序上次退出时的状态等等。 在userdefault里存储新的数据前,我们都需要注册一下,相当于预留一个位置,这样才可以存储。如 if([[NSUserDefaults standardUserDefaults] objectForKey:@”expandedAllItems”] nil) {</p> <p>} userdefault 里可以存储Dictionary, Array, Boolean, Date, Data, Number, String. 简单的int,float类型不能,必须得转化为Number方能存储。 对于较复杂的数据,我们可以存一个Dictionary进去。存Dictionary相对存array来说,好处就相当的明显了,我们在取值时,都特别方便。如: if(nil [[NSUserDefaults standardUserDefaults] objectForKey:@”tr.immed”]) {</p> <p>} 之后,才可以在文件里看见它。 比如工程里的Info.plist,该如何获取到呢? 首先我们应该知道,获取到的plist文件,是一个dictionary的格式,获取的方法有两种: 1. [[NSBundle MainBundle] infoDictionary] 通过该函数,返回的dictionary就是我们所要的plist文件的内容 2. [NSDictionary dictionaryWithContentsOfFile:path]; 这个path则是plish文件的所在地址,而我们所要获取的Info.plist文件的地址则是在/Contents/ 文件夹下,而不是/Contents/Resources/ 这一点需要我们注意。</p> <pre><code>最后,当我们修改完了之后,需要把内容存起来,那么就需要用到下面这个函数了。 - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag</code></pre> <pre><code>非常简单的,就完成了对plist文件的修改</code></pre> xuzizhan tag:www.nibirutech.com,2009-01-04:77 2009-01-04T11:49:00Z 2009-01-04T12:28:15Z stan 12月 技术共享 <p>1.首先要分享的是NSRectArray.经常我们会想使用NSArray或者NSMutableArray来装一些对象。但是NSArray和NSMutableArray都不能装简单类型,比如int,bool或者结构体NSPoint,NSRect这些。但是我们在对诸如NSAttributedString进行一些Rect截取,然后track的时候,往往会需要很多保存NSRect,以便重设的时候删除这些Rect.这下保存这些rect就成了问题。 要说的就是NSRectArray.我们可以如下声明 NSRect aRectArray = <span class="caps">NULL</span>; aRectArray = 一个返回值为NSRectArray的方法,比如NSLayoutManager的-(NSRectArray)rectArrayForGlyphRange方法。 之后再用 for(int i=0; i&lt;XXX; i++) { NSRect gylphrect = aRectArray[i] } 变可以对每个rect进行操作了</p> <p>2.其次要说的是关于removeFromSuperView. 我们可以使用NSView 的addSubview方法来增加一个子view,可是NSView确没有 removeSubview. 只能用 NSView的removeFromSuper将自己从superView上移除,而不能方便自由的移除自己身上的subview.这样经常显的很被动。这里分享一下我实现从自己身上移除所有subview的办法。</p> <p>写一个NSView子类。定义一个叫subViews的成员变量数组。 重载NSView的-(void)didAddSubview:(NSView *)subView方法 -(void)didAddSubview:(NSView *)subView {</p> <p>} 这样每次使用addSubView为这个view累增加子view的时候,这个子view都会被同时存入数组 之后写一个叫removeSubviews的方法来移除所有子view - (void)removeSubviews { if([subViews count] &lt;= 0) return;</p> <pre><code>for(NSView *aView in subViews) {</code></pre> <pre><code>}</code></pre> <p>} 以后只用方便的调用removeSubviews方法便可以清楚所有子view了。</p> xuzizhan tag:www.nibirutech.com,2008-12-09:76 2008-12-09T13:29:00Z 2008-12-09T13:48:45Z 一些易犯不易发现的错误 首先,以前说过,对于NSTextView的string方法并不是返回一个新的NSString对象,而是返回一个指向这个textView的字符串指针,所以当textView中文字改变时,之前返回的那个字符串也会改变。要想返回一个新的string对象需要用[[NSTextView string] copy] 同样,对于NSImage的 imageNamed方法也是一样.通过[NSImage imageNamed:@"xxx"]得到的image对象都是指向xxx这张图片的。比如你在2个类分别用到了 NSImage *image1 = [NSImage imageNamed:@"xxx"];和 NSImage *image2 = [NSImage imageNamed:@"xxx"]; 然后你在第一个类中使用了[image1 setFlipped:YES];来把image1翻转。此时你会发现身在另外一个界面的image2也翻转了。解决方法当然还是一样[[NSImage imageNamed:@"xxx"] copy]; 另外一个很容易犯,切很难debug的错误就是关于重载方法以后的[super xXX];调用父类方法。 由于对view的逐渐深入的使用,经常会重载诸如mouseDown:(NSEvent *)theEvent 一样的方法,重载时候的想法一般是,“我想在鼠标点击的时候干XXXXX事情”,很久以后会发现一些界面bug,很难找到原因,最后才发现是因为没有调用[super mouseDown:theEvent];意思是你把人家mouseDown本来该干的事情都抹杀掉了。 这些问题都很简单,不过很容易粗心就忽略了,而且很难排查,尽量第一次写到最好才是王道 lingyun tag:www.nibirutech.com,2008-12-09:75 2008-12-09T09:40:00Z 2008-12-09T09:45:23Z how to use parameter "..." <p>在使用NSDictionary的时候,经常使用到一个类方法,如下 <p>+ (id)dictionaryWithObjectsAndKeys:(id)firstObject , ...</p> <p>该方法后面的… 表明程序员可以在后面添加多个参数,如下面的这个列子</p> <p>NSDictionary <strong>dict = [NSDictionary dictionaryWithObjectsAndKeys: <code>"value1", </code>“key1”, <code>"value2", </code>“key2”, nil];</p> </p> <p>这主要是通过一个桟实现的。函数中对参数的处理主要是通过对栈进行操作,而c函数的实参都是自右向左压入栈的.</p> <p>主要的栈操作(都是宏)有va_list,va_start ,va_arg,va_end, 定义如下:</p> <p>typedef char * va_list; #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1) ) #define va_start _crt_va_start #define va_arg _crt_va_arg #define va_end _crt_va_end #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) – _INTSIZEOF(t)) ) #define _crt_va_end(ap) ( ap = (va_list)0 )</p> <p>va_start(ap,v):主要是获取可变参数列表的首地址,然后赋值给ap,近似ap=&v+sizeof(v) (这里暂不考虑内存对齐和类型转换)</p> <p>va_arg(ap,t):取得返回类型t的可变参数值, 并使ap指向下一个参数: ap += sizeof(t),这里的t是可变参数的数据类型,如int,float之类</p> <p>va_end(ap):结束</p> <p>va_start(ap,v) va_arg(ap,t) va_end(ap)三者合用,保证程序的健壮性.</p> <p>下面是我写的一个处理多个通知的函数:</p> <p>+ (void)addOBserver:(id)notificationObserver withSelectorsAndNotificationNames:(NSString</strong>)firstObject, ... { va_list argList; </p> <pre><code>NSString *selector = firstObject, *notificationName, *currentObject;</code></pre> <p>//开始 以firstObject为头,获取后面的参数。 va_start(argList,firstObject);</p> <p>// withSelectorsAndNotificationNames,由于桟里的参数是以两个为一组,于是添加了一个标记量</p> <p><span class="caps">BOOL</span> flag = NO; while (currentObject = va_arg(argList, id))//获取id类型的可变参数值,并指向下个,当为nil的时候,退出循环,这也是为什么在多个参数的最后是以nil结尾的。 { if(flag) selector = currentObject; else { notificationName = currentObject; [[NSNotificationCenter defaultCenter] addObserver:notificationObserver selector:NSSelectorFromString(selector) name:notificationName object:nil]; } flag = !flag; } }</p> <pre><code>va_end(argList);//结束</code></pre> <p>使用:</p> <pre><code>[NBUtility addOBserver:self withSelectorsAndNotificationNames:<code>"doPublicTimeline:",</code>"serviceIsPublicTimelineEnabled", @"refreshOutlineView",REFRESH_OUTLINEVIEW, <code>"showSendMessageSheet:",</code>“showSendMessageSheet” , <code>"expandNewAccount:",</code>“NBAccountAdded”,nil];</code></pre> <p>需要注意的是参数的顺序,以及末尾以nil结尾。</p> xuzepei tag:www.nibirutech.com,2008-12-08:74 2008-12-08T15:55:00Z 2008-12-08T16:00:23Z 设计模式学习体会之Factory模式 <div>&lt;font>Factory模式 C++ 代码:&lt;/font></div> <div><br> &lt;font>#include &lt;stdio.h&gt;&lt;/font></div> <div>&lt;font>class CFruit<br> {<br> public:<br> &nbsp; virtual void What() = 0;<br> };&lt;/font></div> <div>&lt;font>&nbsp;&lt;/font></div> <div>&lt;font>class CApple : public CFruit<br> {<br> public:<br> &nbsp; virtual void What()<br> &nbsp; {<br> &nbsp;&nbsp;&nbsp; printf("我是一个苹果\n");<br> &nbsp; }&lt;/font></div> <div>&lt;font><br> };&lt;/font></div> <div>&lt;font>&nbsp;&lt;/font></div> <div>&lt;font>class CPear : public CFruit<br> {<br> public:<br> &nbsp;virtual void What()<br> &nbsp;{<br> &nbsp;&nbsp;&nbsp;printf("我是一只雪梨\n");<br> &nbsp;}&lt;/font></div> <div>&lt;font><br> };&lt;/font></div> <div>&lt;font>&nbsp;&lt;/font></div> <div>&lt;font>class CBanana : public CFruit<br> {<br> public:<br> &nbsp;virtual void What()<br> &nbsp;{<br> &nbsp;&nbsp;&nbsp;printf("我是一条香蕉\n");<br> &nbsp;}&lt;/font></div> <div>&lt;font><br> };&lt;/font></div> <div><br> &lt;font>enum eFruitType{APPLE=1, PEAR, BANANA};&lt;/font></div> <div>&lt;font>&lt;/font>&nbsp;</div> <div>&lt;font>class CFruitFactory<br> {<br> public:&lt;/font></div> &lt;font> <div><br> &nbsp;CFruit *GetFruitInstance(eFruitType type)<br> &nbsp;{</div> <div><br> &nbsp;&nbsp;printf("工厂正在生产水果...\n");</div> <div><br> &nbsp;&nbsp;switch(type)<br> &nbsp;&nbsp;{<br> &nbsp;&nbsp;&nbsp;&nbsp; case APPLE:<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new CApple();<br> &nbsp;&nbsp;&nbsp;&nbsp; case PEAR:&nbsp;&nbsp;&nbsp;&nbsp; <br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new CPear();<br> &nbsp;&nbsp;&nbsp;&nbsp; case BANANA:<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new CBanana();<br> &nbsp;&nbsp;&nbsp;&nbsp; default:<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br> &nbsp;&nbsp;}</div> <div><br> &nbsp;}&lt;/font></div> <div>&lt;font><br> };&lt;/font></div> <div><br> &lt;font>int main()<br> {&lt;/font></div> &lt;font> <div><br> &nbsp;CFruitFactory factory;<br> &nbsp;CFruit *fruit;<br> &nbsp;<br> &nbsp;printf("你想吃什么水果?\n");<br> &nbsp;printf("1 苹果, 2 雪梨, 3 香蕉\n");</div> <div><br> &nbsp;int nType = 0;<br> &nbsp;scanf("%d", &amp;nType);</div> <div><br> &nbsp;fruit = factory.GetFruitInstance(eFruitType(nType));<br> &nbsp;if(fruit == NULL)<br> &nbsp;{<br> &nbsp;&nbsp;&nbsp; printf("没有这种水。");<br> &nbsp;&nbsp;&nbsp; return 0;<br> &nbsp;}</div> <div><br> &nbsp;fruit-&gt;What();&lt;/font></div> <p>&lt;font>&nbsp;return 0;<br> }&lt;/font></p> <div>&nbsp;</div> <div>&lt;font>体会:&lt;/font></div> <div>&lt;font>(1).可以看出Factory模式将具体Product方法抽象出来(如CFuit是CApple,CPear,CBanana的抽象一样)&lt;/font></div> <div>&lt;font>(2).通过一个Factory类来完成根据Product的类型,生产Product的功能,该类具有一个方法,返回抽象Product类的指针(如:CFuit*)&lt;/font></div> <div>&lt;font>(3).通过得到的抽象Product的指针,可以调用具体子类的方法。&lt;/font></div> <div>&lt;font>&nbsp;&lt;/font></div> <div>&lt;font>优点:&lt;/font></div> <div>&lt;font>(1)封装&lt;font>创建过程&lt;/font>。客户不用知道类厂是如何创建类实例的,类厂封闭了所有创建的细节。这样可选择不同的创建方法,增加了灵活性。 &lt;/font></div> <div>&lt;font>(2)将客户与具体类&lt;font>隔离&lt;/font>,提高了各自的可重用性。&lt;/font></div> <div>&lt;font>&nbsp;<br> 缺点:&lt;/font></div> <div>&lt;font>Factory类层次与具体类层次通常是平行的(即一一对应的)。增加一个具体类,一般也要相应地增加一个Factory类,增加了系统复杂度。&lt;/font></div> <br> liwei tag:www.nibirutech.com,2008-11-29:73 2008-11-29T01:06:00Z 2008-11-29T01:11:34Z get text height do you want to get the height of some text? here is a method. UILabel *textLabel = [[[UILabel alloc] init] autorelease]; textLabel.text = textWantGetHeight; textLabel.font = [UIFont systemFontOfSize:13]; textLabel.textAlignment = UITextAlignmentLeft; textLabel.numberOfLines = 4; //the lines limited textLabel.lineBreakMode = UILineBreakModeWordWrap; CGRect frame = [textLabel textRectForBounds:CGRectMake(0, 0,250, 200) limitedToNumberOfLines:4]; frame.size.height is what you want. houzongming@nibirutech.com tag:www.nibirutech.com,2008-11-19:72 2008-11-19T09:58:00Z 2008-11-19T10:13:45Z experience share in Nambu <p>1, 设置当前程序,使之可以自动运行. </p> <pre><code>a) NSMutableArray <strong>loginItems = (NSMutableArray</strong>) CFPreferencesCopyValue((CFStringRef)</code></pre> <pre><code><code>"AutoLaunchedApplicationDictionary", (CFStringRef) </code>"loginwindow",</code></pre> <pre><code>kCFPreferencesCurrentUser, kCFPreferencesAnyHost);</code></pre> <pre><code>loginItems = [[loginItems autorelease] mutableCopy];</code></pre> <pre><code>如此得到所有启动项,其中每个启动项都是个dictionary,启动项有2个key,分别是Hide(BOOL类型), Path(String类型)</code></pre> <pre><code>b) 删除启动项则 [loginItems removeObject:anItem];</code></pre> <pre><code>增加启动项则 [loginItems addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],<code>"Hide", appPath, </code>"Path", nil]];</code></pre> <pre><code>注:appPath可以 = [[[NSBundle mainBundle] bundlePath] copy]</code></pre> <pre><code>c) 最后,写回启动项去.</code></pre> <pre><code>CFPreferencesSetValue((CFStringRef)</code></pre> <pre><code>@"AutoLaunchedApplicationDictionary", loginItems, (CFStringRef)</code></pre> <pre><code>@"loginwindow", kCFPreferencesCurrentUser, kCFPreferencesAnyHost);</code></pre> <pre><code>CFPreferencesSynchronize((CFStringRef) @"loginwindow",</code></pre> <pre><code>kCFPreferencesCurrentUser, kCFPreferencesAnyHost);</code></pre> <p>2, 窗口关闭之后,通过点击dock上的图标,再次显示一个窗口的做法.</p> <p>- (BOOL)applicationShouldHandleReopen:(NSApplication <strong>)theApplication hasVisibleWindows:(BOOL)flag</p> <p>{</p> <pre><code>if (flag) </code></pre> <pre><code>return NO;</code></pre> <pre><code>else</code></pre> <pre><code>{</code></pre> <p>return <span class="caps">YES</span>;</p> <pre><code>}</code></pre> <p>}</p> <p>3, 像sql语句一样,限制返回结果的条数,用setFetchLimit</p> <p>// get the objects of the entites according predicate and sortDescriptors and limit</p> <p>+ (NSArray *)objectsEntityForName:(NSString *)entityName</p> <pre><code>forPredicate:(NSPredicate *)predicate</code></pre> <pre><code>forSortDescriptors:(NSArray</strong>)sortDescriptors</code></pre> <pre><code>limit:(NSInteger)limit</code></pre> <p>{</p> <pre><code>NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[NBCoreData managedObjectContext]];</code></pre> <pre><code>NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];</code></pre> <p>if(sortDescriptors)</p> <p>NSArray *objects = [[NBCoreData managedObjectContext] executeFetchRequest:request error:nil]; }</p> <pre><code>return objects;</code></pre> <p>4, 高效的通知行高变化,如果你用[NSIndexSet indexSetWithIndexesInRange:0—rowsCount – 1]</p> liwei tag:www.nibirutech.com,2008-10-31:71 2008-10-31T09:01:00Z 2008-10-31T09:02:57Z some thing about keyboard 知道键盘什么时候出现与消失可能是非常有用的,具体实现如下: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:self.view.window]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:self.view.window]; //called the keyboard show. - (void)keyboardWillShow { } //called the keyboard hide. - (void)keyboardWillHide { } xuzepei tag:www.nibirutech.com,2008-10-28:69 2008-10-28T01:08:00Z 2008-10-27T16:10:09Z Object-C 内存管理小结 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> &lt;html> &lt;head> &lt;meta> &lt;base> &lt;style> /* default css */ table { font-size: 1em; line-height: inherit; } tr { text-align: left; } div, address, ol, ul, li, option, select { margin-top: 0px; margin-bottom: 0px; } p { margin: 0px; } body { margin: 6px; padding: 0px; font-family: Verdana, sans-serif; font-size: 10pt; background-color: #ffffff; } img { -moz-force-broken-image-icon: 1; } @media screen { html.pageview { background-color: #f3f3f3 !important; } body { min-height: 1100px; } * html body { height: 1100px; } .pageview body { border-top: 1px solid #ccc; border-left: 1px solid #ccc; border-right: 2px solid #bbb; border-bottom: 2px solid #bbb; width: 648px !important; margin: 15px auto 25px; padding: 40px 50px; } /* IE6 */ * html { overflow-y: scroll; } * html.pageview body { overflow-x: auto; } /* Prevent repaint errors when scrolling in Safari. This "Star-7" css hack targets Safari 3.1, but not WebKit nightlies and presumably Safari 4. That's OK because this bug is fixed in WebKit nightlies/Safari 4 :-). */ html*#wys_frame::before { content: '\A0'; position: fixed; overflow: hidden; width: 0; height: 0; top: 0; left: 0; } .writely-callout-data { display: none; } .writely-footnote-marker { background-image: url('MISSING'); background-color: transparent; background-repeat: no-repeat; width: 7px; overflow: hidden; height: 16px; vertical-align: top; } .editor .writely-footnote-marker { cursor: move; } .writely-footnote-marker-highlight { background-position: -15px 0; } .writely-footnote-hide-selection ::-moz-selection, .writely-footnote-hide-selection::-moz-selection { background: transparent; } .writely-footnote-hide-selection ::selection, .writely-footnote-hide-selection::selection { background: transparent; } .writely-footnote-hide-selection { cursor: move; } .editor .writely-comment-yellow { background-color: #FF9; background-position: -240px 0; } .editor .writely-comment-yellow-hover { background-color: #FF0; background-position: -224px 0; } .editor .writely-comment-blue { background-color: #C0D3FF; background-position: -16px 0; } .editor .writely-comment-blue-hover { background-color: #6292FE; background-position: 0 0; } .editor .writely-comment-orange { background-color: #FFDEAD; background-position: -80px 0; } .editor .writely-comment-orange-hover { background-color: #F90; background-position: -64px 0; } .editor .writely-comment-green { background-color: #99FBB3; background-position: -48px 0; } .editor .writely-comment-green-hover { background-color: #00F442; background-position: -32px 0; } .editor .writely-comment-cyan { background-color: #CFF; background-position: -208px 0; } .editor .writely-comment-cyan-hover { background-color: #0FF; background-position: -192px 0; } .editor .writely-comment-purple { background-color: #EBCCFF; background-position: -144px 0; } .editor .writely-comment-purple-hover { background-color: #90F; background-position: -128px 0; } .editor .writely-comment-magenta { background-color: #FCF; background-position: -112px 0; } .editor .writely-comment-magenta-hover { background-color: #F0F; background-position: -96px 0; } .editor .writely-comment-red { background-color: #FFCACA; background-position: -176px 0; } .editor .writely-comment-red-hover { background-color: #FF7A7A; background-position: -160px 0; } .editor .writely-comment-marker { background-image: url('MISSING'); background-color: transparent; padding-right: 11px; background-repeat: no-repeat; width: 16px; height: 16px; -moz-user-select: none; } .editor .writely-comment-hidden { padding: 0; background: none; } .editor .writely-comment-marker-hidden { background: none; padding: 0; width: 0; } .editor .writely-comment-none { opacity: .2; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=20); -moz-opacity: .2; } .editor .writely-comment-none-hover { opacity: .2; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=20); -moz-opacity: .2; } .br_fix br:not(:-moz-last-node):not(:-moz-first-node) { position:relative; left: -1ex } .br_fix br+br { position: static !important } } h6 { font-size: 8pt } h5 { font-size: 8pt } h4 { font-size: 10pt } h3 { font-size: 12pt } h2 { font-size: 14pt } h1 { font-size: 18pt } blockquote {padding: 10px; border: 1px #DDD dashed } a img {border: 0} .pb { border-width: 0; page-break-after: always; /* We don't want this to be resizeable, so enforce a width and height using !important */ height: 1px !important; width: 100% !important; } .editor .pb { border-top: 1px dashed #C0C0C0; border-bottom: 1px dashed #C0C0C0; } div.google_header, div.google_footer { position: relative; margin-top: 1em; margin-bottom: 1em; } /* Table of contents */ .editor div.writely-toc { background-color: #f3f3f3; border: 1px solid #ccc; } .writely-toc > ol { padding-left: 3em; font-weight: bold; } ol.writely-toc-subheading { padding-left: 1em; font-weight: normal; } /* IE6 only */ * html writely-toc ol { list-style-position: inside; } .writely-toc-none { list-style-type: none; } .writely-toc-decimal { list-style-type: decimal; } .writely-toc-upper-alpha { list-style-type: upper-alpha; } .writely-toc-lower-alpha { list-style-type: lower-alpha; } .writely-toc-upper-roman { list-style-type: upper-roman; } .writely-toc-lower-roman { list-style-type: lower-roman; } .writely-toc-disc { list-style-type: disc; } /* end default css */ /* default print css */ @media print { body { padding: 0; margin: 0; } div.google_header, div.google_footer { display: block; min-height: 0; border: none; } div.google_header { flow: static(header); } /* used to insert page numbers */ div.google_header::before, div.google_footer::before { position: absolute; top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } callout.google_footnote { display: prince-footnote; footnote-style-position: inside; /* These styles keep the footnote from taking on the style of the text surrounding the footnote marker. They can be overridden in the document CSS. */ color: #000; font-family: Verdana; font-size: 10.0pt; font-weight: normal; } /* Table of contents */ #WritelyTableOfContents a::after { content: leader('.') target-counter(attr(href), page); } #WritelyTableOfContents a { text-decoration: none; color: black; } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } @footnotes { border-top: solid black thin; padding-top: 8pt; } } /* end default print css */ /* custom css */ /* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } /* end ui edited css */ /* editor CSS */ .editor a:visited {color: #551A8B} .editor table.zeroBorder {border: 1px dotted gray} .editor table.zeroBorder td {border: 1px dotted gray} .editor table.zeroBorder th {border: 1px dotted gray} .editor div.google_header, .editor div.google_footer { border: 2px #DDDDDD dashed; position: static; width: 100%; min-height: 2em; } .editor .misspell {background-color: yellow} .editor .writely-comment { font-size: 9pt; line-height: 1.4; padding: 1px; border: 1px dashed #C0C0C0 } /* end editor CSS */ &lt;/style> &lt;/head> &lt;body> <div> <br> &lt;font><b>一、非GC(Garbage Collection)模式</b>&lt;/font> </div> <div> &lt;font>&lt;/font>&nbsp; </div> <div> &lt;font>1.在非GC模式中,每个对象都有与之关联的引用计数(Reference Count),当分配或复制对象时(<span class="docEmphStrong"><tt>alloc</tt></span>, <span class="docEmphStrong"><tt>new</tt></span>, <span class="docEmphStrong"><tt>copy</tt></span>,&nbsp;or <span class="docEmphStrong"><tt>mutableCopy</tt></span>&lt;/font><a></a>&lt;font> )引用计数自动设为1。&lt;/font> </div> <div> &lt;font>&lt;/font>&nbsp; </div> <div> &lt;font>2.可以使用release方法将引用计数减1,当引用计数为0时,release方法会调用对象的dealloc方法来销毁对象。&lt;/font> </div> <div> &lt;font>&lt;/font>&nbsp; </div> <div> &lt;font>3.可以使用retain方法来使引用计数加1,保持对象。&lt;/font> </div> <div> &lt;font>&lt;/font>&nbsp; </div> <div> &lt;font>4.如果一个对象被添加到数组,它的引用计数将加1,当该数组被销毁时对象的引用计数将减一。所以为了保证数组销毁时,数组中存放的对象也能销毁。通常我们将一个对象加入数组以后,可以调用release方法将对象引用计数减1.&lt;/font> </div> <div> &lt;font>&lt;/font>&nbsp; </div> <div> &lt;font>exp.&lt;/font> </div> <div> &lt;font>&nbsp;&nbsp;&nbsp; LotteryEntry *newEntry;<br> &nbsp;&nbsp;&nbsp; newEntry = [[LotteryEntry alloc] initWithEntryDate:iWeeksFromNow];<br> &nbsp;&nbsp;&nbsp; [array addObject:newEntry];<br> &nbsp;&nbsp;&nbsp; <span class="docEmphStrong">[newEntry release];</span>&lt;/font> </div> <p> <span class="docEmphStrong">&lt;font>&lt;/font></span>&nbsp; </p> <div> <span class="docEmphStrong">&lt;font>5. 数组在添加对象时,并没有拷贝对象,只是保存了对象的指针,并将对象的引用计数加1。当数组销毁时,数组的每个对象都会调用release方法&lt;/font></span> </div> <p> <span class="docEmphStrong">&lt;font>&lt;/font></span>&nbsp; </p> <div> <span class="docEmphStrong">&lt;font>6.在仿问设置属性的方法时,应该先保持在释放&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>&lt;/font></span>&nbsp; </div> <div> <span class="docEmphStrong">&lt;font>exp.&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>- (void)setProperty:(id)newProperty&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>{&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>&nbsp;&nbsp;&nbsp;&nbsp;[newProperty retain];&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>&nbsp;&nbsp;&nbsp; [property release];&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>&nbsp;&nbsp;&nbsp; property = newProperty;&lt;/font></span> </div> <div> <span class="docEmphStrong">&lt;font>}&lt;/font></span> </div> <div> <span class="docEmphStrong"></span>&nbsp; </div> <div> <span class="docEmphStrong">&lt;font>7.autorelease 方法用法标记对象不会被释放直到事件循环结束。每个应用程序都至少有一个自动释放池(autorelease pool),通过发送autorelease消息可将对象放入池中。在应用程序的事件循环中,当代码执行完毕并将控制权交还给应用程序对象时,应用程序对象会放送一个release消息给自动释放池,而自动释放池再给它包含的每个对象放送release消息。任何引用计数为0的对象就会自动的自我回收。&lt;/font></span> </div> <div> &lt;font>&nbsp;&lt;/font> </div> <div> &lt;font>8.许多OC类,拥有类的方法,可以直接返回autorelease对象。&lt;/font> </div> <div> &nbsp; </div> <div> <div> &lt;font>exp.&lt;/font> </div> <div> &lt;font>- (NSString *)description&lt;/font> </div> <div> &lt;font>{&lt;/font> </div> <div> &lt;font>&nbsp;&nbsp; return [NSString stringWithFormat:@"%d",count];&lt;/font> </div> <div> &lt;font>}&lt;/font> </div> <div> &nbsp; </div> </div> <div> &nbsp; </div> <div> &lt;font>9. NSObject类的retainCount方法可以返回对象当前的引用计数。&lt;/font> </div> <div> &nbsp; </div> <div> &nbsp; </div> <div> &lt;font><b>二、GC模式</b>&lt;/font> </div> <div> <b>&lt;font>&lt;/font></b>&nbsp; </div> <div> &lt;font>1. &lt;font>在GC模式下&lt;/font>(<span class="docEmphStrong"><tt>retain</tt></span>, <span class="docEmphStrong"><tt>release</tt></span>,&nbsp; <span class="docEmphStrong"><tt>autorelease or dealloc)方法被忽略。</tt></span>&lt;/font> </div> <div> &lt;font><span class="docEmphStrong"><tt></tt></span>&lt;/font>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>2.在GC模式下如果想要在对象销毁前做些处理工作,可以重载finalize方法,该方法在对象被销毁前被调用&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>exp.&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>- (void)finalize&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>{&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp; //you can do some last time stuff here&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp;[super finalize];&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>}&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>3. 非对象数据使用GC&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>exp.&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>int *intBuff;&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>setp 1. 使用Strong references声明变量&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>__strong int *intBuff;&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>setp 2. 使用<span class="docEmphStrong"><tt>NSAllocateCollectable方法为变量分配空间,并且将该方法的第2个参数设置为NSScannedOption</tt></span>&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font><span class="docEmphStrong"><tt>intBuff= NSAllocateCollectable(100 * sizeof(int), NSScannedOption);</tt></span>&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font><span class="docEmphStrong"><tt></tt></span>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt><span class="docEmphStrong"><tt>&lt;font>4. 对于Core Foundation 数据结构,可以调用CFMakeCollectable方法来使用GC&lt;/font></tt></span></tt></span></div> <div><span class="docEmphStrong"><tt><span class="docEmphStrong"><tt></tt></span></tt></span>&lt;font>&nbsp;&lt;/font></div> <div><span class="docEmphStrong"><tt>&lt;font>5.GC模式的优缺点:&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>优点:&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp; i. 对多线程支持比较好&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp; ii. 减少内存泄漏&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp;&nbsp;iii. 高效的访问方法&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>缺点:&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp;&nbsp;i.不适应在需要大量创建对象的程序中&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp;&nbsp;ii.对内存的堆栈占用比较大&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&nbsp;&nbsp;&nbsp;&nbsp;iii.由于collector运行在线程中,跟非GC的程序相比,更占用cpu&lt;/font></tt></span></div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div> <div><span class="docEmphStrong"><tt>&lt;font>&lt;/font></tt></span>&nbsp;</div><br> xuzizhan tag:www.nibirutech.com,2008-10-28:70 2008-10-28T00:52:00Z 2008-10-28T00:54:43Z 一个比较隐蔽的错误以及mouseTracking <p>首先说个很容易忽略,又能耗你很多时间去debug的问题.</p> <p>最近写代码的时候,遇到一个很囧的错误。众所周知,我们通常建一个controller类,来控制界面。在我这个controller类里,声明了一个 NSTextField *statusText,并在IB里拉上线连接到界面上对应的textField, 这个textField用来显示状态信息。然后在这个controller类里,我想用一个方法来设置这个textField的stringValue,根据命名规范,我把这个方法叫做setStatusText - (void)setStatusText:(NSString *)newStatus { }</p> <p>在awakeFromNib里,写上[self setStatusText:@”new status”]; 编译运行,发现界面上对应的textField地方没有任何东西和文字,用debuger追踪一看,发现[statusText setStringValue:newStatus];中的statusText对象根本不存在.怎么回事呢? 因为,根据key value-coding规则,如果你有一个成员变量叫(id)abc,那么cocoa会自动为你生成(id)abc和setAbc:(id)value;这2个方法用来读取和修改abc.不过系统会查找看你是否已经自己写了这2个方法,如果是,则会调用你自己写的方法来读取和修改。再看这个controller类,我有个成员变量叫statusText,刚好我这个方法叫setStatusText,于是IB在初始化statusText的时候就不会自己init,而是调用我这个setStatusText方法,而这个方法中我自己并没有生成NSTextField对象,因此statusText对象根本就不存在,自然运行以后什么都没有。</p> <p>一句话来说就是,大家在给方法取名的时候,如果用到setXxxYyy的时候,就要注意xxxYyy是否与你的成员变量重名了。</p> <p>然后,最近在做一些tool tip window的效果。实现的效果是,当鼠标进入某一个区域(比如一张图片的范围),弹出一个tool tip window,显示相关信息,鼠标在此区域移动时候,窗口跟随鼠标移动,当鼠标移除区域后,窗口消失。 苹果在NSView中提供了- (NSTrackingRectTag)addTrackingRect:(NSRect)aRect owner:(id)userObject userData:(void *)userData assumeInside:(BOOL)flag 方法,顾名思义,trackingRect就是追踪view中一个rect来响应鼠标事件。 返回值NSTrackingRectTag就是一个int,用来标记你添加的这个trackingRect,以便你以后使用-(void)removeTrackingRect:(NSTrackingRectTag)aTag方法来移除它。 aRect自然就是你要响应的区域,这里就设置为图片区域。 owner就是-(void)mouseEntered等方法所在对象了,一般为self。 assumeInside为YES的话,既当trackingRect添加后,就算发现鼠标在其中,也不响应,要等鼠标第一次离开后才开始响应,所以一般设置为NO. 添加区域以后,就在自己的view中重载- (void)mouseEntered:(NSEvent *)theEvent和-(void)mouseExited:(NSEvent *)theEvent方法。顾名思义,当鼠标进入添加的trackingRect区域后,程序会调用mouseEntered方法,移除时调用mouseExited方法,于是只用在这2个方法中分别写上[aWindow orderFront:nil]和[aWindow orderOut:nil].就可实现。</p> <p>但是还要实现鼠标在区域中移动时窗口跟随移动,而trackingRect并不响应mouseMoved:, 为了更加方便的实现,可以用-(void)addTrackingArea:(NSTrackingArea *)trackingArea,同样是NSView的方法。 用 -(NSTrackingArea *)initWithRect:(NSRect)rect options:(NSTrackingAreaOptions)options owner:(id)owner userInfo:(NSDictionary *)userInfo来生成一个NSTrackingArea对象。 这里rect同样也是响应区域,设置为图片的frame.关键在于options了。NSTrackingAreaOptions是系统定义的全局常量,诸如NSTrackingMouseEnteredAndExited,NSTrackingMouseMoved,NSTrackingCursorUpdate,NSTrackingActiveWhenFirstResponder,NSTrackingActiveInActiveApp,NSTrackingAssumeInside 当需要多个options时候,用按位或符号 | 来连接。顾名思义,我们需要用到NSTrackingMouseEnteredAndExited,NSTrackingMouseMoved 于是我们的options就设置为NSTrackingMouseEnteredAndExited| NSTrackingMouseMoved。 然后在view中重载- (void)mouseEntered:(NSEvent *)theEvent -(void)mouseExited:(NSEvent *)theEvent -(void)mouseMoved:(NSEvent *)theEvent方法。 -(void)mouseMoved:(NSEvent *)theEvent { NSPoint aPoint = [NSEvent mouseLocation];</p> <p>} 便可以实现窗口跟随鼠标移动 最后值得一提的是,window的默认坐标系原点在左下,而需要让window左上角跟随鼠标,因此苹果提供了setFrameTopLeftPoint来设置window的左上角坐标,很方便。</p> lingyun tag:www.nibirutech.com,2008-10-27:68 2008-10-27T01:48:00Z 2008-11-14T03:49:41Z attributed String problem <p>在nambu的消息显示中,消息有着许多的特殊属性,例如在链接处,需要显示颜色为蓝色,鼠标变为handpointingcursor。但是呢这只是局部,又不能全部设置为这样,于是,就需要我们的NSAttributedString 或 NSMutableAttributedString. <p>NSMutableAttributedString 中含有这样的一个方法, – (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)aRange name 是指需要添加的属性的名字: NSString *NSFontAttributeName; NSString *NSParagraphStyleAttributeName; NSString *NSForegroundColorAttributeName;</p> ... <p>value 则是对应着不同的name,有着不同的value,比如: NSFontAttributeName-------Default Helvetica 12-point NSForegroundColorAttributeName-------blackColor</p> ... <p>range ,则是指我们需要添加属性的范围。 NSForegroundColorAttributeName, NSFontAttributeName, nil];</p> <pre><code>即黑色,Helvetica-Bold字体,13号大。</code></pre> <pre><code>于是,在range的帮助下,我们就可以添加我们所需要的属性。 需要注意的是NSMutableAttributedString 有addAttributes 和 setAttributes 这两个方法。add是指在原有的基础上,继续添加;set是指去掉原来,只有新增的属性。</code></pre> <pre><code>这样的随意改变属性,就有个潜在的问题了,如何准确的获得这个string的rect呢?不同的字体,不同的大小,会导致rect跟以前的有差距,这样的差距会让信息显示极其不正确。 - (NSRect)boundingRectWithSize:(NSSize)size options:(NSStringDrawingOptions)options size,可以指定信息显示的宽度或高度,options则是显示的要求。 这样返回的rect,则是我们想要的rect。</code></pre> <pre><code>例如在nambu的tableview里,它的每一行里,其实是个textfield,当resize tableview时,信息也会随着压缩,使得行数越来越多,那么这是,textfield则需要变换它的高度。 NSRect rect = [attributedMessageText boundingRectWithSize:NSMakeSize([textfield frame].size.width, 0) options:NSStringDrawingUsesLineFragmentOrigin];</code></pre> <pre><code>那么,rect.size.height 则是我们所需要的textfield的高度。</code></pre> </p> houzongming@nibirutech.com tag:www.nibirutech.com,2008-10-08:67 2008-10-08T09:06:00Z 2008-10-08T09:08:25Z setFrame问题 注意,不要把一个subview [subview setFrame:NSZeroRect]; 否则再设置新的frame时,会因为newwidth/0 以及newheight/0都没有意义,而变的无法resize这个subview的子view 这个问题,卡了我几乎一下午的时间。 xuzizhan tag:www.nibirutech.com,2008-09-23:66 2008-09-23T13:49:00Z 2008-09-24T00:20:20Z 关于NSCell的那些事儿 众所周知,任何一个成功的NSControl(见注释1)背后都有一个NSCell。因为多数功能都可以通过NSControl直接实现,因此NSCell一直是被人忽略的角色。今天就和大家一起走进NSCell的世界,简单谈谈NSCell的常用,实用用法。<br> <br> 大家打开itunes,mail,或者Finder,都会看到左边的树形列秒(NSOutlineView).每一行,即一个Cell中,既有图片,又有文字。并且整个outlineView只有一列,要实现这种效果,就只有靠操作Cell了。<br> 就以NSOutlineView作为例子。利用outlineView的<br> <br> - (<span>id</span>) outlineView: (<span>NSOutlineView</span> *) aOutlineView objectValueForTableColumn: (<span>NSTableColumn</span> *) tableColumn<br> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; byItem: (<span>id</span>) item<br> 这个delegate方法可以根据不同节点(item)显示不同的值,当然这个值不能是图片。因此我们要实现左边的图片就只能依靠cell了。<br> 因为apple没有提供一个text and image的NSCell子类,因此我们需要自己建立这样一个类,建立一个新类,取名NBImageAndTextCell(恩,这个名字很规范,很低调又很华丽),并继承至NSTextFieldCell。怎么把这个类和我们的NSControl(即outlineView)联系起来呢,当然NSControl有个叫setCell的方法可以做,但是既然我们有IB(注释2),于是就在IB里设置即可,方便切安全。打开IB选中你的outlineView,再选中其中的column,然后再选中cell。这过程可以通过连续的 command+ctrl+ 方向键下 完成。在cell的inspector窗口(command+shift+I 打开)的identity中将 Class identity改成“NBImageAndTextCell"回车即可,现在我们的cell类就属于这个outlineView了<br> <br> <br> 在outlineView的delegate类中使用delegate方法<br> <br> - (<span>void</span>)outlineView:(<span>NSOutlineView</span> *)ov willDisplayCell:(NSCell *)cell forTableColumn:(<span>NSTableColumn</span> *)tableColumn item:(<span>id</span>)item<br> {<br> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span>NBImageAndTextCell</span> *imageAndTextCell = (<span>NBImageAndTextCell</span> *)cell;<br> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; [imageAndTextCell <span>setImage</span>:[<span>item</span> itemImage]];<br> <br> }<br> <br> 顾名思义,"willDisplayCell"就是在即将要显示cell又还没显示cell的那个时候,我们在这个时候把我们要显示的图片通过 setImage 方法 传给cell。 这个image一般就是node的某个属性,这样就可以根据不同的node显示不同的图片。比如Finder中文件夹node的图片就是蓝色小文件夹图<br> <br> 在 NBImageAndTextCell中声明一个 NSImage *image 和一个- (void)setImage:(NSImage *)anImage;方法。这个setImage方法就是我们上面用到的。<br> - (<span>void</span>)setImage:(<span>NSImage</span> *)anImage <br> {<br> &nbsp;&nbsp;&nbsp; if (anImage != <span>image</span>) <br> &nbsp;&nbsp;&nbsp; {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<span>image</span> <span>release</span>];<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>image</span> = [anImage <span>retain</span>];<br> &nbsp;&nbsp;&nbsp; }<br> }<br> <br> 接下来就是如何在cell中画上这个image的方法了。这是核心所在<br> - (<span>void</span>)drawWithFrame:(<span>NSRect</span>)cellFrame inView:(<span>NSView</span> *)controlView <br> {<br> &nbsp;&nbsp;&nbsp; if (<span>image</span> != nil)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br> &nbsp;&nbsp;&nbsp; {<br> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; [<span>image</span> <span>setSize</span>:(<span>NSSize</span>){16,16}];<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>NSRect</span>&nbsp;&nbsp;&nbsp; imageFrame;<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>NSSize</span> imageSize = [<span>image</span> <span>size</span>];<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>NSDivideRect</span>(cellFrame, &amp;imageFrame, &amp;cellFrame,imageSize.width+5, NSMinXEdge);<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ([<span>self</span> <span>drawsBackground</span>]) {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [[<span>self</span> <span>backgroundColor</span>] <span>set</span>];<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>NSRectFill</span>(imageFrame);<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageFrame.<span>origin</span>.x += 3;<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageFrame.<span>size</span> = imageSize;<br> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<span>image</span> <span>compositeToPoint</span>:imageFrame.<span>origin</span> <span>operation</span>:NSCompositeSourceOver];<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;&nbsp;&nbsp; [<span>super</span> <span>drawInteriorWithFrame</span>:cellFrame <span>inView</span>:controlView];<br> }<br> <br> 这个draWithFrame继承至NSTextFieldCell,通过这个方法来画上Cell。参数cellFrame就是这个Cell在control(即outlineView)每一行的frame,因此你的outlineView的一行多高多宽,位置在哪儿就是这个cellFrame了。<br> <br> 看第一句 if (image != nil),这个image是在outlineView的delegate方法里通过setImage传进来的,因此如果我们想要某一个节点没有任何图片的话,就传个nil进来,于是就不会执行括号内画图的方法了。<br> 接下来就是核心<br> <span>NSDivideRect</span>(cellFrame, &amp;imageFrame, &amp;cellFrame,imageSize.width+5, NSMinXEdge);<br> <br> 这个NSDivideRect就是一个切割cell的方法,里面的4个参数当然就是决定你怎么切的。<br> 简单来说,<br> 第一个参数是:在哪个里面切&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里是cellFrame,意思是我们在整个cell内切 <br> 第二个:切出来的东西放哪儿&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里是imageFrame一个出参,就是切出来的范围一个Frame 赋给imageFrame<br> 第三个:切剩下的放哪儿&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个出参cellFrame,切后的放入cellFrame,也就说之后的cellFrame就自由我们切剩下的那么大了<br> 第四个:切的边界举例cell边界多远&nbsp;&nbsp; 距离边界imageSize.width+5这么远<br> 第五个:从cell的哪边切&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSMinXEdge是靠cell左边,NSMaxXEdge是右边<br> <br> 于是现在我们的imageFrame就是我们要放图片的地方和大小,cellFrame就是剩下的cell大小和位置了<br> <br> 经过小的调整 imageFrame.origin.x += 3;&nbsp; imageFrame.size = imageSize;&nbsp; 后,就调用[image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver];<br> 在这个范围内画上我们的image即可<br> <br> 最后千万别忘了[super drawInteriorWithFrame:cellFrame inView:controlView];没有这个,cell都不会被draw上,自然我们切莱切去,画来画去的东西都不能显示了<br> 这个也是我们要继承NSTextFieldCell的原因,就是为了用这个方法。<br> <br> ok,大功告成,你的outlineView可以图文并茂了,以后还想切一块出来显示点其他什么图啊什么的,就随便你了。<br> <br> <br> <br> <br> 小白注释1:NSControl 是NSView子类,如NSTextField,NSButton,NSTableView 等<br> 小白注释2:IB=Interface Builder<br> houzongming@nibirutech.com tag:www.nibirutech.com,2008-09-23:65 2008-09-23T13:28:00Z 2008-09-23T14:21:18Z Source List View (Mail-like) controls <pre> 大家注意到Mail.app得左下角得,那个add/remove/resize控件了嘛? <img src="http://bluerope.org/story-media/source-view-controls/full-bar.png"> 不少程序都有类似功能,最近也在Nambu.app上作了这个工作。 第一步,加上Source list view controls 把NSSplitView放到window上,加上两个button,选择为Square button,然后分别设置image为NSAddTemplate和NSRemoveTemplate. 第二步,设置这两个button得autoresize类型为保持左、下即可 <img src="http://bluerope.org/story-media/source-view-controls/button-autoresizing.png"> 第三步,这两个按钮占用了很少得空间,剩余部分,我们用个custom view来填充,这个view会处理我们第一步添加得splite view得 resize工作,并且会画三条竖线来提示用户那个区域可以作为拖动手柄。添加了custom view后,subclass这个custom view,比如 类名为NBResizeGripView. 这个custom view因为一直要保持与split view左子view同宽,因此autoresize设置为保持左、下、右, 自动放缩宽度。 <img src="http://bluerope.org/story-media/source-view-controls/grip-autoresizing.png"> 到这里,我们已经画出了add/remove/resize三个控件了,并且你拉动split view的分界线,也不会出现resize问题 第四步,弄一个类,来做为split view的delegate,不妨叫做NBMainWindowController 第五步,NBMainWindowController加个指向那个resize控件的outlet IBOutlet NBResizeGripView *splitViewResizer 第六步,告诉split view,你的add/remove/resize控件的尾巴部分(画着三个竖线那块儿)也可以控制split view的大小变化。代码 如下,你要写到你的那个split view的delegate类里。 // NBMainWindow.m // 除了那个split view分界线外,再设置一个额外的Divider,也就是那个画了三个竖线的区域 -(NSRect)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(NSInteger)dividerIndex { NSRect resizeBounds = [splitViewResizer bounds]; // 得到画了三个竖线的区域的bounds resizeBounds.origin.x = resizeBounds.size.width - 15.0; resizeBounds.size.width = 15.0; return [splitViewResizer convertRect:resizeBounds toView:splitView]; } 第七步,就是要让我们的NBResizeGripView能够在右边画三条灰色线了. // NBResizeGripView.m - (void)drawRect:(NSRect)rect { // fill with gradient NSRect bounds = self.bounds; NSColor* color1 = [NSColor colorWithCalibratedRed:249.0/255 green:249.0/255 blue:249.0/255 alpha:1.0]; NSColor* color2 = [NSColor colorWithCalibratedRed:223.0/255 green:223.0/255 blue:223.0/255 alpha:1.0]; NSColor* color3 = [NSColor colorWithCalibratedRed:243.0/255 green:243.0/255 blue:243.0/255 alpha:1.0]; NSGradient* gradient = [[[NSGradient alloc] initWithColorsAndLocations: color1, 0.0, color2, 18.0/21.0, color3, 1.0, nil] autorelease]; [gradient drawInRect:bounds angle:90.0]; // draw the top border NSColor* borderColor = [NSColor grayColor:131.0/255.0]; [borderColor setStroke]; CGFloat y = bounds.size.height; CGFloat x = bounds.size.width; [NSBezierPath strokeLineFromPoint:NSMakePoint(0.0, y-0.5) toPoint:NSMakePoint(x, y-0.5)]; // draw the grip lines NSColor* gripColor = [NSColor grayColor:(66.0/255.0)]; [gripColor setStroke]; NSInteger lineIndex; CGFloat lineXPos = x - 4.5; for (lineIndex = 0; lineIndex &lt; 3; ++lineIndex) { [NSBezierPath strokeLineFromPoint:NSMakePoint(lineXPos, 5.5) toPoint:NSMakePoint(lineXPos, y-5.5)]; lineXPos -= 3.0; } } 第八步,完了,build and go. </pre> lingyun tag:www.nibirutech.com,2008-09-23:64 2008-09-23T11:10:00Z 2008-09-24T00:35:29Z WebView 使用体会 <p>最早接触的,姑且算是这个webkit了吧。 webkit 内就只有webview这一个控件。它也是safari的核心。换句说呢,用了webview,就相当于有一个小型的safari了。</p> <p>1.引用webkit 首先要导入#import “webkit/webkit.h”</p> <p>这样就可以声明webview了。</p> <p>仅仅这些,是不够的。如果运行这样一个程序,会发现有图标在下面一跳一跳的,但是没有界面出来。这是因为需要我们在frameworks里添加webkit.framework。</p> <p>如此两部分,才算是真正的能用webview了。</p> <p>2.简易浏览器 IB 给我们提供了一些非常强大的功能,能使得程序员减少许多的代码。 如,一个NSTextField 和 WebView 不用写代码,仅仅在interface builder 就能完成一个简易的浏览器. 按住ctrl,从NSTextField 拖向WebView,选中takeStringURLFrom</p> <p>就这样,我们的程序完成了,commad+r 运行该界面 在textfield里输入网址,回车,网页便可以浏览了。</p> <p>3.代码编写 首先这里要提到- (void)awakeFromNib</p> <p>该函数是专门用于处理ib不能完成的事件,这里最好是只放关于ib的代码。它的执行顺序是在- (id)init 之后。</p> <p>- (void)awakeFromNib {</p> <p>NSString *resourcesPath = [[NSBundle mainBundle] resourcePath]; NSString *htmlPath = [resourcesPath stringByAppendingString:@”/index.html”];</p> <p>}</p> <p>在这里,我们设置了webview的delegate,还设置了webview启动时,自动载入index.html这个文件,在webview内显示。 如果时想打开其他的网页的花,则[]NSURL URLWithString:@”www.nibirutetch.com”]。</p> <p>对于启动以后的操作,则可以在webview的delegate方法 – (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame 里面操作。如名字所示,是在webview load 完成之后执行里面的操作。</p>