呃...

其實不會有什麼精深的東西,也就只是把我學習過程記錄一下...

2014年9月8日 星期一

[問題] APNS通知 (My answer for question in MacDev)

    這個問題不長就不簡述了,最後倒是有不小心稍稍念了這位發問者 XD 我是覺得念得有理,畢竟APN不是什麼冷門問題,網路上一堆中文資源,還有很貼心的文章會解釋APNs原理,沒這麼難姑狗吧!?


 作者  flamelad (flamelad)                                       看板  MacDev
 標題  Re: [問題] APNS通知
 時間  Mon Aug 25 00:45:04 2014
───────────────────────────────────────

※ 引述《haves560 (新鮮人)》之銘言:
: 我是用UILocalNotification接收到APNS訊息後
: 顯示在app上面,可是遇到個問題,我把程式關閉後沒辦法push
: 請教一下 各位在做APNS push都是用什麼方式讓程式關閉後還能接收的到?

    問題描述有點不清楚,我先假設你的情況有兩種:1. 希望程式退到背景後,還可以收到APN 後讓App執行某些動作。 2. 程式完全關閉,不在foreground or background 執行,希望Device能收到APN後,讓App執行某些動作。

    假設1:
    若如1 的假設,則你必須了解UIApplication State。確切來說,你應該要知道什麼是背景處理,當然你若希望在App的其他階段執行你的動作,UIApplication State will help you more.
    另外下面這連結的文章,很清楚的說明iOS7在背景處理的狀況。
    http://onevcat.com/2013/08/ios7-background-multitask/

    假設2:
    若如2的假設,則情況與1不同,簡單來說,當App在not running狀態下時,Device收到APN後,iOS不會去替使用者執行App以及讓App執行某些動作,你可能需要多了解...呃,下段說明。

    可以看得出你在這部分所遇到的問題是你不太熟悉相關議題,因此建議你需要好好了解一下APNs、App lifecycle、前/背景處理差異、UIApplication State以及各種Notification的使用時機與用途,相信會對你在這邊遇到的問題有很大的幫助。

    最後算是我雞婆murmur,還請忍耐一下,有任何讓你感到不舒服的地方請多見諒:)
 這部分算是挺熱門的議題,相比其他問題,網路上姑狗一下關鍵字有很多相關的繁體資源以及衍伸議題,未來希望可以先稍微姑狗一下,我始終相信除了來自他人幫助以外,慢慢培養獨立解決問題的能力以及享受解決問題後所帶來的成就感,這種良好的自我循環,才是成就一個好的開發者的最重要因素。我也剛踏在這路上,共勉之 :)

[問題] UINavigationController and Camera (My answer for question in MacDev)

    發問者沒有很清楚描述問題,所以假設問題是想詢問使用UIImagePickerController時,NavigationBar消失了以及回不到rootView。


 作者  flamelad (flamelad)                                       看板  MacDev
 標題  Re: [問題] UINavigationController and Camera
 時間  Sun Aug 24 00:39:08 2014
───────────────────────────────────────

※ 引述《stevenlin08 (stevens)》之銘言:
: hi all,
: 請問,若第一個頁面是TableViewController,而我將會導航*1到
: 另一個viewController(B),並開啓照相。
: 此時,會使原本的NavigationBar 消失。
: 導致我沒法退出到rootView .
: *1:
: 用pushViewController: view(B)推入控制器。
: 目前,我認為有一列方式,但還是沒法成功。
: 1.要如何讓NavigationBar 出現?
: 2.使用popViewControllerAnimated:並無法使畫面回覆

    這部分同樣並非很熟悉,就直接回應於板上給其他版友指正:) 你的文章中缺少一些細
節,另外我也並不太懂你的*1的意思,因此我先假設幾件事情:
        a. 是一般tableView中的cell點了會導向另一個ViewController。
        b. 在另一個ViewController中的somewhere invoke UIImagePickerController。
        c. 照著一般UIIPC最簡單的範例去實作Camera功能的呼叫。

    問題1:
        若是因為要解決問題2才有問題1,那這應該沒問題了。若不是,可以使用
    isNavigationBarHidden確認是否NavigationBar在invoke Camera時被設定為hidden。
    問題2:
        請在imagePickerControllerDidCancel中,先dismiss掉正在使用的UIIPC,再
    pop回rootView即可。
    [self dismissViewControllerAnimated:YES completion:^{}];
    [self.navigationController popToRootViewControllerAnimated:YES];

[問題] 請教AVAudioPlayer小問題 (My answer for question in MacDev)

    原問題是詢問AudioToolBox,不過我沒使用過AVFundation相關類別,所以在這個問題的往來回信中,我是使用AVAudioPlayer進行測試。
    問題簡述:有關AVFundation中的AVAudioPlayer在連續播放音效時,似乎在音效之間會有些許間隔的存在問題。


 作者  flamelad (flamelad)
 標題  Re: F大您好 請教AudioToolBox小問題
 時間  Sat Aug 16 21:32:26 2014
───────────────────────────────────────

    Hello, 我先把上一封信打太快造成些謬誤的地方修正一下:p
    在上一封信中我闡述了alloc放在Button中所造成的問題,這邊我並不是想說延遲,而是重新又被assign一次,所以才會有第二次點擊就會中斷第一次的播放,並非延遲,因此你將alloc放在其他地方使用,就會發現第一次點擊後的音效並不會被中斷。

    回到你的問題,我重新思考一次,想要seamless播放音效,又不希望會重疊,我想一想,也許只是一個很簡單的問題。這邊真的挺不好意思,中午真的太趕沒好好仔細review寄給你的信,因為盲點在於AVAudioPlayer會有延遲感並非是program運作的問題,而是沒有對症下藥。你可以試試使用AVAudioPlayer,但是button 的 Sent Event改為TouchDown,而不是TouchUpInside,我想應該就能解決問題了。

    試試看吧,有問題再來信討論吧 :)

: 不好意思又打擾了
: 程式其實寫得差不多了
: 只是有些小問題不知道怎麼處理
: 想跟您請叫關於AudioToolBox的音效播放 不知您是否熟悉
: 由於我本來按鈕播放音效是用AVAudioPlayer
: 但聲音跑出來都會延遲個零點幾秒 很短的延遲 但是還是有感
: 對音樂軟體來說有些不太流暢
: 現在是考慮用AudioToolBox來播放
: 因為音效聲音跟我手觸碰幾乎同步反應 沒感覺到延遲
: 雖然無法調整音量 跟其他控制   但就是反應很快
: https://www.youtube.com/watch?v=bq7ldV9Wsnc
: 想請問一下F大
: 我用AudioToolBox播放  發現音跟音之間都會重疊
: 譬如第一個音還沒結束的時候  我再按第二個音,    第一個音不會消失 變成重疊存在
: 有辦法讓就只有最後一個點擊的有聲音 而之前的都被停止嗎?
: 就是只有單一個按鍵的聲音存在而已
: Thank you so much!

[問題] 請教如何改變UIButton的highlighted狀態? (My answer for question in MacDev)

引用:http://www.ptt.cc/bbs/MacDev/M.1407647206.A.BD3.html
原文章問題簡述:如何手指不離開螢幕觸發多個Object
在ptt上回應了一篇個人認為算是有通盤回應到答案的文章,如下。(回覆部分黑底反白)


 作者  flamelad (flamelad)                                       看板  MacDev
 標題  Re: [問題] 請教如何改變UIButton的highlighted狀態?
 時間  Sun Aug 10 13:06:43 2014
───────────────────────────────────────

    關於你的問題昨天原本想回信,但因為太晚又考慮到我並不是非常熟悉,所以就沒回
應了。由於你來信詢問,那我會盡我綿薄之力提供建議,不對之處還請多見諒。另外這篇
我就直接回應到版上占用一些版面,讓版友可以指出我不對之處或提供更佳解 :)

※ 引述《pistols (我不想上報 別寄信啦)》之銘言:
: 請問一下版友
: 我想設置多個UIButton
: 譬如我從xcode右下角拉了九個UIButton放到ViewControl
: 排列就像電話的按鈕 1 2 3...9這樣九宮格的排列
: 我想設計每個按鈕的state狀態為highlighted時  會產生個別的反應或音效
: 可是忽然發現UIButton的highlighted的特性跟我想的不太一樣
: 譬如當我按住button1時候  就會有highlighted
: 但是我手指拖移離開button1 移到button2的範圍內時
: button1的highlighted仍然未消失   button2也沒有出現highlighted

    就如上次你詢問時的解答,假如你只是想要click button 後可以得到被點擊後或點住
不放的狀態,那isHighLighted 就可以得到你想要的結果,否則應該使用其他的-(method)
或判斷。(請務必參考isHighLighted 的description)
    另外就我所了解的是,當你手指未離開屏幕而移動,整體來說只會是某個 Object 的
a series of events,包括Inside, Outside, DragExit等,而不是手指從button1 拖曳到
button2 就會trigger button2 的event。

: 想請問版友
: 我如果想讓每一個button只要是手指有接觸到該按鈕的範圍內就會有highlighted反應
: 而一離開或是拖移超出範圍馬上就不會有highlighted
: 我該如何設置呢?

    這並不難,請參考UIButton繼承的UIControl中的一些method,主要是需要改變按下
button的default UIControlEvent。

: 譬如像上面說的例子
: 當我手指離開button1範圍 移到button2範圍的時候
: 如何讓button1的highlighted馬上消失  而變成button2的highlighted跑出來

    這例子若是手指並不離開屏幕,則與上一段你所希望的並不相同,請見第一段的解釋
。若你想有手指不離開屏幕而觸發多個 Objects,應該使用Gesture Recognizer或是版友
所提到的UITouch中的-(method)。

: 是要拉每個按鈕的IBOutlet到interface底下 增加什麼判定的屬性嗎?
: 我是有試過拉IBAction 然後用touch drag outside或是drag inside等等
: 但都不會跑出我所說的那樣
: 而且  我的功能是用按鈕的state去做反應的
: 希望版友可幫忙解答一下
: 非常感謝!

    請參考UIControl。另外若有拉IBAction,每當你觸發了版面上的某個Object event時
,則IBAction中常見的sender就是被觸發Object的instance,你可以借由這個sender去改
變行為。

  以上,大致上是我的理解,希望有幫助:)

Edit:
    最後一段有些謬誤的地方,不好意思,趕吃飯:p
    謬誤:
        1. 在button中的sender並不該是改變event,而是增減一些event才對。
        2. 若是要修改該button的event,一般也不會在該button中實現,而是如你
           所說,另外declare一個IBOutlet,然後去增加該button的event

[問題] 如何讓兩個按鈕一起按會有獨立事件? (My answer for question in MacDev)

    有點久沒更新,罪惡啊~不過這也不能怪我,原本預計要寫iOS如何與php傳送加解密的資料,但原理部分實在有點難寫,這一兩個禮拜再來完成它。接下來一系列是將我在其他地方替其他人解答的文章或往來信件,轉錄過來做個記錄,哪天也需用得到。

原問題及回覆:(回覆部分黑底反白)


 作者  flamelad (flamelad)
 標題  Re: [問題] 如何讓兩個按鈕一起按會有獨立事件?
 時間  Sat Jul 26 01:59:14 2014
───────────────────────────────────────
: 想請教板友們
: 關於iOS開發的按鈕問題
: 譬如設計個音樂遊戲
: 按了A按鈕會有個IBAction方法 是發出Sound A的聲音
: 並另外再設計B跟C按鈕 但是按下去不具任何聲音
: 想請問一下
: 如果我想讓它有另外一個功能:
: "當B按鈕按住 再按A按鈕 才會產生Sound B的聲音"
: "當C按鈕按住 再按A按鈕 才會產生Sound C的聲音"
: 我應該要在A按鈕的IBAction方法怎麼寫才會有
: "按A = Sound A聲"   "B按住又按A = Sound B聲"  "C按住又按A = Sound C聲"
: 就是把A鈕當作發聲的主事者  B跟C只是疊加A用來改變聲音
: 請問這樣的功能大概是要如何寫?
: 希望板友可幫忙解答一下!  感謝!
: p.s. 另外 聲音的部分由於是短短的音效  我使用AudioToolbox去寫
:      實機測試才發現 音量無法從機身的音量按鍵調整大小聲....
:      如果使用短音音效比較推薦用哪種框架的呢?
    我是資淺開發者,就獻醜說說有關button的想法,覺得不好或根本是個誤會,
    還請多諒解:)

    1.
    寫一個-method裡頭含有button的istracking判斷,用來判斷buttonB以及buttonC的
UIControlEvents狀態,再透過tag或title判斷哪個button被按下。declare一個變數值,
並根據-method中的判斷是來指定數值,可以是1,2,3......,總之,就是用來當buttonA
按下時,判斷該使用什麼音效。
大致上是這樣:
UIButton *buttonB & *buttonC -> init button並各指定Down與cancel給B與C,並含有
一個@selector(-method)-> -method寫判斷式去判斷button以及利用istracking判斷
button狀態->修改一個@property的變數值為0,1,2其中一個 -> ButtonA按下時根據變數
選擇音效發出。

    2.
    用notificationcenter去監聽buttonB&C狀態,其餘判斷與構想差不多跟1一樣

   以上是一點淺見,不好用或根本誤解了還請多見諒:)

 作者  flamelad (flamelad)
 標題  Re: [問題] 如何讓兩個按鈕一起按會有獨立事件?
 時間  Sat Jul 26 12:14:35 2014
───────────────────────────────────────

    另外看到有版友說能使用highlighted這個State,在這邊補述一下,button的event有
各種不同狀況,那假定是最常見Down、Cancel與UpInside,highlighted可能不足夠滿足你
的條件。當button TouchDown的時候,isTracking與isHighLighted都會等於YES,但是當
button觸發的event是UpInside時,isTracking會等於NO,但isHighLighted會等於YES,這
在Description中也有提到。
    簡而言之,不管是Cancel或UpInside,istracking都會改變狀態,但isHighLighted只
有在Cancel時,才會改變狀態。另外,是我昏了頭,確實不需要額外寫-method,一時忘記
直接使用isTracking在buttonA中判斷buttonB&C的狀態即可,不好意思:P

2014年8月17日 星期日

NSNotificationCenter

同樣是很久前就解決的問題...積了不少文章 囧

這個類別在我剛接觸時其實有稍微困擾過,後來再姑狗上看到了同是Stanford的教學卻有附註解釋,真是容易懂太多了,不常用到時會經常忘記,就發這篇記錄一下囉。

透過defaultCenter這個+(method)來alloc與init一個NSNotificationCenter instance,在調用下方的-(method)即可實現監視物件活動的方法。

NSNotificationCenter *center=[NSNotificationCenter defaultCenter];
addObserver - 誰來收通知(通常是self)
selector    - 當收到通知時被調用的方法
name        - 決定監視該發出通知的情況類型,通常是已經預先define好的字串
object      - 被監視的對象

NSNotificationCenter

- (void)addObserver:(id)observer // you (the object to get notified)
selector:(SEL)methodToInvokeIfSomethingHappens
name:(NSString *)name 
// name of station (a constant somewhere)
object:(id)sender; // whose changes you’re interested in (nil is anyone’s) You will then be notified when there are broadcasts

You will then be notified when there are broadcasts 
- (void)methodToInvokeIfSomethingHappens:(NSNotification *)notification
{
notification.name 
// the name passed above
notification.object // the object sending you the notification notification.userInfo // notification-specific information about what happened
}

How to implement serial execution of GCD queue?(group, barrier, serial)


廢話time:
原本打算是從上一篇文章開始都用英文撰寫來練習英文寫作,但是打完上一篇文章後卻覺得,繁體中文資源已經不多,很多技術文件或討論文件在中文搜尋上根本是零零落落或乾脆搜尋不到相關資料,因此還是改變決定,繼續以中文撰寫,算是勉強盡一份心力於中文資源。

積存已久的問題,雖然早已解決,但是GCD的部分還是沒有很熟,還需要了解NSOperationQueue與NSThread的差別,現在主要在熟悉CS193p的文件,等未來有空再發一篇文章補齊三者的使用時機。

在main thread 使用NSURLConnection,建立一個thread等待資料庫讀完,再回到main thread執行一些code,在program裡面看起來如下:

//part.1
[NSURLConnection *myDB=[...];
//概要來說是使用POST Method, 送NSURLrequest以及設定delegate為self, 
//在NSURLconnection的protocol - connection didRecievedata實現一個loadprogress

//part.2
     dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
     dispatch_async(queue,^{
     while (myDB.loadprogress!=1){
          //這裡只是等待progress=1, 就是讓data收完
}
     dispath_async(dispatch_get_main_queue, ^{
          //data收完,回到main thread去做事,例如轉換格式,取資料,填資料到tableview等
     });
});

//part.3
//這邊繼續做main thread的事

現在問題在於,假使我希望part3在某個片段可以等待part2完成(收data)再繼續做事,比較好的寫法是什麼?

Solution:
GCD有group、serial或barrier的建立方式,都可以實現等待上個thread完成後再執行的效果。以下是一些簡單的implement:

barrier - 如同barrier的意思,會擋住下游的thread執行直到上游的thread執行完畢以及本身thread執行完畢,方讓下游的thread繼續執行。
   
    dispatch_queue_t queue=dispatch_queue_create("flamelad", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"concurrent:%d\n",i);
        }
    });
    dispatch_barrier_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"barrier:%d\n",i);
        }
    });
    dispatch_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"concurrent1:%d\n",i);
        }
    });
//    輸出結果:
//    2014-08-17 01:58:42.464 Sherlock[2836:1303] concurrent:0
//    2014-08-17 01:58:42.465 Sherlock[2836:1303] concurrent:1
//    2014-08-17 01:58:42.466 Sherlock[2836:1303] concurrent:2
//    2014-08-17 01:58:42.467 Sherlock[2836:1303] barrier:0
//    2014-08-17 01:58:42.468 Sherlock[2836:1303] barrier:1
//    2014-08-17 01:58:42.469 Sherlock[2836:1303] barrier:2
//    2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:0
//    2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:1
//    2014-08-17 01:58:42.471 Sherlock[2836:1303] concurrent1:2
 
serial- 與concurrent不同(很明顯吧XD),queue中的每一個thread會循序處理,FIFO
    dispatch_queue_t serial=dispatch_queue_create("flamelad", NULL);
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial:%d\n",i);
        }
    });
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial2:%d\n",i);
        }
    });
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial3:%d\n",i);
        }
    });
//    輸出結果
//    2014-08-17 02:19:43.616 Sherlock[2898:1303] serial:0
//    2014-08-17 02:19:43.626 Sherlock[2898:1303] serial:1
//    2014-08-17 02:19:43.643 Sherlock[2898:1303] serial:2
//    2014-08-17 02:19:43.644 Sherlock[2898:1303] serial2:0
//    2014-08-17 02:19:43.646 Sherlock[2898:1303] serial2:1
//    2014-08-17 02:19:43.648 Sherlock[2898:1303] serial2:2
//    2014-08-17 02:19:43.649 Sherlock[2898:1303] serial3:0
//    2014-08-17 02:19:43.650 Sherlock[2898:1303] serial3:1
//    2014-08-17 02:19:43.651 Sherlock[2898:1303] serial3:2
group - group有notify和wait方式可以實現類似barrier的效果,不同處在於可以做一些細節上的設定。從輸出結果即可知wait會等待上游thread完成,notify則會等到group中所有thread完成。
    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group=dispatch_group_create();
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group:%d\n",i);
        }
    });
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group2:%d\n",i);
        }
    });
//    可設定等待下游的thread等待上游thread多久時間後開始執行,FOREVER則是等到上游執行完成後。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group3:%d\n",i);
        }
    });
    dispatch_group_notify(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group4:%d\n",i);
        }
    });
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group5:%d\n",i);
        }
    });
//    輸出結果
//    2014-08-17 02:32:54.587 Sherlock[3001:130b] group:0
//    2014-08-17 02:32:54.587 Sherlock[3001:3c07] group2:0
//    2014-08-17 02:32:54.588 Sherlock[3001:130b] group:1
//    2014-08-17 02:32:54.588 Sherlock[3001:3c07] group2:1
//    2014-08-17 02:32:54.590 Sherlock[3001:130b] group:2
//    2014-08-17 02:32:54.591 Sherlock[3001:3c07] group2:2
//    2014-08-17 02:32:54.591 Sherlock[3001:130b] group3:0
//    2014-08-17 02:32:54.592 Sherlock[3001:3c07] group5:0
//    2014-08-17 02:32:54.593 Sherlock[3001:130b] group3:1
//    2014-08-17 02:32:54.593 Sherlock[3001:3c07] group5:1
//    2014-08-17 02:32:54.594 Sherlock[3001:130b] group3:2
//    2014-08-17 02:32:54.595 Sherlock[3001:3c07] group5:2
//    2014-08-17 02:32:54.596 Sherlock[3001:3c07] group4:0
//    2014-08-17 02:32:54.597 Sherlock[3001:3c07] group4:1
//    2014-08-17 02:32:54.598 Sherlock[3001:3c07] group4:2

這篇只介紹了三種最常用到的GCD thread執行方式來解決我的問題,但其實尚有些如dispatch_once、dispatch_semaphore_t等的執行方式,有興趣者再自行姑狗,都是在實現multi-thread石,可能會用到的方法。

2014年7月27日 星期日

Some problems with my development Part.1 (tabBar, navigationItem, Segue)

since the article, I will use English to write my blog. If there are any problems in my blog, please tell me, thanks.

1. How to edit tabBar items in tabBarController?

[[self.tabBarController.tabBar.Items objectAtIndex:(Integer)] setTitle:(NSString)]

2. how to edit/define back/leftBarButton's title in navigationController?

[self.navigationController.navigationBar.topitem setTitle:NSString];(This is wrong!!)
There are three button types in navigationBar:
 a. leftBarButtonItem, you can define a custom leftBarButtonItem of navigationBar in the view controller where the button is.
 b. backBarButtonItem, you can define custom backBarButtonItem of navigationBar in the superview controller where the button is.
 c. default Button, if you don't define anything(left and back all nil), navigationController will auto-generate a back button.

setBackBarButtonItem method: Important! Assuming now have two view controllers which called A&B, and you want to change B's back Button. So setBackBarButtonItem must be wrote in A.
// backBarButtonItem doesn't need to put target and action, because it will work as function of back button.
    [[UIBarButtonItem alloc] initWithTitle:@"Customize backButton"
                                     style:UIBarButtonItemStyleBordered
                                    target:nil
                                    action:nil];
    [[self navigationItem] setBackBarButtonItem:newBackButton];
setLeftBarButtonItem method: Important! Writing these codes to customize button in view controller which you want to customize it.
// Don't forget to put target and action, or it doesn't work as button.
    [[UIBarButtonItem alloc] initWithTitle:@"Customize leftButton"
                                     style:UIBarButtonItemStyleBordered
                                    target:self
                                    action:@selector(-method)];
    [[self navigationItem] setLeftBarButtonItem:newBackButton];

P.S.: Left/back button instance variable is nil at begin in the navigationController, if want to change anything for left/back button, you must to create an UIButton object and assign to navigationController.

3. Performing segue after executing something what you want to do.

There are two steps:
    a. When you establish push relationship between view controllers, don't make it on button. Building it at any non-trigger component.
    b. Using the method which show as below to jump to next view after complete all what you want to execute.
    [self performSegueWithIdentifier:NSString sender:(id)]

2014年7月5日 星期六

chilkat blowfish for iOS data transmission PART3

這兩三個禮拜工作下來,發現其實沒什麼時間可以更新這邊的文章,上班拼命找solution,下班死命看knowledge,未來到勉強上手前,可能真的只會有筆記式的文章留在這了。
然後新人想去公司培養能力真的要慎選公司,公司沒有好的規劃也沒有專業領域前輩的話,進去只會非常非常非常的吃力,而且會浪費許多時間走彎路。
以上這意見僅供給想安穩過生活的人參考,在沒有不合理對待的前提下(像是薪資22k、責任制、要求幾天內要有成果),我認為做一件事最重要的是有沒有『興趣』,雖然我累得要命,但卻也累的快樂。『愛你所選』我認為是這麼解釋的:做你有興趣或有目的的事,並一直持續保持熱情地去做,而不是什麼把工作當成興趣,那只是無法朝向自己目標前進的人以及資方壓榨勞方的理由。人生短短幾十年,有必要把1/3或更多的時間花在自己其實並不感興趣的工作上嗎?三個傻瓜的道理,『追求卓越,成功自然隨之而來』,加油!

不廢話了,下面說明最後我實做的部分。

#import 
#import "CkoCrypt2.h"

@interface BlowFish : NSObject
-(NSString *) encode:(NSString *)str;
-(id) decode:(NSString *)str;
@end

先建立Blowfish的header file,並在前面繼上回的chilkat blowfish for iOS data transmission PART1, chilkat blowfish for iOS data transmission PART2已經設定好的CkoCrypt2的header file import進來(或@class),並建立encode與decode兩個實體方法(或要建類別方法也行)以及一個CkoCrypt2的物件供其它函數的使用。
#import "BlowFish.h"

@interface BlowFish()
@property CkoCrypt2 *crypt;
@end

@implementation BlowFish

-(instancetype)init{
    self = [super init];
    _crypt = [[CkoCrypt2 alloc] init];
    NSString *keyAscii = @"1234567812345678";
    NSString *ivAscii = @"12345678";
    if([_crypt UnlockComponent: @"Anything for 30-day trial"]){
        _crypt.CryptAlgorithm=@"blowfish2";
        _crypt.CipherMode = @"cbc";
        _crypt.EncodingMode = @"hex";
        _crypt.KeyLength = [NSNumber numberWithInt:256];
        _crypt.PaddingScheme = [NSNumber numberWithInt:3];
        [_crypt SetEncodedIV: ivAscii encoding: @"ascii"];
        [_crypt SetEncodedKey: keyAscii encoding: @"ascii"];
    }
    else
        NSLog(@"trail day is exceeded");
    return self;
}

-(NSString *) encode:(NSString *)str{
    return [_crypt EncryptStringENC:str];
}
-(id) decode:(NSString *)str{
    return [_crypt DecryptStringENC:str];
}
@end
implement裡面其實就沒什麼好說的,就是在init裡面把前面part1與part2說明過的一一設定完成即可,最後encode與decode方法return一個NSString的物件,那內容當然是encode或decode後的東西囉。
...
BlowFish *blowfh=[[BlowFish alloc] init];      //別忘了要init,在BlowFish中的所有設定都是放在init裡。
NSString *encodeStr=[blowfh encode:@"123"];    //123字串加密
NSLog(@"encodeStr:%@",encodeStr);              //可以看到16進位的一串數字
NSString *decodeStr=[blowfh decode:encodestr]; //123字串解密
NSLog(@"encodeStr:%@",decodeStr);              //可以看到123字串

呃,就像上面的註解囉。
以上,就是非常簡單但我浪費很多時間的chilkat的blowfish加密方式Orz

2014年6月27日 星期五

chilkat blowfish for iOS data transmission PART2

繼上回的chilkat blowfish for iOS data transmission PART1安裝完成後,再來就是正式的引用啦。

以下這裡是官網給的範例,其實照著走就可以了..那我到底為啥花了這麼多時間 Orz

CkoCrypt2 *crypt = [[CkoCrypt2 alloc] init];

BOOL success;
success = [crypt UnlockComponent: @"Anything for 30-day trial"];
if (success != YES) {
    NSLog(@"%@",crypt.LastErrorText);
    return;
}
以上這串一定要寫進code,不寫的話不給用,也別想在他的include資料夾找他的source code破解,沒加密才怪啊,不過網路上可能有破解版就是?

//  Attention: use "blowfish2" for the algorithm name:
crypt.CryptAlgorithm = @"blowfish2";

//  CipherMode may be "ecb", "cbc", or "cfb"
crypt.CipherMode = @"cbc";

//  KeyLength (in bits) may be a number between 32 and 448.
//  128-bits is usually sufficient.  The KeyLength must be a
//  multiple of 8.
crypt.KeyLength = [NSNumber numberWithInt:128];

//  The padding scheme determines the contents of the bytes
//  that are added to pad the result to a multiple of the
//  encryption algorithm's block size.  Blowfish has a block
//  size of 8 bytes, so encrypted output is always
//  a multiple of 8.
crypt.PaddingScheme = [NSNumber numberWithInt:0];

//  EncodingMode specifies the encoding of the output for
//  encryption, and the input for decryption.
//  It may be "hex", "url", "base64", or "quoted-printable".
crypt.EncodingMode = @"hex";

//  An initialization vector is required if using CBC or CFB modes.
//  ECB mode does not use an IV.
//  The length of the IV is equal to the algorithm's block size.
//  It is NOT equal to the length of the key.
NSString *ivHex;
ivHex = @"0001020304050607";
[crypt SetEncodedIV: ivHex encoding: @"hex"];

//  The secret key must equal the size of the key.  For
//  256-bit encryption, the binary secret key is 32 bytes.
//  For 128-bit encryption, the binary secret key is 16 bytes.
NSString *keyHex;
keyHex = @"000102030405060708090A0B0C0D0E0F";
[crypt SetEncodedKey: keyHex encoding: @"hex"];

//  Encrypt a string...
//  The input string is 44 ANSI characters (i.e. 44 bytes), so
//  the output should be 48 bytes (a multiple of 8).
//  Because the output is a hex string, it should
//  be 96 characters long (2 chars per byte).
NSString *encStr;
encStr = [crypt EncryptStringENC: @"The quick brown fox jumps over the lazy dog."];
NSLog(@"%@",encStr);

//  Now decrypt:
NSString *decStr;
decStr = [crypt DecryptStringENC: encStr];
NSLog(@"%@",decStr);

以上除了最後的加解密example可以不用管,其它照著放進去再修改就行了。
需要更改的地方可能有幾個:
1. EncodingMode =@"hex";
這邊看個人喜好喜歡用什麼加解密的input/output格式,例如使用hex,則data進去後,出來就是一串hex。
2. ivHex
為16進位的加密字串,由於blowfish有8 bytes的block size且IV的長度與block size相同,所以這邊改成自己的加密字串,並且只需要16個字即可。至於為啥是blowfish 是8 bytes,請google XD
3. keyHex
這邊的長度就是一開始設定的keylength長度,由於是128/8=16bytes,所以要用32個16進位的字去填滿。

4.  crypt的方法SetEncodedKey: encoding:@"hex" 中的encoding:
這邊雖然範例中沒有提,但其實是可以改的,例如我在使用的時候就是把兩個encoding的@"hex"改為@"ascii"。我的情況是改成@"hex"會導致解碼錯誤。原因是什麼還不清楚,如果有人知道再告訴我,不然等改天我清楚了再來補發一篇囉。(發完立刻想到可能的原因..囧)
自解:我這邊 PHP API放置的server預設編碼方式是utf-8,因此用ascii字元集編碼,才不會導致傳過去的資料,在API那一邊看會有亂碼產生,這也是為什麼我改成hex的話,會導致解碼錯誤。


最後使用設定好的crypt的EncryptStringENC以及DecryptStringENC去加解密就可以了。
下一回是最終回,使用在與PHP API資料加解密的傳輸。

補上chilkat對於iOS的blowfish reference documentation
http://www.chilkatsoft.com/refdoc/objcCkoCrypt2Doc.html

chilkat BlowFish for iOS data transmission PART1

這部分真的是浪費了我不少時間,要不斷兩邊驗證收到的data正不正確,還要把收到的data轉回正確能用的格式,my god Orz 以下正式心得文開始。

這邊使用的是第三方寫的資料加密傳輸,chilkat software中的iOS encryption部分,主要是採用blowfish的CBC方式去加密。(這裡我屬於使用者,什麼是blowfish以及CBC,請找google大神)

引用前需注意事項:
這個是以盈利為目的的第三方API提供者,所以API只有30天試用期,想找永遠免費的可能就要自行再多找找了,我大概一個月後有時間才會再來弄這個資料加解密的部分 (茶)

引用方式:(其實官網有說明,但我還是花了不少時間 Orz)
1. http://www.chilkatsoft.com/downloads_ios.asp  在這邊download他的code,有分兩種版本,都能用,差別好像在compile時候用的lib?
2. 下載後需要解壓就解壓,資料夾放哪裡是都沒差,不過我還是習慣放在project的資料夾下
3. 在command模式下執行他提供的shellscript去輸出一個allversion的.a檔
4. 開啓Xcode如下圖,加入static library,選取剛剛產生出來的.a檔

5. 加入-lresolv到Build setting裡的other linker flag,以及設定user header search path為$(SRCROOT),並選擇recursive。(由於SRCROOT是指向這個project的資料夾位置,所以我把解壓出來的檔案就放在project的資料夾中)

6. 在project中產生一個cpp檔案,什麼都可以刪掉,能正常run就行,然後加入一行import "CkCrpyt2.h"

到這邊大致上設定完成,再來就是引用加解密的部分了,請看下回的chilkat blowfish for iOS data transmission PART 2。
如果還有什麼問題其實可以先看官網的安裝說明,大致上會遇到的問題應該都有說明。



2014年6月26日 星期四

care alertView when view is changed

記錄記錄....
//若alertview發生在換場景的時候,需注意delegate的角色會不會已經release了
UIAlertView alert=[[UIAlertView alloc] initWithTitle:@"登入成功"
                                          message:@""
                                         delegate:self
                                cancelButtonTitle:@"確定"
                                otherButtonTitles:nil];
[alert show];

[self.navigationController popToRootViewControllerAnimated:YES];
//這一段執行後會call dealloc method
在使用alertView時,遇到一個情況如上方code,當我在同一區塊使用poptorootview後,由於viewcontroller已經執行dealloc,所以當按下alertView 不管是cancel button或自定button都會發生delegate找無人的情況導致crash。

有兩個方式解決這個問題:
1.
delegate:nil
在delegate的參數部分直接改成nil,這樣任何事件都不會去找view controller了。呃,還是很有用的,假如真的只是要做一個警告視窗而已,沒有要觸發什麼事件的話。
2.
- (void)dealloc{
    [_alert setDelegate:nil];
需要在dealloc的方法中,加入一段[alert setDelegate:nil],讓delegate變成nil,這樣當alert有button被click時,alert不會再去找已經release掉的view controller,不過當然若原先有在按鈕寫什麼事件,也都不會觸發了。與方法1相比,差別在於除了view controller 被release時不能觸發任何事件,這個方法在其他時候還是可以delegate給view controller去執行事件。

P.S.:修改dealloc時,只要加入自己想要release或set nil的部分即可,官方文件已經有說明在ARC機制下,dealloc方法裡面在編譯時,compiler會自動加入[super dealloc]。


2014年6月25日 星期三

NSPredicate for NSDictionary and NSArray


NSPredicate 可以用來驗證Array, String等內容是否符合格式,以及還有各種判斷式可以驗證內容,回傳也分為Array、BOOL或?我也還沒非常熟悉這個類別,總之是很有用的一個類別,詳細格式就不多說,挺多文章的,就記錄一下我釐清後的記錄。
ex:
@interface ...:...
@property NSDictionary *dataDict;
end
@implement ...
//以下是要做到能使用MATCHES的表示式去判斷物件內容有無符合條件
//Pridicate的驗證format,這裡是用正規表示式當成驗證條件。

NSString *verifyStr=@"[A-Z0-9a-z]{1,18}";

//一維Array的驗證方式,另外宣告testArray那行的意思是,取出dataArray
符合verifyStr的正規表示式的結果。filteredArrayUsingPredicate會累積
比對後return YES的 NSarray 物件並回傳。

NSArray *dataArray=@[@"abc",@"1234"];

NSPredicate *predict=[NSPredicate predicateWithFormat:@"SELF MATCHES %@", verifyStr];

NSArray *testArray=[dataArray filteredArrayUsingPredicate:predict];


//單筆Dictionary的驗證方式,這邊使用的方法是只會驗證內容是否符合條件並回傳YES or NO,在 predicateWithFormat部分可以直接列出想要判斷的欄位以及條件。

NSPredicate *predict=[NSPredicate predicateWithFormat:@"(acc MATCHES %@) AND (pwd MATCHES %@)", verifyStr,verifyStr];

_dataDict=@{@"acc": @"abc",@"pwd":@"1234"};

BOOL result=[predict evaluateWithObject:_dataDict];


//多筆Dictionary的驗證方式,透過allValues取出所有value去進行驗證,並如同第一種狀況,會將累積符合驗證條件的結果後,以 NSArray 物件回傳值。

NSPredicate *predict=[NSPredicate predicateWithFormat:@"(first_name MATCHES [A-Z]) AND (last_name MATCHES [a-Z])"];

NSDictionary *multiDict=@{@"1":@{@"first_name":@"A",
                                 @"last_name":@"a"},
                          
                          @"2":@{@"first_name":@"B",
                                 @"last_name":@"b"},
                          
                          @"3":@{@"first_name":@"C",
                                 @"last_name":@"c"}
                          
                          };

NSArray *testArray=[[multiDict allValues]filteredArrayUsingPredicate:predict];

說明:
-(NSPredicate *)[predicateWithFormat:@"(acc MATCHES %@) AND (pwd MATCHES %@)", verifyStr,verifyStr]
NSPredicate物件。predicateWithFormat可視為與NSString的initWithFormat方法等相同,不同的是這邊NSString要輸入欲驗證的格式,例如這邊的acc MATCHES %@,意思即是說acc欄位的值要符合後方傳入的verifyStr內容,其中MATCHES的驗證內容可用正規表示式。
-(NSArray*) filteredArrayUsingPredicate:(NSPredicate *)
NSArray 方法,需傳入NSPredicate物件,透過已經設定好的格式,一一對Array內容進行判斷,累積比對後return YES的 NSarray 物件並回傳。
-(BOOL) evaluateWithObject:(id)
NSPredicate的instance方法,傳入值為id型態,對傳入的物件判斷後,回傳YES or NO的結果。

2014年6月21日 星期六

first ViewController of review


記錄一下過程,不過以後的記錄或心得可能會越寫越精簡(也就是少XDD),因為這樣寫法好花時間,現在公司急件啊!!Orz

1. 建立ViewController
開啟simple view,storyboard會初始建立一個ViewController,sub of UIViewController class,同時project裡頭會有ViewController.h/m,到這邊第一個就建好了。

2. 建立第二個ViewController,取為ScViewController
 使用storyboard拉View Controller,在Identity inspector中,將Custom class選為ScViewController,這動作是指示這個ViewController是由ScViewController負責

3. 建立Navigation Controller
選取希望使用者第一個看到的ViewController(不要選到view了,點擊下方黑色條列比較不會有問題),選取在功能列裡的editor->embed in->Navigation Controller即可。
Navigation Controller說明:
根據字面來簡單講,就是俱有scene導航的能力,展示使用者選取的scene。在有多個scene的情況下,會透過Navigation Controller來顯示目前的scene (embed in, 將畫面嵌入到Navigation Controller的scene),在官方解釋來說就是RootViewController。
P.S.: scene存放的方式是使用stack。
還是不懂Navigation Controller的話,請看CS193p的lecture 6,我覺得會比我在這邊畫虎爛半天還清楚。

4. 使用IB提供的物件庫拉button

5. button 前往ScViewController
 透過segue push 方式,也就是在button滑鼠右鍵拉到ScViewController去,就會在storyboard出現一條關係線,記得為這個segue 的identifier取個名。超方便啊這個,這個時代怎麼會這麼進步?
segue說明:
簡單來說,按照字面解釋就是滑順的動作,如滑順的推segue push,滑順的模態(XD?)segue modal, etc...,總之,沒啥意思,但要解釋是因為有些方法叫做...segue...,有點概念會比較好理解跟記這些方法。
另外說明button這個右鍵拖曳的一些功能:

  • 建立segue
  • 若在code裏面有宣告一些變數是要搭配storyboard版面上的物件,拖曳可建立relationship
  • 未在code裏面宣告變數,從storyboard的物件拖曳到code裡面會自動宣告變數

6. 方法
//button拉線的方式可以前往下個scene,但也可透過此方法前往scene
- (void)performSegueWithIdentifier:(NSString *)segueId sender:(id)sender;
// 在segue執行之前要做的動作
 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{}
//是否執行Segue的判斷
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{}
//是否pop掉目前的scene,也就是回上一scene
- (void)popViewControllerAnimated:(BOOL)animated;

IBOutlet & IBAction of review


雖然懂,但每次看到要在腦中自我解釋都會忘掉意義...
簡單來說,這兩個只是為了告知IB說,這邊有outlet(msg)/action要跟UI溝通(connection),IB你踏馬的記得要看一下,因此,這兩個宣告對於變數什麼改變都沒有,只是賦予意義而已,從下方原始定義可以看出來。

#ifndef IBOutlet
#typedef IBOutlet
#endif

#ifndef IBOutlet
#typedef IBAction void
#endif

簡單的範例來看 -
@interface ...

@property IBOutlet UILabel *detailDescriptionLabel;
- (IBAction)buttonPressed:(id)sender; 
@end

其實等於
@interface ...

@property UILabel *detailDescriptionLabel;
- (void)buttonPressed:(id)sender; 
@end


P.S.:
1. IB= Interface Builder, 視覺化界面設計工具,就是storyboard開啟的畫面,透過xml保存界面設計,這邊不太懂幹嘛用xml就是,json的格式感覺好看許多,改天再研究 XD
2. IB開頭的宣告都是為了給Interface Builder看,讓IB知道這是需要它解析的東西

review of obj-C id

id形態應該被用在以下地方:
1. 在一個collection中有多種不同class的物件
2. 不能知道資料傳遞時對於結構, 資料或方法呼叫時,例如MVC中常可看見的delegate

為了讓以上的使用方式可以更加安全,因此有了introspection與protocols

  • introspection 

是一個方法集合的意義,主要說明這個方法集合是用來做自我判別。例如isMemberOfClass, isKindOfClass等,另外值得一提的是SEL資料形態的變數selector,存放的資料是可被這個變數看見的“方法”,在此方法集合裡也有可驗証方法是否有被實做的方法。完整範例:[obj respondsToSelector:@selector(方法:)]

  • protocols     
最常見的就是委派,委託方需要建立一個變數為id <protocolMethod> delegate,讓使用動態形態的物件都必須實作protocol的方法,避免物件引用了本身未定義的方法

另外一提存在於selector的方法,在Object都有方法可以直接引用,例如以下範例:

[obj performSelector:selector]
[obj performSelector:selector withObject:obj]
[array makeObjectsPerformSelector:selector
[array makeObjectsPerformSelector:selector withObject:target]
[button addTarget:self action:@selector(digitPressed:) ...]; 

review of class of foundation

物件形態的資料:
NSString            - NSMutableString
NSArray             - NSMutableArray
NSNumber         - NSMutableNumber
NSDictionary      - NSMutableDictionary
NSSet                 - NSMutableSet - NSIndexSet
NSUserDefaults  
NSDate
NSData

immutable & mutable
物件內容可變與不可變,例如NSString = @"This is a shit" 不可執行deletechar, insertstring等會改變內容之動作。
而包含多物件的結構則是不可改變結構內容,例如NSarray = @[@"1", @"2", @"3", ...] 初始化建立後,不可執行NSarray[1]=@"1"等改變
陣列結構內容的動作。

review of delegation

這部分算是自我解讀的心得,請參考就好


delegation(委派) - 這是一種思維邏輯下的產物,因為Obj-C不使用delegation的方式,一樣可以完成delegation要做的事。但為了更容易釐清程式的設計概念,所以有了delegation。

  • 目的
delegation是為了簡化[類別之間的資料傳遞過程]以及[程式設計的思維模式]所產生的設計方式。

舉例來說,一個遊戲場景在程式設計中可能會分為View與Controller兩個部份,View是顯示遊戲場景的畫面,Controller則是負責控制場景。在這種設計模式下,若需要收集遊戲畫面的資訊或顯示資訊(例如遊戲中顯示FPS數字),不使用delegation有兩個做法:
1. 在view的類別底下新增一個method去print資訊
2. controller新增一個收集view傳來的資訊並顯示資訊的method.
這些設計方式在範例的思維邏輯上就會有一些矛盾:
1. 範例的敘述中,View是顯示遊戲場景使用,卻又多了可以顯示資訊的method
2. controller是負責控制場景,卻又有不相關的method去收集資訊
所以,delegation就是為了解決這種思維上的矛盾,所發展出來的design pattern
  • 概念
一般來說,當類別相互有關連時才會需要資訊傳遞,舉個常見例子就是view與controller。view需要顯示場景,但不希望工作變複雜而想把資訊顯示交給controller去做,因此view就委託(delegation) controller去顯示資料,而controller也不需要自己定義一個收集資訊的方法,而是根據view希望的資料顯示方式(protocol)去完成顯示即可。
delegation實做的概念可分為幾個步驟去理解,以下將委託方稱A,被委託方稱B:

A部分:
1. A需要定義protocol,並且需具有引用protocol的動態變數(基本上為了能更好理解,Obj-C皆使用delegate),讓B可以接受delegation。
2. A本身需具備判斷B有無實做protocol的方法,避免錯誤引用未實做的method
3. A的資料透過delegate動態變數傳遞給B

B部分: 
1. B引用A定義的protocol,宣告接受A的delegation
2. B需要實做protocol中的required部分
3. B本身需被指定為被委託方(程式碼直觀來看就是A.delegate = B)
4. 收到A的資料,執行protocol的方法

執行順序: A.1, B.1, B.2 -> B.3 -> A.2 -> A.3 -> B.4
當B.3完成後,則A執行本身的工作時,將會執行委託的流程A.2與A.3,並且透過delegate動態變數呼叫在B中的protocol方法,執行B.4步驟

了解delegation的目的,更容易清楚delegation的概念。

review of protocal

protocol
簡單來講 - 沒有被實做的方法集合,具有限制性,限制引用protocol則必須該實做的內容。
引用        - protocol會被類別, 變數或類目宣告時引用,protocol被引用以後,引用者要conform (採用) protocol定義的方法才不會出錯。
限制性    - @required與@optional說明了方法需不需要實做,一般protocol的宣告是默認為required,required的方法沒實做會出錯。

protocol 宣告:
新建一protocol名稱的header檔
@protocol ProtocalName
-協定方法
+協定方法
@optional
-協定方法
+協定方法
@end

引用方式:
interface NSObject(Category) <ProtocolName, ..., ..., ...>  //類目引用
-協定方法
+協定方法
...
end 

interface NSObject <ProtocolName, ..., ..., ...>                  //類別引用
-協定方法
+協定方法
...
end 

id <ProtocolName, ..., ..., ...> 變數名稱                              //變數引用 (表示此變數有實做這些協定)

測試有無實做protocol的API:
id 變數名稱;
...
if ([變數名稱 conformsToProtocol: @protocol (ProtocolName)] == YES)
...

end 

review of category & class extension

  • category(類目) 
不修改原有的類別內容,新增此類別的方法
  • class extension(類別衍伸)

同類目,但不止可以新增方法到類別,還可新增變數到類別。類別衍伸時,介面與實做區段要在同一檔案,一般此做法可以讓方法具有private性質(因為方法不會出現在.h中,使用者看不到,但若使用者知道名字一樣可以用),給類別使用。

ex:在NSObject類別新增一類目

開新檔,取名原則為原類別名稱加上類目名稱 -> NSObject+NewCategoryMethod.m&.h,類目宣告方式→interface 類別(類目名稱)
使用時同樣import此新檔,則型態即具有此類目方法
interface NSObject(NewCategoryMethod)
-(...)
+(...)
end

@implementation NSObject(NewCategoryMethod)
-(...)
+(...)
end
ex:在NSObject類別新增一類別衍伸

在實做檔中(.m檔案裡),宣告一無名稱類目

@interface NSObject()
...
end

@implementation NSObject
...
end

2014年6月20日 星期五

review of enum & typedef

就有點忘了這樣...


enum用法:
1.
enum 宣告名稱 {內容1,內容2=數值,內容,...}
enum 宣告名稱 變數名稱;

2.
enum {內容1,內容2=數值,內容,...} 變數名稱

3.
enum 宣告名稱:宣告型態 {內容1,內容2=數值,內容,...}

typedef用法:
typedef 型態 新型態名稱

enum + typedef:
typedef  enum {內容1,內容2=數值,內容,...} 型態名稱
型態名稱 變數名稱;

#ifdef & #ifndef
1.
#ifdef
...
#else
...
#endif

2.
#ifndef
...
#else
...
#endif

comment of IAP

壓力踏馬的比山大啊... (這篇廢話有點多)

        老實說,身為公司第一個開發iOS App的工程師,尤其還是一位不太會開發iOS App的工程師XD,當遇到專案日期評估、App開發可行性或功能開發可行性等問題,這比天塌下來還可怕啊。
        今天還在想我改給andriod用的銀行金流php API,忽然想到Apple可沒在給你玩這套時,就馬上抖了一下,OK,這不是我的問題,嗯,不是我的。只是我要想辦法解決而已Orz
       這也沒什麼好解決的,Apple可沒那麼仁慈,總之就是上報一句「Apple拒絕非官方的付費機制」,然後提一些不是做法的做法,像是在App內放置一網頁用來向外轉到不是放在App內的付費網頁,審核時不可內含任何訊息是需要透過非IAP方式付款(茶)
        另外有翻到的黑心做法是不上架到App store,利用企業內部署App的資格,將App部署到自己的server上(ipa, plist),透過網頁提供給Users安裝。這是個解法啦,不過我當作沒這回事就是了XD
        總之,Apple接受的只有IAP這官方付費機制,其餘什麼外接內接管你什麼接,Apple一概回絕。

P.S.: 經過大大指點得知,IAP的管制是對於虛擬貨物,也就是沒有實體物品的情況下受限於Apple的條款,若是實體貨物,則不受限於IAP的管制,是可以轉向其他付款方式。

OK, let me start to Develop it


        呃,其實不會有什麼精深的東西,也就只是把我學習過程記錄一下,原因是看到有人說最好在學習過程中做些記錄,所以就這樣啦,會把這一個禮拜來私底下記錄的review心得轉過來,以後有心得就繼續更新。

        要說的是,總算開始做這件事了?