2013年8月2日 星期五

Objective-C 關於 static

static 這個變數的修飾詞, 也許是比較沒緣份, 個人沒有太多的機會去實際使用到.

但是在 iOS 開發時使用 tableview 一定有看過下面的寫法.

static NSString *CellIdentifed = @"CellID";
....下略

static 在變數前表示將該變數宣告成 "靜態" 變數. 很多書或資料上都有介紹 static 的用法.

一直以來我也沒對他多做深入研究, 今日咱家技術長對 static 的一番解釋, 讓我對 static 有徹底了解的感覺.

要真正了解 static, 果然還是得從記憶體的角度去解釋.

EX:

static NSString *str = @"someStr";

表示系統會在記憶體中分配一塊固定的記憶體給 str 這個變數, 變數名稱必須是唯一.

 之後若重覆存取 str, 該變數在記憶體中的位置也不會改變.

什麼意思呢? 舉個簡單的例子好了

假設今天有兩個 method 如下

-(void) helloStatic {

      static int a=0;

      NSLog(@"static a = %d", a++);

}

-(void) noStatic {

      int a=0;

      NSLog(@" a = %d", a++);

}
然後在 viewDidLoad 呼叫這兩個 method, 如下
-(void)viewDidLoad {
      int i = 0;
      for(i=0;i<5;i++){
          [self helloStatic];
      }
      for(i=0;i<5;i++){

          [self noStatic];

      }
}
那麼印出來的結果會是
static a = 0
static a = 1
static a = 2
static a = 3
static a = 4
a = 0
a = 0
a = 0
a = 0
a = 0

Why???

在 viewDidLoad 中, 分別呼叫了五次 helloStatic 與 noStatic

helloStatic 中使用 static 修飾詞, 所以每次在對 a 這個變數做存取時, 系統都在同一個固定的記憶體位址做+1的動作, 所以印出來的結果會是 0 ~ 4.

但 noStatic 沒有使用 static, 所以每次呼叫 noStatic 這個方法時, 系統都會分配不同的記憶體位址給 a 且初始值為 0, 所以印出來5次的值皆為 0.

至於要怎麼在同一個檔案中誇  method 存取 static 變數? 只要使用 extern 就可以了.

一樣, 人腦 compiler 而己, 有錯誤請指正, 感謝各位

2013年7月9日 星期二

Asset framework 簡單範例

ALAssets 簡介 參考

An instance of ALAssetsLibrary provides access to the videos and photos that are under the control of the Photos application.

The library includes those that are in the Saved Photos album, those coming from iTunes, and those that were directly imported into the device. You use it to retrieve the list of all asset groups and to save images and videos into the Saved Photos album.

簡單說就是其實存在 camera roll 裡的東西不是單純的影片和照片,而是一個一個的 Asset 裡面包很了很多其他的資訊,像是 Location。

昨天有位朋友問我要如何實現 iPhone 內建相機左下角打開 camera roll 的功能,且button要是camera roll裡最後一張的圖片。

這個 Button 分成兩個部份,一個是要如何擷取 camera roll 裡最後一張照片出來,第二個是要開啟相簿(目前似乎無法像 Apple 直接打開某一個 Asset)

要做到這功能必須使用 AssetsLibrary.framework 且要有 block 的觀念。

下載 DemoCode

這個 DemoCode 很多都是由 Apple 官方文件修改而來的,有興趣更進一步了解的可以參考 這裡

還有一點要注意的是,在 Democode 裡的 ViewController.h 必須 adopt UIImagePickerControllerDelegate 和 UINavigationControllerDelegate 協定

其他說明在 democode 裡都有註解表示了,就不在此多加闡述了,有問題可以留言。


2013年7月5日 星期五

Objective-C property屬性


Property 是在Objective-C 2.0之後才有的,主要是方便取存或設定物件裡的實體變數, 系統會幫你寫好 setter 和 getter (在 xcode4.5 之後不需要搭配 @sythesize 這個合成指令, compiler會幫你關聯至在原變數名稱前加上底線 ex  _abc).


另外 Property 有幾個屬性可以設定, 語法如下

@property(attribute1, attribute2…)int iVal;

這些屬性分別為

readonly : 唯讀,(compiler 不會幫你合成 setter method)。

readwrite : 可讀可寫(Default)。

assign : 在設值時替換新舊資料(Default)。

retain : 在設值時retain新的資料,release舊資料。

copy : 在設值時copy一份新資料,release舊資料。

strong /weak : iOS5 之後取代 retain 和 copy, 由compiler幫你決定使用那一種.

nonatomic : non safety thread

atomic :  safety thread(Default)

assign 就只是單純的將新值賦予變數, 如下

- (void)setX:(int)iVal {
       _value = iVal;
}

retain用於物件。會在給值前先 release 舊的值並於新的物件上將其引用計數器+1 後賦予變數, 如下

- (void)setX:(NSString*)sVal {
          if (_string != sVal ) {
                [_string release];
                _string = [sVal retain];
        }
}


copy 與 retain 基本上是一樣的, retain 是將該物件的引用計數器+1, 而 copy 會依照類別型態做不同的動作, 這部份還待理解....但是一切在 iOS 5 提出 ARC 後, retain 和 copy 都被 strong 和 weak 取代了, 交由 compiler 幫你決定是用 retain 或是 copy.

strong 跟 retain 沒兩樣, 每次呼叫時都會將引用計數器+1, weak 則只是單純的參照記憶體位址.

weak 最常用的兩個地方是 delegation 中的 id<myDelegate> delegate 和 subviews.

因為所有的 subviews 都被 mainview 所持有, 所以只需參照記憶體位址即可.

atomic 表示若同時有多個地方呼叫該變數的 setter 或 getter 則必須一個一個慢慢來. 較安全, 但效能較低.

nonatomic 表示多人能同時呼叫 setter 和 getter 安全性較低,但性能較高, 大多數時間都設定為此屬性.

2013年7月4日 星期四

Objective-C Delegate 委派觀念及使用整理

## DemoCode 下載

delegation 是一種 design pattern (設計模式),方便在各個物件之間傳遞參數以及互相支援。

@protocol 是Obj-c 裡的一個關鍵字,直到@end關鍵字做結束,通常宣告在 .h 檔置於 @interface 之上,用法如下:

//aaa.h
@protocol myDelegation <NSObject>
   @required
         -(void)delegationPrint;
   @optional
         -(int)delegationLog:(int) iVal;
@end

@interface aaa <NSObject>

@property (weak, nonatomic) id <myDelegation> delegate;
-(void) someMethod;
-(void)anotherMethod:(NSString *)str
@end
在 aaa.h 宣告了一個叫做 myDelegation 的 protocol(協定) 出來並定義兩個方法 delegationPrint 以及 delegationLog:

其中 delegationPrint 屬於 @require,表示若有其他類別採納了 myDelegation 這個protocol則"一定"要實現 delegationPrint 方法,否則編譯器會出現警告,反之 @option 則可以不實現。

再來宣告並讓編譯器自動合成一個 "採納 myDelegation" 的泛型物件叫做 delegate,且屬性為 weak及nonatomic 其中 weak 為必要,避免物件相互持有無法釋放造成 memory leak。

**採納myDelegation的泛型物件名稱為增加閱讀性,一般都固定名稱為 delegate,不建議採取其他名稱。

**唯有採納 myDelegation 的物件可以呼叫 @protocol 所定義的方法(method)。

程式流程怎麼跑用 log 來看最清楚不過了,所以在每個 Method 都加了 NSLog 方便大家做 tracking.

假設今天有個類別 bbb 採納了 aaa 的 protocol 則程式片段應如下。


//aaa.m
-(void) someMethod {
         //確保 delegate 不為空且可回應 delegatePrint Method
     if(self.delegate != nil && [self.delegate delegatePrint]){
         NSLog(@"aaa.m someMethod exec");
         [self.delegate delegatePrint];
     }
}

-(void)anotherMethod:(NSString *)str {
     if(self.delegate != nil && [self.delegate delegateLog:]){
        NSLog(@"aaa.m anotherMethod exec");
        NSLog(@"str = %@ and return = %d",str, [self.delegate delegateLog:123]);
     } 
}


// bbb.h
#import "aaa.h"
@interface bbb:UIVierController <myDelegation>
@end



//bbb.m
@implementation ViewController
-(void)viewDidLoad {
      aaa *myAAA = [aaa new];
      myAAA.delegate = self;
      [myAAA someMethod];
      [myAAA anotherMethod:@"Hello World!"];
}
-(void)delegationPrint{     NSLog(@" bbb.m delegationPrint exec"); } -(int)delegationLog:(int) iVal{     NSLog(@"bbb.m delegationLog exec");     NSLog(@"bbb.m delegationLog: %d",iVal);     return iVal*10; }
@end
程式執行的結果輸出如下

aaa.m someMethod exec

bbb.m delegationPrint exec

aaa.m anotherMethod exec

bbb.m delegationLog exec

bbb.m delegationLog:123

str = HelloWorld! and return = 1230


以上程式碼片段是人腦 coding + 人腦 compiler,若是有錯誤的地方還請大家指正。




2013年6月25日 星期二

FMDB 隨手記



原址:
http://bonjouryentinglai.wordpress.com/2011/03/20/fmdb%EF%BC%9A%E6%88%91%E7%9A%84sqlite%E6%95%91%E6%98%9F/

         //需加入 imageIO.framework & MessageUI.framework

-開啟/關閉資料庫
使用資料庫的第一件事,就是建立一個資料庫。要注意的是,在iOS環境下,只有document directory 是可以進行讀寫的。在寫程式時用的那個Resource資料夾底下的東西都是read-only。因此,建立的資料庫要放在document 資料夾下。方法如下:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentDirectory stringByAppendingPathComponent:@"MyDatabase.db"];
FMDatabase
*db = [FMDatabase databaseWithPath:dbPath] ;
if (![db open]) {
NSLog(@“Could not open db.”);
return ;
}
通常這段程式碼會放在UIViewController中viewDidLoad的函式裡。指定路徑後,用[FMDatabase databaseWithPath:]回傳一個FMDatabase物件,如果該路徑本來沒有檔案,會新增檔案,不然會開啟舊檔。最後呼叫[db open]可以開啟該資料庫檔案,[db close]則關閉該檔案。
-建立table
如果是新建的資料庫檔,一開始是沒有table的。建立table的方式很簡單:
[db executeUpdate:@"CREATE TABLE PersonList (Name text, Age integer, Sex integer, Phone text, Address text, Photo blob)"];
這是FMDB裡很常用的指令,[FMDatabase_object executeUpdate:]後面用NSString塞入SQLite語法,就解決了。因為這篇主要是在講FMDB,所以SQLite的語法就不多說了,上述程式碼建立了一個名為PersonList的table,裡面有姓名、年齡、性別、電話、地址和照片。(嗯….很範例的一個table)
-插入資料
插入資料跟前面一樣,用executeUpdate後面加語法就可以了。比較不同的是,因為插入的資料會跟Objective-C的變數有關,所以在string裡使用?號來代表這些變數。
[db executeUpdate:@"INSERT INTO PersonList (Name, Age, Sex, Phone, Address, Photo) VALUES (?,?,?,?,?,?)",
@"Jone", [NSNumber numberWithInt:20], [NSNumber numberWithInt:0], @“091234567”, @“Taiwan, R.O.C”, [NSData dataWithContentsOfFile: filepath]];
其中,在SQLite中的text對應到的是NSString,integer對應NSNumber,blob則是NSData。該做的轉換FMDB都做好了,只要了解SQLite語法,應該沒有什麼問題才是。
-更新資料
太簡單了,不想講,請看範例:
[db executeUpdate:@"UPDATE PersonList SET Age = ? WHERE Name = ?",[NSNumber numberWithInt:30],@“John”];
-取得資料
取得特定的資料,則需使用FMResultSet物件接收傳回的內容:
FMResultSet *rs = [db executeQuery:@"SELECT Name, Age, FROM PersonList"];
while ([rs next]) {
NSString *name = [rs stringForColumn:@"Name"];
int age = [rs intForColumn:@"Age"];
}
[rs close];
用[rs next]可以輪詢query回來的資料,每一次的next可以得到一個row裡對應的數值,並用[rs stringForColumn:]或[rs intForColumn:]等方法把值轉成Object-C的型態。取用完資料後則用[rs close]把結果關閉。
-快速取得資料
在有些時候,只會query某一個row裡特定的一個數值(比方只是要找John的年齡),FMDB提供了幾個比較簡便的方法。這些方法定義在FMDatabaseAdditions.h,如果要使用,記得先import進來。
//找地址
NSString *address = [db stringForQuery:@"SELECT Address FROM PersonList WHERE Name = ?",@"John”];
//找年齡
int age = [db intForQuery:@"SELECT Age FROM PersonList WHERE Name = ?",@"John”];
大概就是這樣囉~對於在Objective-C上使用SQLite有困難的朋友,看完之後是不是覺得一切都變的很簡單呢

2013年5月9日 星期四

Invalid binary (uniqueIdentifier)

這兩天送審新的 App 被 Apple 以我有呼叫非法的 API 而 rejected。

隨後又寄了封信告訴我是不可以使用 UDID,但我本身的 Code 早就已經全改成 UUID 囉。

所以應該是第三方的套件造成的問題。

有這個問題的捧友可以打開 terminal 切到專案目錄下執行 grep -Rnis 'uniqueIdentifier' *

這會列出所有包含 uniqueIdentifier 關鍵字的檔案。

以我的例子來說他會告訴我在 libadon.a 裡發現關鍵字,這是國內廣告商 VPon 的檔案。

我隨即去電詢問 Vpon,但他們一再跟我保證他們的 SDK 沒有問題,他們已經將相關的程式註解掉了。

但是問題還是沒解決,只好反覆再交叉測試,最後發現原來是 ADMOB 搞得鬼。

但令我耐悶的是 grep 並沒有回我 admob 裡有這個關鍵字,以致於我一開始方向就錯了。

把 Admob 的 SDK 從 6.2.1 更新到 6.4.1 就沒問題了。

但這裡要注意的是,若你把 6.2.1 檔案砍掉重新加入 6.4.1 還是沒解決問題的話。

請到 target -> build setting 搜尋 search paths ,再找到 library search paths 把裡面所有舊的 6.2.1 的路徑全都刪了,應該就可以了。

為了解這問題花了我一個早上呀。。。希望有幫到各位



參考網址

http://stackoverflow.com/questions/16409966/app-rejected-but-i-dont-use-udid

https://groups.google.com/forum/?fromgroups=#!topic/google-admob-ads-sdk/G7zIDyRTnJs



2013年2月21日 星期四

In App Purchase 筆記 - test user & Cannot connect to iTunes Store

寫 Blog 防老年癡呆

最近在做 In App Purchase 的功能,雖然之前有做過,但大概隔了一年之久,也忘的差不多

了,把舊的 Code 再拿出來復習了一下,發現 iOS 5 之後有些小變動,雖然影響不大,但這

次卻卡在其他的關卡許久,花了我一天半的時間才全部搞定。

首先是卡在 Test User 帳號問題,要測試 In App 一定要先申請 test user,直接到 iTunes

connect 裡的 Manager User 去申請即可。只有兩個重點要記下來。

1. 申請完後千萬不要在手機入的 設定->iTunes&App Stores 裡登入這個 Test User 帳號。

2.千萬別用這帳號去下載 App store 裡的任何 App

另一個卡關的地方是在寫好 In App Purchase 的 Code 後,無論我如何點擊,Apple 總是會回我 Error Domain=SKErrorDomain Code=0 "Cannot connect to iTunes 
Store" UserInfo=0x1f578f50 {NSLocalizedDescription=Cannot connect to iTunes Store}

無論在我一再的確認 Bundle ID 或是 重置我手機的網路,砍掉App重裝還是電腦整個重開機後都是一樣的結果。後來解決的方法也很瞎,原來只是 Target 裡的 version 和 build 沒有設好,請參考 http://stackoverflow.com/questions/2359739/iphone-store-kit-cannot-connect-to-itunes-store  一直往下捲,有圖的那個解答才是Work的。


就這樣。



2013年1月21日 星期一

Mysql 筆記 - Query Cache

進入 mysql CMD

# mysql -u root -p


設置 Query Cache

編輯 /etc/my.cnf 中的 [mysqld]區段

加入 query_cache_size = 64M

64M為建議預設值,最小值為 40K

restart mysqld
/etc/init.d/mysqld restart


查詢 Query Cache 狀態
mysql> show status like ‘%Qcache%’;
+————————-+———+
| Variable_name | Value |
+————————-+———+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 2086936 |
| Qcache_hits | 1 |Cache命中率增加1
| Qcache_inserts | 1 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 1 |
| Qcache_total_blocks | 4 |
+————————-+———+
8 rows in set (0.00 sec)

重整 Query Cache Memory leak
mysql> flush query cache;

清空 Query Cache Memory
mysql> reset query cache;

2013年1月20日 星期日

台灣匯率王 - 簡單好用又能幫你賺錢的最佳 App!

台灣匯率王自上架以來,始終以快速、簡單、直覺的操作以及即時、準確、在地化的匯率為宗旨為所有人帶來便利的匯率轉換工具,也在 iOS App Store 上累積超過十萬次下載,每天的活躍使用者達八千人次。

如今,堤刻科技為感謝大家一直以來的支持,決定將台灣匯率王再次注入新的靈魂並正式進入 2.0 版!以下將為大家介紹 2.0 版的四大功能。


首先,台灣銀行這次採用與 Facebook 相同的 Slide Menu 界面,分成左中右三個頁面,左邊為 Menu 頁,中間為主要視窗頁,右邊固定為目前手機內的匯率(最後一次更新的台灣銀行線上牌告匯率,主要方便離線使用),第一次進入程式時會提醒各位。



向右滑動開啟功能選單,往左滑動開啟匯率總覽




功能一 簡單快速的匯率轉換 

匯率轉換的功能採用台灣銀行牌告匯率並可隨時更新匯率並保存在手機內。

請注意,若您是點選台幣輸入,則會自動幫你轉換所選的外幣金額,滾動下方的選項會自動

將你輸入的台幣金額轉換成該幣別,反之,若您是輸入外幣金額,則會自動幫您轉換成台

幣,滾動下方的選項會自動將你輸入的外幣金額全數換成台幣。


功能二 匯率到價通知  

若您近期或長期在觀察匯率的漲跌,一定個煩惱,當我出門在外時沒辦法一直盯著電腦看,若匯率到達我設定的買賣價時該怎麼辦呢?

匯率到價通知的功能便是幫您解決這個困擾。

只要您預先設定好您的目標價,匯率王便會以每二分鐘的頻率幫您做檢查。

若達到您設定的目標價便會以推播通知您,目前台灣匯率王支援的推播通知幣包含現金、即期、黃金匯率多達29種可以設定。

推播通知可利用 In-App 的方式來延長您的使用期限,三個月只要 NT 30,六個月 NT 60,一年只要 NT 90 唷,很划算吧,還望大家多多支援。
匯率觸價通知

第一次使用需先建立您的推播清單帳號,只要設定一次,在任何iDevice都可以同時登入使用。

多達29種的幣別可設定
您設定的推播通知清單列表。

透過 In App 來延長您推播通知的期限 


功能三 匯率比較

匯率王收集了市面上常見的18家銀行共17種幣別的匯率資訊(目前設定每五分鐘更新一次)。

讓大家隨時了解各家銀行的匯率,也可以依買賣價高低做排序。
多達18家銀行,17種幣別

功能四 歷史兌換

常出國的朋友們應該都和我一樣會有個小困擾,外幣換好了,拿去國外要消費時常常會在腦中算這個商品換成台幣是多少錢,到底是貴還是便宜?

您可以使用我們一般的匯率換算功能,但現在您還有個更好的選擇就是歷史兌換功能。

只要在您出國前設定好您用多少台幣換了多少外幣,到了國外就可以利用實際兌換的匯率(包含手續費之類的支出)馬上知道您到底是用了多少台幣買了這項商品,還可以輸入退稅的 % 數幫你換算哦。


先設定好您實際支出台幣金額和實際收入的外幣金額

點選設定好的匯率,可即時換算該商品的價值,也可以輸入退稅的%數幫您算哦
















2013年1月2日 星期三

#備忘 挑選 XMPP 伺服器

原文 http://stackoverflow.com/questions/10387205/recommended-ec2-instance-size-for-an-ejabberd-cluster


大意:

AWS EC2 至少要選 Large (7.5G memory, 可服務100k+)以上等級。

memory  > CPU。

jabber.org 使用了 2.7G的 memory 服務 10k+ 的使用者。

2013年1月1日 星期二

AWS EC2 iptables 小陷阱

最近將伺服器漸漸移到 AMAZON 上面去,大至上沒什麼問題,但 Reboot 後卻怎樣都連不上伺服器,連 telnet/ping 全都回我 time out!! 令我傻眼。。。

後來到AMAZON的論壇上求救後發現是伺服器 iptables 設定的問題!

後來救回來之後仔細了研究一下 iptables 的寫法。

原來 iptables-save 這個鬼東西並不會真正的幫你把 iptables rule 儲存起來,restart後還是會消失掉。

正解是當你設定好所有的 iptables rule 後要下一行


iptables-save > /etc/sysconfig/iptables


這樣 iptables 後才會真正的存起來,restart 後也不會消失!