2013年12月10日 星期二

iOS 開發筆記 - ARC forbids explicit message send of 'dealloc'

最近我也投入 ARC 開發模式 XD 再也不去管到底要不要對哪個 object 執行 release 的議題,這適應其實也不難,就真的不要寫 [object release] 即可 XD

但有些時候還是要在物件銷毀時去做一些處理,例如解決或等待相關的事件完成,就常常會寫這段:

- (void)dealloc
{
    // do something ...
    [super dealloc];

}


結果,Xcode 就噴這段訊息:

ARC forbids explicit message send of 'dealloc'

Transitioning to ARC Release Notes 文件查證:
You may implement a dealloc method if you need to manage resources other than releasing instance variables. You do not have to (indeed you cannot) release instance variables, but you may need to invoke [systemClassInstance setDelegate:nil] on system classes and other code that isn’t compiled using ARC. 
Custom dealloc methods in ARC do not require a call to [super dealloc] (it actually results in a compiler error). The chaining to super is automated and enforced by the compiler.

僅需改成這樣了:

- (void)dealloc
{
    // do something ...
}

iOS 開發筆記 - 使用 NSNotification 傳遞與接收事件

通常會設計多個 UIViewController 供使用者觀看與操作,如果 AUIViewController 提供設定參數後,當使用者切換到 BUIViewController 可以馬上就使用到新資料來操作時,這時可以把資料儲存在指定地方,當 BUIViewController 之 viewWillAppear 裡就能夠去重讀資料來用。

然而,有時希望 AUIViewController 改完資料時,可以馬上讓 BUIViewController 也得知,這時做法至少有兩種,第一種就是讓 AUIViewController 可以接觸到 BUIViewController ,例如把 BUIViewController 當作一個 member variable ,也就是 AUIViewController.member = BUIViewController object,只是這招有點破壞架構的美感,但偶爾使用還滿堪用的!第二種,就是採用 Notification 架構,讓 BUIViewController 聆聽專屬於它的事件,而 AUIViewController 在需要的時候送事件出去即可,此架構更加符合 async 。

此例筆記 NSNotification 的用法(此架構也稱 radio station,Android 派則是 broadcast 啦):

BUIViewController.h:

#define kTargetDataUpdate @"TargetDataUpdate"

BUIViewController.m 之註冊聆聽事件:

- (void)setupOptions:(NSNotification *)event
{
        NSLog(@"event:%@, object:%@", event, event.object);
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupOptions:) name:kTargetDataUpdate object:nil];

}


BUIViewController.m 之移除聆聽事件:

- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name: kTargetDataUpdate object:nil];
    [super viewWillDisappear:animated];
}


AUIViewController (或其他物件亦可) 之發送事件:

NSDictionary *info = @{@"status":@"ok"};
[[NSNotificationCenter defaultCenter] postNotificationName: kTargetDataUpdate object:info userInfo:nil];

2013年12月9日 星期一

iOS 開發筆記 - Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

AttriubuteTableViewCellIndentifier DocumentOutlineTableViewCell

最近摸 Storyboard 一陣子,偶爾還會犯一些傻 bug ,就是 Identifier 設錯地方 XD 例如跑去 Identity Inspector 的 Identity 之 Restoration ID :

IndentityTableViewCellRestorationID

結果程式一跑時,就噴訊息了:

** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

解法就是開啟 storyboard 後,仔細查看左邊的 Document Outline 之 UITableViewController 裡的 Table View Cell 是不是有被標記起來,接著去看看右邊 Attributes Inspector 顯示的 Identifier 是不是跟程式碼一樣的,如此就能 debug 啦。

忘記是不是 iOS 5 SDK 開始,預設的 UITableViewController 的範例程式都是寫成綁定在 Interface builder (xib) / Storyboard 的寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


早期 iOS 4 SDK 預設 UITableViewController 之 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}


早期 Interface builder (xib) 的用法,須寫程式註冊 Nib:

- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:@“MyCell" bundle:nil]
forCellReuseIdentifier:@“myCell”]; // "MyCell" for MyCell.xib
}

2013年12月3日 星期二

iOS 開發筆記 - 解決 Storyboard UITableViewCell contentView 第一次設定 subViews frame 失效問題

為了讓自己趕快習慣 Storyboard 開發模式,瘋狂地用 Storyboard 做事 XD 除了降低原本的開發速度外,接著碰到一些好像是 bug 的奇妙問題?

這個問題是在 Storyboard 內已經拉好一個 UITableViewController ,接著在裡頭增加 UITableViewCell 後,再上頭加多個 ImageView 把玩,然而,希望 device rotation 後能後平均地顯示在 UITableViewCell 裡頭,例如有五個 ImageView 可以平均地分布。

此時在 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 已經完成重新計算各個 ImageView 的 frame 座標。

由於 Storyboard 是以 Portrait 模式設計,因此裝置一開始從 Portrait 轉換到 Landscape 時,一切都正常顯示。但如果裝置一開始就是在 Landscape 模式時,反而那些 ImageView 卻還是依照 Storyboard 裡的 Portrait 模式設計,無論我把他們的 frame 座標更新也無用。(但經過 device rotation 後,會顯示正常)

不曉得這是不是 bug ?總之,最後就想到一招就是第一次強制 ImageView 重新歸位 XD 希望這招之後可以拿掉。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        // ...

        if( cell.tag == 0 )
        {
                cell.tag = 1;
                for (UIView *v in [cell.contentView subviews])
                {
                        [v removeFromSuperview];
                        [cell.contentView addSubview:v];
                }
        }
        return cell;
}

iOS 開發筆記 - iOS app 狀態為 Pending Contract 的處理

通常看到這樣的問題,都是忘了填寫銀行等資訊,不過,若發生在免費的 iOS app 時,這時就請打電話給客服聊聊天吧(Worldwide Telephone Support,台灣打 0800-022-237 )

此例是登入 iTunesConnect 後,上頭一直顯示 program membership expired 訊息,但明明我幾個月前已經 renew 過了 :P 同理切換到 Contracts, Tax, Banking 裡頭,也叫我去 re-new 開發者帳號...打電話問客服...報一下 Apple ID (Email address) 後,得知是當初 re-new 時少完成一個流程(可能是少了某表單的同意,但現況又看不到表單),導致在 iOS Developer Center 是 OK ,但在 iTunesConnect 是不 OK 的現象,解法就是聊個天就解掉了...