2011年11月26日 星期六

做個腦還是做隻手?

大概在兩年前,有一位朋友說:「說實在話,錢灑出去寫程式的人隨便找」
大概在一年前,有一位大人說:「工程師要有生涯規劃,不可能一直寫程式」
大概在半年前,有一位資深說:「我們需要知道可以做到什麼程度,不用會做到」

所以,感覺上實際由自己的手從無到有,似乎是一個遲早要擺脫的模式?
更有另一種的感覺是,越早脫離似乎越成功?
那麼升級成思考的腦,脫離手的日子似乎是大家的願望?

每個人都想要當腦,都想要當PM,都想要當管理職,都想要"規劃",
那?誰要來"實做"?誰要當產出者?誰要來做事?誰要當手?
------------------------------------------------------
手與腦的貴賤

普遍存在社會上的認知,腦總是比手要高階,實際將專案完成的人總是比說話的沉默,管理、規劃及設計的確各個都是高深的學問,但實際將其完成的各項技術不也是需要認真的鑽研?

有另一句常常聽到的話「我老了,新的技術追不上了,但觀念讓我用一輩子」,半年前有一位大人問我「這樣的東西需要怎樣可以學會?」,我腦中浮現的不是語言的名稱、不是技術的辭彙,更不是什麼鬼架構,而是一顆砰砰跳的心,其實當下我一時語塞,只說「就想學,然後花時間就可以了」。

所以,老了真的是不去看些新東西的藉口嗎?
還在腦海裡面的觀念,真的可以用一輩子嗎?

或許三、五年,或十年後我還在這樣的產業,也會說類似的話,但其實自己心中明白,因為不想去經歷實作的辛苦,所以希望用所謂的'管理技術'來完成,可是我們竟然在心中默默的覺得自己比這些人更加高級與尊貴。
------------------------------------------------
手與腦的組合

這兩個簡單的角色,不外乎幾種配對:

1. 聰穎的腦+靈巧的手
一種簡直無敵的組合,許多高官都認為他們的人力分配就是這種組合,PM有無比的經驗與判斷能力,而工程師有著足以應付亂七八糟要求的能力,如果有這種夢幻組合,這將是一場愉快的工作體驗。

2. 有經驗的腦+拙劣的手
這是一種想起來應該很普遍的組合,由充滿經驗的資深人員,帶領並傳承相關的經驗與知識給年輕一輩,建立一種正向的工作環境,感覺上是一個很棒的模式,但通常跟下一種模式只是一線之隔。

3. 不懂的腦+懂得比腦多的手
這是第二種模式的變形,普遍發生在變化快速的產業,通常手上掌握的技術,會在兩三年內變成讓人皺眉的寂靜,而根紮不深的人,更容易變成一個自以為是的腦。

4. 停止的腦+無力的手
「悲劇」

5. 手腦合一
這是一種很奇妙的演進,有些人當腦,資源會自然而然的安排妥適,甚至三頭六臂;但有些人則必須手腦合一,手即是腦,腦即是手。
--------------------------------------------------
你為什麼是隻手?

1. 因為你會殺雞,剛進研究室的時候,指導教授跟我們講了一個小故事「殺雞的故事」,內容是:

一群人去當兵,有一天突然要殺一隻活跳跳的雞,排長問「誰會殺雞?」,一片靜默.......
排長眼睛一瞄,指著一個方向說「就你了」,
那方向的人狐疑,不知道是誰被指到了,
一位舉起手指著自己說「是我嗎?」
排長:「對!就是你!今天你殺雞」

就在一陣雞飛狗跳後,呆子A殺了人生的第一隻雞,往後雞來了誰殺?呆子A就這樣殺了一整個軍旅生涯的雞。

2. 因為你應徵手

打開104會看到有些公司徵PM,實際去談有時會發現,這一個職缺好像不用會什麼實質上的技術,管理、規劃似乎才是重點,所以很快的一個不懂的腦產生了。

曾經我跟一位在當PM的學姊聊天:
「學姊,你當PM,那有需要先會寫嗎?」
「我不會,但是我覺得我是一個好PM。」
「怎麼說勒?」
「因為我不會亂答應事情,不確定的功能我會先回來問工程師做不做得到」

會先徵求工程師的意見,已經變成一個好PM的判斷標準?我的以為是,事情的難易不應該"光由"實做的人來判斷,如果腦連這些事情都不能掌握,要怎麼相信規劃的東西可以執行?最後又是金玉其外..。

也有一種腦,認為別人的事情都相當的容易,
他們會說「就我所知,這東西沒什麼了不起的」
「我知道這對你很簡單」
「我處理好最困難的部分了,你就照著這規劃實作吧」
其實不管是實際很麻煩、困難的內容,被說的簡單至極,或是輕鬆簡單的內容,被說的難如登天,其實都是一個很詭異的事情。
---------------------------------------------------
轉型做腦,脫離技術文件的泥沼簡單嗎?

大人們會告訴你「工程師要有生涯規劃」,講起來好像要做哪方面的人由自己決定?,所以我們應該在某一個時刻致力於轉型,從實作的手慢慢往腦來前進,但其實在實際情況上也不全是這樣。

如果你不願意實作,那有兩條路:
1. 死的很慘,做什麼都不行。
2. 直接變腦,大人自然會分配靈巧的手給你,過的相對快樂。

如果你挺愛研究東西,也有兩條路:
1. 做什麼都行,無窮無盡的做到死。
2. 說「我老了,新的技術追不上了」,燒掉文件,殺掉開發工具加上沉默。

不管是哪一種,共通的特性是遠離那些技術,停止花時間在鑽研小東西上面,因為我們要做偉大的'管理';所以組織充滿「不懂的腦+懂得比腦多的手」,倚老賣老、老人不做事、說的比做的簡單,這樣類似的言語將會充滿整個辦公空間,而辦公室留晩的總是那些人。

所以就實質面,如果我是上面的人會怎麼希望?
正面的想:大家教學相長,不斷傳承,正向的成長。
負面的想:腦更外向的變成嘴,手安靜的做不做思考。

所以,原本認為總不能什麼都不懂,什麼都不自己實作的人,反而陷在於泥沼之中。
------------------------------------------------
「我們需要知道可以做到什麼程度,不用會做到」

對於這句話,腦的角度看,我認為不夠;手的角度看,我認為很幹,那麼,這句話應該是誰的定義?應該是「嘴」。
那腦該怎麼樣?「需要知道怎麼樣可以做到,不用完全會細節,但也不應完全不會」
那手呢?「需要找出實做的方法,掌握細節的項目」

所以看看你家的嘴、你家的腦、你家的手,
嘴有確切了解可以做到的程度嗎?
腦有實際明瞭怎麼樣可以做到嗎?
手有認真努力將細節做的更好嗎?
-------------------------------------------------
做個腦還是做隻手?

如果當腦,我還是希望自己仍然是一雙堪用的手,
如果當手,我還是希望自己擁有那一顆遠眺的腦。

根要紮,也需要新知的滋養。

2011年11月22日 星期二

Android錯誤排除_Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it

使用SQLite的時候,很有可能出現這個訊息:

『Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it』

意思是沒辦法讀取CursorWindow這個Cursor的第0行,請確定訪問Cursor之前,已經完成初始化的動作。

這時候,我們會一直去看有沒有初始化,然後找不到錯誤;

這錯誤有可能是因為去讀取沒有定義的欄位名稱。

例:
String dis_Explore = cursor.getString(cursor.getColumnIndex("dis_Explore"));
但是欄位中並沒有dis_Explore這個欄位。

這樣的錯誤很有可能發生在資料庫定義改變或是增刪欄位時。

2011年11月21日 星期一

要MP還是HP?

還是學生的時候,常在考試需要熬夜的時候,跟朋友說:
「你覺得hp高好?還是mp高比較高?」
通常不會有人知道我在說什麼...
「如果你現在睡覺,明天考試的時候你就擁有很高的血量,可以撐很久,但是都不會寫,可能撐完整場也打不到考卷的60%」
「但是,如果你現在熬夜看書,你就擁有很高的mp,可以用魔法打考卷,但是你可能撐不久,可能發兩發25就倒了」
「所以到底該早睡覺?還是該熬夜看書勒?」

總之,上面的對話,可以非常容易得到一個結論:「要在期限前努力的看書,然後在考試前充分的休息」,說起來這是一個廢話,但是廢話通常讓人做不到。

-------------------------------------------------------------
對應到生涯上,我們為了無形的考試,
「工作三年有百萬存款了嗎?」
「30歲,要結婚了嗎?」
「35歲,5子有幾個了呀?」
「40歲,是不是做了個官了?」
總之,有很多很多自己的希望與別人的期待。

那...如果我想要準備,好好考這些試,我到底該準備什麼?





就在我坐著想了10分鐘,應該要準備『更多的運氣』。

iOS學習_App生命週期

寫App的第一件事情就是生命週期,以一個簡單的流程圖記載一下。

- didFinishLaunchingWithOptions:撰寫程式初始化內容。
- applicationWillResignActive:撰寫資料儲存的內容。
- applicationDidBecomeActive:撰寫資料回復的內容。

iOS程式進到背景後,僅有限的網路功能(串流、上傳等)、GPS、撥放音樂,可以繼續進行,其他自行開發的程式都將會停止。

iOS學習_Objective-C超基本亂記2

第三堂課就在磅礡大雨中進行,為了往後可以複習一下觀念,還是補一下筆記。
------------------------------------------------------------

1.繼承

#import "要繼承的類別.h"

@interface className:要繼承的類別 {
}
說明:
- 繼承用":"
- 在繼承前記得先import

2. 覆寫
-進行覆寫時,不需要增加overwrite等關鍵字,取名相同則直接進行覆寫。

3. 使用父層方法
- 要使用父層方法,與許多程式語法相同,使用super。

4. 使用自己
- 要取得自己,使用self

5. 類別初始化
- Objective-c並沒有建構函式,若是要進行初始化,可自行以一般函式的寫法來達成。

h檔定義
- (ola *)initWith:(NSString *)nameValue age:(CGFloat)ageValue;

m檔實做
- (ola *)initWith:(NSString *)nameValue age:(CGFloat)ageValue;
{
self = [super init];
if (self)
{
self.name = NameValue;
self.age = ageValue;
}
return self;
}
說明:
- 實做時,先呼叫父層的init方法,若繼承於NSObject,則代表呼叫[NSObject init],成功建立實體後,塞入初始化數值,最後回傳自己。(也就是例中ola類別)

6. id動態型別
- 有時候一個Array裡面可能有多種類別,這時可以利用動態型別來承接。

if ([temp isMemberOfClass:[ola class]])
判斷temp是否為ola型別。(無多型概念)


if ([temp isKindOfClass:[ola class]])
判斷temp是否為ola型別的相關種類(有多型概念)


if ([temp respondsToSelector:@selector[initWith]])
判斷temp是否有initWith的方法

7. try的使用

@try
{
}
@catch (NSException *e)
{
NSLog(@"%@,%@" , [e name], [e reason]);
}
@finally
{
//對錯都執行的部分
}
說明:
- [e name]取得錯誤名稱
- [e reason]取得錯誤原因

8. 自行拋出錯誤

[NSException exceptionWithName:@"錯誤名稱" reason:@"錯誤原因" user:nil];


9. 靜態屬性
- Objective-c沒有靜態屬性,所以是以C++的語法,寫在import之後,@interface之前。

static int count;

使用時,必須要先告知編譯器要使用外部的屬性

extern int count;
count++;


10. 靜態方法
於h檔與m檔中,以+號開頭

11. 補充類別(Category)
h檔

#import "ola.h"

@interface ola (olaOps)
- (void) size;
@end
說明:
- #import "ola.h":import要被補充的內容
- @interface ola (olaOps):olaOps類別名稱,ola 未被補充的類別

m檔

#import "olaOps.h"

@implementation ola(olaOps)

- (void) size
{
NSLog(@"17");
}

@end
說明:
- 實際使用時,類別與補充類別都要import,Xcode才會有提示可以使用。
- 可以利用該方式,補充NSString,NSObject等基本類別。

12. 通訊協定(Protocol)
- 類似其他語言的interface的概念。
- 使用時使用大括號,多個Protocol用逗點隔開。
- 可利用@required與@optional來指定是否一定要實做。

13. 前處理(define)
- 告知編譯器要置換的文字(replace的概念)

#define ola ABCDEFG
說明:
- 程式中看到ola的字樣,會先置換成ABCDEFG,編譯器才會編譯。
- 後面沒有";",中間沒有"="

-------------------------------------------------------

對沒碰過的人來說,內容真的很多。

2011年11月14日 星期一

asp.net小抄_抓取使用者IP

有時候會被要求,後端的管理系統必須要記載使用者的IP。

原本是使用:
string UserIP = HttpContext.Current.Request.UserHostAddress;
但發現無法抓取到源頭的IP。

後來改為:

string UserIP = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

2011年11月11日 星期五

iOS學習_基本的記憶體管理

很快的iOS課程來到第二堂,繼第一堂課後又加深了一些感想:

1. Mac電腦需求越來越急迫。
2. 如果要一邊寫iOS、一邊寫網站、一邊寫Android一定會很痛苦。
---------------------------------------------------------
第二堂課最重要的是『記憶體管理』
1. 若是有建立記憶體空間,便需要進行記憶體管理

NSString *Value = [[NSString alloc] initWithFormat:@"%@",@"ola"];
說明:
- [NSString alloc]為宣告記憶體空間。
- initWithFormat為初始化的方法。

2. 可以藉由Reference Counting來看出被引用的次數

NSString *Value = [[NSString alloc] initWithFormat:@"%@",@"ola"];
[Value retainCount];
說明:
- retainCount方法可以查詢出該物件記憶體空間被引用的次數,若Counting為零,則該空間會被回收。

3. 不良管理將會造成dangling(要使用、但已被回收)與memory leak(不需要了,但沒回收)的問題。
- dangling錯誤訊息為:EXC_BAD_ACCESS,一發生即會造成程式當機。
- memory leak不會立刻造成程式錯誤,但有可能慢慢將手機記憶體耗光而當掉。

4. 若是已經宣告的記憶體空間,被別的指標引用到,則可以手動將引用次數加一(Reference Counting+1),來確保該空間不會被回收

[Value retain];


5. release方法並不是"直接"將該空間釋放,而是將Reference Counting -1

[Value release];


6. 常數、沒有alloc的型態不需要進行記憶體管理,iOS會自行處理。

7. 標準有管理記憶體的類別寫法
h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
}
- (void)setName:(NSString *)vname;
- (NSString *)name;
@end


m檔

#import "OlaClass.h"

@implementation OlaClass

- (void)setName:(NSString *)vname;
{
if (name)
{
[name release];
}
name = vanem;
[name retain];
}

- (NSString *)name;
{
return name;
}

- (void)dealloc
{
[name release];
[super dealloc]
}

@end
說明:
- 寫setter時,若是沒有進行retain,則會造成指定的值釋放記憶體後,產生dangling pointer的問題,所以必須retain,保留該空間。但若都不進行release則會造成Reference Counting不斷加上去,最後產生memory leak問題,所以應在每一次setter的時候先判斷是否存在,進行release。

- 經過上述處理,複寫dealloc方法,並執行release,來將加一的Reference Counting減回去。

8. 若是用合成方法,則可以更快速的完成上述類別
h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
}
@property NSString *name;

@end


m檔

#import "OlaClass.h"

@implementation OlaClass
@synthesize (readwrite, retain ,nonatomic) name;

- (void)dealloc
{
[name release];
[super dealloc]
}

@end
說明:
- 合成方法:h檔加property 、m檔加synthesize
- 讀寫:readwrite(有getter、有setter)
- 讀寫:readonly(只有getter)
- Setter設定:assign(不做記憶體操作)
- Setter設定:retain(做retain並且release)
- Setter設定:copy(做copy並且release)
- 單一性:atomic(考慮多執行緒)
- 單一性:nonatomic(無考慮多執行緒)←通常用這個
- 用合成方法,還是要複寫dealloc

2011年11月9日 星期三

iOS學習_Objective-C超基本亂記

經過這些日子一陣混亂以後,已經註定要接觸apple系列,就在沒有不喜歡但也沒有像當初Android一樣超熱血的情況下,開始了第一堂課程,結束後有幾個感想:
1. 應該趕快買一台MacBook Pro。
2. 讓很多人來學Objective-C的apple真的很厲害。
3. 要比Android花更多更多的時間,才能到'自以為會一點'的心情。
4. 如果跟之前一樣,一直在各種語言(專案)轉換,痛苦程度倍增。

筆記:
1. 變數宣告
int age = 18;
(C裡面的int宣告,Objective-C撰寫常會與c語言混合撰寫)

NSString *name = @"ola";
(NSString為Objective-C的型態,前有NS前置詞皆為Objective-C內容,*號為c語言指標概念。因NSString為物件,物件前必須加@符號)

CGFloat size = 18.5
(CGFloat為小數宣告)

NSDate *today = [NSDate date]
(NSDate為日期物件,取得今日日期的方法為[NSDate date]→執行NSDate內的date方法,取得今天日期)

2. log的用法
NSLog(NSString)
例:NSLog(@"今天%@,姓名:%@,年紀:%d",today,name,age);
NSString要顯示時,可先用格式符號代替,再於後面對應相關變數。
%@:物件
%d:整數
%f:浮點

3. 類別相關事項
一個分為兩種檔案:.h及.m
.h:標頭檔,用以定義屬性、方法與繼承何者
.m:實作的內容

.h檔

#interface

@interface OlaClass:NSObject{
NSString *name;
CGFloat age;
}

- (NSString *)PrintName;
- (void)setName:(NSString *)vname;
- (NSString *)name;
- (void)setAge:(CGFloat)vage;
- (CGFloat)age;
@end

說明:
1. 宣告name與age兩個屬性(不需要宣告public等內容,因為所有的屬性都不能直接讀取,要存取屬性都必須藉由getter與setter)
2. {}中間為宣告屬性的地方,}與@end中間為宣告方法的地方。
3. - (NSString *)PrintName; →宣告PrintName實體方法,回傳NSString,'-'為實體方法。+為靜態方法。
4. - (void)setName:(NSString *)vname; →宣告vname實體方法,沒有傳回值,但傳入一個NSString。


.m檔

#import "OlaClass.h"

@implementation OlaClass

- (NSString *)PrintName
{
return [NSString stringwithFormat:@"name: %@,age:%d",name,age];
}

- (void)setName:(NSString *)vname;
{
name = vanem;
}

- (NSString *)name;
{
return name;
}

- (void)setAge:(CGFloat)vage;
{
age = vage;
}

- (CGFloat)age;
{
return age;
}

@end

說明:
1. @implementation與@end中間為實做方法的地方。
2. 在寫Set時,必須避開原本的屬性名稱,無法直接命名相同,而用類似this.name的方式表示指定不同的變數。

4. 方法使用
[name length]:使用name的length方法。
[ola setname:@"ola"]:使用ola的setname方法,並傳入NSString。

5. 其他
*每一版的Xcode都會有蠻大幅度的改變,升級時要注意。
*模擬器模擬兩指操作:option+滑鼠
*模擬器模擬螢幕旋轉:command+方向鍵
*寫程式的提示鍵與取消提示都是esc鍵
*didFinishLaunchingWithOptions為啟動時執行的方法

加油加油~~~~凹嗚~~~~~

2011年11月6日 星期日

Android學習_Spinner改變字型大小(TextSize)

在Android2.X版時,Spinner會跳出一個類似遮罩的選單,方便使用者點選,這時候有些人會覺得該選單字體太大,會希望可以做一些改變。

但到了Android3.X版時,Spinner的預設模式卻變成像網頁的下拉式選單一樣,對於手指較大的人在操作上卻顯得有些不太方便,所以反而會希望將該字體變大。

總之,不管是變大變小,Spinner改變字體時有兩個部分:

第一部分為顯示選單值的地方,第二部分則是選單的項目。

改變方式:
1. 準備一個TextView為Base的XML檔案

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
style="?android:attr/spinnerDropDownItemStyle"
android:textSize="25sp" />


2. 指定spinner樣式時,使用上述XML檔
A. 改變第一部分字體:(big_spinner_dropdown_item為上述檔案)

SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, R.layout.big_spinner_dropdown_item,cursor, new String[] { PutFieldName }, new int[] {android.R.id.text1});


B. 改變第二部分字體:

adapter.setDropDownViewResource(R.layout.big_spinner_dropdown_item);


所以若是剛好用cursor放入spinner內,又希望兩部分字體都變大:

if (cursor.getCount()>0)
{
cursor.moveToFirst();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, R.layout.big_spinner_dropdown_item,cursor, new String[] { PutFieldName }, new int[] {android.R.id.text1});
adapter.setDropDownViewResource(R.layout.big_spinner_dropdown_item);
spinner.setAdapter(adapter);
}