顯示具有 IOS開發 標籤的文章。 顯示所有文章
顯示具有 IOS開發 標籤的文章。 顯示所有文章

2012年9月4日 星期二

iOS 應用程式的沙箱機制


在操作的過程中經常需要取得目前應用程式的資料夾路徑,接下來就簡略地介紹 iOS 的應用程式沙箱機制,以及如何取得沙箱中的資料夾路徑。
在 iOS 系統中,出自於安全性的前提下,應用程式採用了沙箱 (Sandbox) 安全體系的運作機制,當應用程式被安裝到 iOS 系統時,系統會替應用程式建立一個主目錄 (Home Directory),以應用程式的 GUID 來命名,這個主目錄便是應用程式本身的 Sandbox,應用程式被限制僅能存取自己的 Sandbox,無法任意地使用其他應用程式 Sandbox 中的資料,如有需要時必須發送數據請求並經過權限的檢測認可,無法通過檢測則請求會被中斷。

沙箱 (Sandbox) 機制的幾項特點

  • 每個應用程式都有屬於自己的儲存空間
  • 應用程式無法存取其他應用程式的儲存空間
  • 應用程式送出的請求數據必須通過權限的檢測,若無法通過時則請求會被中斷。

沙箱結構

一般而言,沙箱中會有下列四個子資料夾:
²  AppName.app
應用程式主目錄,所有與應用程式建置時有關的檔案都存放於此資料夾中,包括應用程式本身的執行檔及相關資源等等,僅允許讀取不可修改。又稱應用程式的程式包。由於應用程式在安裝時必須經過簽名認證,所以在運行時不能對這個資料夾中進行任何內容的修改動作。
²  Documents 
這個資料夾用來儲存使用者的資料或其他應定期備份的資料,因此在應用程式運行時需要使用的檔案都應該被儲存於這個資料夾中
²  Library
此資料夾中包含兩個子目錄,分別是:
Ø   Caches 
用來儲存應用程式專用的文件,例如保存應用程式再次啟動時需要的快取資料
Ø   Preference 
儲存應用程序的偏好設置文件,應用程式不應該直接於此資料夾創建偏好設定文件,而是透過 NSUserDefault 類別來取得與進行偏好設定文件的操作
²  tmp 
用來儲放臨時檔案的資料夾,保存應用程式再次啟動時較不需要的快取資料。當 iOS 裝置與 iTuens 進行同步時,iTunes 不會將存放於 tmp 資料夾中的檔案進行備份。
若是應用程式中使用了 tmp 資料夾做些檔案的暫存動作,務必記得自行清除不再使用的檔案,避免 tmp 資料夾中的垃圾檔案佔據了使用者的儲存空間。

如何取得沙箱中的資料夾路徑

前面已經簡單地介紹完 iOS 沙箱機制,接下來的重點便是我們要如何去取得沙箱中的資料夾路徑,畢竟操作檔案文件前的先決條件是我們必須得知道檔案路徑在哪裡,才有辦法進行後續的存取動作。
    // 取得應用程式根目錄資料夾路徑
    NSString *homeDirectory = NSHomeDirectory();

    // 取得 AppName.app 程式包路徑
    // - 直接從 Info.plist 屬性清單中 "NSBundleInitialPath" 鍵值直接取得應用程式的程式包路徑
    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];     // 取得 Info.plist 屬性清單
    NSString *appFilePath = [infoDictionary objectForKey:@"NSBundleInitialPath"];

    // 取得 Documents 資料夾路徑
    NSString *documentsDirectory = [homeDirectory stringByAppendingPathComponent:@"Documents"];

    // 取得 Library 資料夾路徑
    NSString *libraryDirectory = [homeDirectory stringByAppendingPathComponent:@"Library"];

    // 取得 Caches 與 Preference 資料夾路徑
    NSString *cachesDirectory = [libraryDirectory stringByAppendingPathComponent:@"Caches"];
    NSString *preferenceDirectory = [libraryDirectory stringByAppendingPathComponent:@"Preference"];

    // 取得 tmp 資料夾路徑
    NSString *tmpDirectory = [homeDirectory stringByAppendingPathComponent:@"tmp"];

2012年8月14日 星期二

申請iOS Developer Program 的帳號



可以參考這篇:







我們目前是用個人的開發帳號,若要申請公司或機關帳號,Apple 會要求你提供有公司英文名稱的法律文件,若沒有可以用以下a+b取代。

a.營利事業登記證(我是附公司設立登記表)
b.自己翻譯的公司英文名稱加蓋發票章就可以了。

 PS. 若是政府機關,應該要傳真的是「政府營利事業登記證」。

以上的文件要用傳真到國外去喔!


2012年8月10日 星期五

UIViewController的生命週期 - Part2



資料來源:

UIViewController的生命週期及iOS程式執行順序



當一個視圖控制器被創建,並在螢幕上顯示的時候。 代碼的執行順序


alloc
創建對象,分配空間
init (initWithNibName)
初始化物件,初始化資料
loadView
nib載入視圖 ,通常這一步不需要去干涉。除非你沒有使用xib檔創建視圖
viewDidLoad
載入完成,可以進行自訂資料以及動態創建其他控制項
viewWillAppear
視圖將出現在螢幕之前,馬上這個視圖就會被展現在螢幕上了
viewDidAppear
視圖已在螢幕上渲染完成

當一個視圖被移除螢幕並且銷毀的時候的執行順序,這個順序差不多和上面的相反


viewWillDisappear
視圖將被從螢幕上移除之前執行
viewDidDisappear
視圖已經被從螢幕上移除,使用者看不到這個視圖了
dealloc
視圖被銷毀,此處需要對你在initviewDidLoad中創建的物件進行釋放

關於viewDidUnload :在發生記憶體警告的時候如果本視圖不是當前螢幕上正在顯示的視圖的話 viewDidUnload將會被執行,本視圖的所有子視圖將被銷毀,以釋放記憶體,此時開發者需要手動對viewLoadviewDidLoad中創建的物件釋放記憶體。 因為當這個視圖再次顯示在螢幕上的時候,viewLoadviewDidLoad 再次被調用,以便再次構造視圖。
 當我們創建一個UIViewController類的物件時,通常系統會生成幾個預設的方法,這些方法大多與視圖的調用有關,但是在視圖調用時,這些方法的調用順序如何,需要整理下。
 通常上述方法包括如下幾種,這些方法都是UIViewController類的方法:

- (void)viewDidLoad

- (void)viewDidUnload

- (void)viewWillAppear:(BOOL)animated

- (void)viewDidAppear:(BOOL)animated

- (void)viewWillDisappear:(BOOL)animated

- (void)viewDidDisappear:(BOOL)animated



下面介紹下APP在運行時的調用順序。

1- (void)viewDidLoad

      一個APP在載入時會先通過調用loadView方法或者載入IB中創建的初始介面的方法,將視圖載入到記憶體中。然後會調用viewDidLoad方法來進行進一步的設置。通常,我們對於各種初始資料的載入,初始設定等很多內容,都會在這個方法中實現,所以這個方法是一個很常用,很重要的方法。

      但是要注意,這個方法只會在APP剛開始載入的時候調用一次,以後都不會再調用它了,所以只能用來做初始設置。

2) - (void)viewDidUnload;

      在記憶體足夠的情況下,軟體的視圖通常會一直保存在記憶體中,但是如果記憶體不夠,一些沒有正在顯示的viewcontroller就會收到記憶體不夠的警告,然後就會釋放自己擁有的視圖,以達到釋放記憶體的目的。但是系統只會釋放記憶體,並不會釋放物件的所有權,所以通常我們需要在這裡將不需要在記憶體中保留的物件釋放所有權,也就是將其指針置為nil

      這個方法通常並不會在視圖變換的時候被調用,而只會在系統退出或者收到記憶體警告的時候才會被調用。但是由於我們需要保證在收到記憶體警告的時候能夠對其作出反應,所以這個方法通常我們都需要去實現。

      另外,即使在設備上按了Home鍵之後,系統也不一定會調用這個方法,因為IOS4之後,系統允許將APP在後臺掛起,並將其繼續滯留在記憶體中,因此,viewcontroller並不會調用這個方法來清除記憶體。

3- (void)viewWillAppear:(BOOL)animated;

      系統在載入所有資料後,將會在螢幕上顯示視圖,這時會先調用這個方法。通常我們會利用這個方法,對即將顯示的視圖做進一步的設置。例如,我們可以利用這個方法來設置設備不同方向時該如何顯示。

      另外一方面,APP有多個視圖時,在視圖間切換時,並不會再次載入viewDidLoad方法,所以如果在調入視圖時,需要對資料做更新,就只能在這個方法內實現了。所以這個方法也非常常用。

4) - (void)viewDidAppear:(BOOL)animated

      有時候,由於一些特殊的原因,我們不能在viewWillApper方法裡,對視圖進行更新。那麼可以重寫這個方法,在這裡對正在顯示的視圖進行進一步的設置。

5) - (void)viewWillDisappear:(BOOL)animated

      在視圖變換時,當前視圖在即將被移除、或者被覆蓋時,會調用這個方法進行一些善後的處理和設置。

      由於在IOS4之後,系統允許將APP在後臺掛起,所以在按了Home鍵之後,系統並不會調用這個方法,因為就這個APP本身而言,APP顯示的view,仍是掛起時候的view,所以並不會調用這個方法。

6) - (void)viewDidDisappear:(BOOL)animated

      我們可以重寫這個方法,對已經消失,或者被覆蓋,或者已經隱藏了的視圖做一些其他操作。


IOS 開發 loadView viewDidLoad 的區別


iPhone開發必不可少的要用到這兩個方法。 他們都可以用來在視圖載入的時候,初始化一些內容。 但是他們有什麼區別呢?

viewDidLoad 此方法只有當viewnib檔初始化的時候才被調用。

loadView 此方法在控制器的viewnil的時候被調用。 此方法用於以程式設計的方式創建view的時候用到。 如:

1.
 2.- ( void ) loadView {
 3.    UIView *view = [ [ UIView alloc] initWithFrame:[ UIScreen
 4.mainScreen] .applicationFrame] ;
 5.    [ view setBackgroundColor:_color] ;
 6.    self.view = view;
 7.    [ view release] ;
 8.}
 9.

你在控制器中實現了loadView方法,那麼你可能會在應用運行的某個時候被記憶體管理控制調用。 如果設備記憶體不足的時候, view 控制器會收到didReceiveMemoryWarning的消息。 默認的實現是檢查當前控制器的view是否在使用。如果它的view不在當前正在使用的view hierarchy裡面,且你的控制器實現了loadView方法,那麼這個view將被release, loadView方法將被再次調用來創建一個新的view



 .

UIViewController的生命週期 - Part1



資料來源:http://w11h22j33.iteye.com/blog/1565210

         UIViewControl是IOS程式中的一個重要組成部分,扮演者一個大管家的身份,管理著程式中的眾多視圖,今天看看了官方文檔並做了如下一些簡單的記錄:

        何時載入view,載入的原則是什麼,視圖何時消失等問題,文檔中講的都很詳細。Controller的view最好在需要顯示時再去載入,並且在系統發出記憶體警告時釋放比必要的view及相關的資料物件。

一、UIViewController的初始化

  初始化時會根據需要調用init,initWithCoder等相關函數,這個時候我們可以做一下簡單的初始化操作,建立ViewController中需要使用的資料模型等,不建議在初始化階段就直接創建view及其他與顯示有關的物件(應該放到loadView的時候去創建,或者採用懶載入的方法創建)。
  我們都知道ViewController可以通過代碼和xib兩種方式創建,這兩種方式的初始化流程也不盡相同。

  1)使用xib創建的VC

  xib其實最終是會把我們的設置保存成一個資料集,當需要初始化構建VC的時候,回去讀取記錄的資料集,然後幫我們動態的創建VC,因此可以想像它在初始化時會先去找看是否實現initWithCoder方法,如果該類實現了該方法,就直接調用initWithCoder方法創建物件,如果沒有實現的話就調用init方法。調用完初始化方法以後緊接著會調用awakeFromNib方法,在這個方法裡面我們可以做進一步的初始化操作。

  2)使用代碼創建VC

  使用代碼創建時,我們根據需要手動的創建VC中的資料,如果自己定制VC時,還需要在init中調用[super init]。

二、UIViewController中View的load和unload

  前面講了不建議在VC初始化的時候就創建view及其他與顯示相關的代碼,官方文檔建議將View的初始化操作放到loadView的時候再做,當VC接到記憶體告警時會調用didRecieveMemoryWarning這個時候我們就要做出回應,釋放暫時不需要的物件。如果無視這個警告,系統記憶體不夠用時會會繼續發送,如果還得不到處理就會強制退出程式。下面看具體的loadView和unloadView時候都會做什麼操作。

  1)Load週期


  當需要顯示或者訪問view屬性時,view沒有創建的話,VC就會調用loadView方法,在這個時候會創建一個view並將其賦給VC.view屬性。緊接著就會調用VC的viewDidLoad方法,這個時候VC.view保證是有值的,可以做進一步的初始化操作,例如添加一些subview。注意:定制VC時,如果覆蓋loadView方法,不需要調用[super loadView]方法。

  2)Unload週期



  當app收到記憶體警告的時候,會調用每一個VC的didRecieveMemoryWarning方法,我們需要做出回應,釋放程式中暫時不需要的資源。通常都會重寫該方法,重寫時候需要調用super的該方法。如果檢測到當前VC的view可以被安全釋放的話,就會調用viewWillUnload方法,這個我們必須要重視,因為當VC的view消失時候它的subviews可能會被一起釋放,我們需要根據具體情況做一些記錄,以保證下次能夠正確創建,同時不出現記憶體洩漏。調用viewWillUnload以後,會將VC.view屬性設置成nil,然後在調用viewDidUnload方法,這個時候我們可以釋放那些強引用的物件。

官方文檔:The View Controller Life Cycle

2012年7月24日 星期二

iPhone App 應用程式的生命週期


主題: iPhone App 應用程式的生命週期


心得:

l   今天拿到Android App開發基礎班的課程表,發現有一個section特別介紹Android App的生命週期,於是我也去網路搜尋IOS App相關的生命週期資訊,如下面二圖描述非常詳細。

l   我有使用一種月曆記事的app,通常我有設密碼,只要app被置於背景(非關閉App)後再被開啟,都會要求我重新輸入密碼,這樣的功能就可以寫在Application Delegate 派遺以下兩個方法applicationWillEnterForeground 以及 applicationDidBecomeActive,我可以在程式裡面去撰寫相對應的 Event


l   超重要的發現:
n   當應用程式在執行時突然有來電或是簡訊時,這個時後應用程式會受到干擾而中斷,這時後applicationWillResignActive: 會被呼叫。
n   如果使用者忽略這個中斷事件時,則會呼叫 applicationWillBecomeActive 這個方法來恢復程式的狀態。否則應用程式會進入背景狀態,進而呼叫applicationDidEnterBackground:
n   要特別注意的是,我應該在applicationWillResignActive: 妥善保存執行時的狀態,然後在applicationWillBecomeActive: 恢復應用程式的狀態。



2012年7月23日 星期一

實作 IOS 定位服務

剛玩定位這玩意,參考了好幾位大師的範例和說明,然後再實作、整理出來的筆記。

參考IOS 定位範例:


1.       iPhone開發實戰定位與地圖


2.       GPS 衛星定位展示範例



3.       Map Kit (part3)





實作IOS定位服務


關於定位服務,IOS中已經提供二個framwork給開發者使用:

1.          CoreLocation.framework

n   (CLLocationManager類別)定位-獲得經緯度。

2.      MapKit.framework

n   (MKReverseGeocoder 類別)當前的經緯度所對應的地理位置資訊是什麼。

n   (MKAnnotationView類別)把取到的經緯度資訊顯示到地圖上



Xcode新建專案:

.h檔裡要宣告這是一個符合MKMapViewDelegate, MKReverseGeocoderDelegate, CLLocationManagerDelegate Protocal的類別。

1. MKMapViewDelegate:委派代理地圖呈現

2. MKReverseGeocoderDelegate:委派理取得地理位置資訊

3. CLLocationManagerDelegate:委派代理取得當前的經緯度。



LocationViewController.h

#import <UIKit/UIKit.h>

#import <CoreLocation/CoreLocation.h>

#import <CoreLocation/CLLocationManagerDelegate.h>

#import <MapKit/MapKit.h>

#import <MapKit/MKReverseGeocoder.h>

@interface LocationViewController : UIViewController<MKMapViewDelegate, MKReverseGeocoderDelegate, CLLocationManagerDelegate> {

    CLLocationManager   *locmanager;

    MKMapView *mapView;

}

@end

.m檔裡要實作定位服務的功能,記得將delegate設為self,程式碼如下:

LocationViewController.m

#import "LocationViewController.h"

#import "DemoAnnotation.h"

@interface LocationViewController ()

@end


@implementation LocationViewController

- (void)viewDidLoad {

    [super viewDidLoad];

// 加入MKMapView 的物件,可呈現地圖上的所有資訊。

    mapView=[[MKMapView alloc] initWithFrame:self.view.frame];

    mapView.delegate=self;

    [self.view insertSubview:mapView atIndex:0];

    // CLLocationManager 物件,startUpdatingLocation 可以

    locmanager = [[CLLocationManager alloc] init];

        [locmanager setDelegate:self];

        [locmanager setDesiredAccuracy:kCLLocationAccuracyBest];        

    //透過呼叫startUpdatingLocation開啟定位功能,然後使用stopUpdatingLocation停止定位,其中定位資訊是通過loctionManager:didUpdateToLocation

        [locmanager startUpdatingLocation];

}


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation

{

      // newLocation是新偵測到的地理位置,針對此地設定區域範圍

            MKCoordinateRegion region;

            region.center=newLocation.coordinate;

            //使用 Span 設定地圖的縮放。

            MKCoordinateSpan span;

            span.latitudeDelta=.005;

            span.longitudeDelta=.005;

            region.span=span;

            [mapView setRegion:region animated:TRUE];

    // DemoAnnotation是繼承MKAnnotation的自訂類別,稍後會提到,可以記錄關於此座標點的相關資訊,像主題及副標題。

    DemoAnnotation *annotation = [[DemoAnnotation alloc] initWithCoordinate:newLocation.coordinate];

        // 將座標資訊加入地圖

    [mapView addAnnotation:annotation];

    //

    // 若不想用自訂的格式可以直接使用MKReverseGeocoder類別,如下:

    MKReverseGeocoder *geocoder=[[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];

            geocoder.delegate=self;

            [geocoder start];

    */

}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {

}

這裡先補充newLocation表示最新定位,oldLocation表示上一次的定位資訊。這兩個都是CLLocation對象。以下是CLLocation的屬性說明:

altitude 海拔高度
coordinate
經緯度
course
行駛方向
horizontalAccuracy
水準方向的精確度
Speed
行駛速度
timestamp
時間戳記
verticalAccuracy
垂直方向的精確度

didUpdateToLocation事件的處理,若不想用自訂的格式,是使用MKReverseGeocoder類別,就要實作didFindPlacemarkdidFailWithError,可以取得經緯度的地理資訊,例如:國家、郵地區號..



- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark{

    NSLog(@"\n country:%@\n postalCode:%@\n ISOcountryCode:%@\nlocality:%@\n subLocality:%@\n administrativeArea:%@\nsubAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",

          placemark.country,

          placemark.postalCode,

          placemark.ISOcountryCode,

          placemark.administrativeArea,

          placemark.subAdministrativeArea,

          placemark.locality,

          placemark.subLocality,

          placemark.thoroughfare,

          placemark.subThoroughfare);

    // 將座標資訊加入地圖

    [mapView addAnnotation:placemark];

}

-  (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error{

    NSLog(@"reverse geocoder fail!!");

}


[地圖顯示] - 如何把取到的經緯度資訊顯示到地圖上呢?其實每個座標資訊在地圖中顯示後都對應一個MKAnnotationView,而MKAnnotationView又負責解析了一個實現MKAnnotation協定的資料物件。因此我們首先要做的事情就是把取到的經緯度轉換為MKAnnotation協定物件。先定義一個實現MKAnnotation協議的類:

DemoAnnotation.h

#import <Foundation/Foundation.h>

#import <MapKit/MapKit.h>


@interface DemoAnnotation : NSObject<MKAnnotation> {

    CLLocationCoordinate2D _coordinate;

}

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;

@end



DemoAnnotation.m

#import "DemoAnnotation.h"

@implementation DemoAnnotation

@synthesize coordinate=_coordinate;

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate{

    if(self = [super init]) {

        _coordinate=coordinate;

    }

    return self;
}
-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{

    _coordinate=newCoordinate;

}

-(NSString*)title{

    return @"我的位置";
}
-(NSString*)subtitle{

    return @"Peggy haha";
}

@end




呼叫[mapView addAnnotation:XXX] 會實作mapView 協定中的 viewForAnnotation didReceiveMemoryWarning

viewForAnnotation這個方法,這是MKMapView實際建立座標點的地方。MKMapView類別在render地圖的時候會依照Annotation集合的資料建立座標點。

Annotation集合中有幾筆資料vieForAnnotation方法就會被執行幾次。因此每次viewForAnnotation被執行,我們都要建立一個MKAnnotationView物件的實體,並且return這個實體。 MKMapView接收到MKAnnotationView的實體就會將它顯示在地圖上,這就是我們想要顯示在地圖上的座標點。在上面程式碼中使用了 MKPinAnnotationView這個物件是繼承自MKAnnotationView,作用就是在地圖上顯示一個大頭釘。



LocationViewController.m



-  (MKAnnotationView *)mapView:(MKMapView *)mapView

             viewForAnnotation:(id <MKAnnotation>)annotation{

    NSString*annotationViewId=@"CurrentUserAnnotationView";

    MKPinAnnotationView *annotationView= (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewId];

    if(annotationView==nil)

    {

        annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewId];

        //地圖上的大頭針自訂格式

        annotationView.image = [UIImage imageNamed:[NSString stringWithFormat:@"12.png"]];       



        annotationView.draggable = NO;

        //annotationView.animatesDrop = YES; //->目前只有預設的大頭針才有動畫效果。

//設定在點選大頭釘的時候氣泡視窗是否會談出來。

        annotationView.canShowCallout = YES;

       

        //設定右邊的箭頭鈕及事件

        UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];  

        [button addTarget:self action:@selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];  

        annotationView.rightCalloutAccessoryView=button;  

       

        //設定左邊的小圖示及事件

        UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeContactAdd];

        [leftButton setImage:[UIImage imageNamed:@"star.png"] forState:UIControlStateNormal];

        [leftButton addTarget:self

                       action:@selector(checkButtonTapped:event:)

             forControlEvents:UIControlEventTouchUpInside];

        annotationView.leftCalloutAccessoryView = leftButton;

    }

    return annotationView;
}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

}

- (void)checkButtonTapped:(id)sender event:(id)event{  

    UIAlertView *tmp= [[UIAlertView alloc] initWithTitle:@"訊息!" message:@"Peggy測試" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];  

    [tmp show];  

}  



程式執行結果: