1. 程式人生 > >這些天,折騰ios的那些事

這些天,折騰ios的那些事

序言

從清明假回來,部門老闆開始給我佈置實習的第二個任務,叫我按照一個很蛋疼的登陸流程做一個iPad應用的登陸模組,也許說起登陸模組大家都會,就是驗證使用者名稱和密碼的過程,但是實際的產品的登陸流程卻遠比這個複雜,這裡不對登陸過程具體介紹了,主要說說折騰ios具體開發過程的事情,作為一個初入行的碼農,以後一定要記住這些教訓。

(1)工作任務很簡單,先看懂PC版的登陸流程,然後照葫蘆畫瓢的將其流程在ipad上執行起來。pc版的是一個同事寫的,用的C#,vc08版和10版的版本轉化就折騰了半天,不過前兩週一直再整C#,所以程式碼看起來還比較快,花了一天的事情對蛋疼的流程終於有了一個清晰的瞭解,不過後來發現還是有個地方沒有理解老闆的意圖,還好補救起來不是很麻煩。題注:以後接任務的時候一定要弄清楚老闆的最終目的,一定要拿個小本記清楚,長期堆程式碼的人腦子不夠用,和同事的交流一定要不嫌麻煩,交流的過程實際上就是交流感情的過程,以後的圈子就是這樣建立的。

(2)ipad的開發當然要用到mac,xcode,還好機子環境都已經搭建好了,ipad版本的已有程式碼拷貝下來,開始整啦。第一個問題,就是ios開發的模擬器除錯和真機聯機除錯(device)的區別,聽同事說的,模擬器的執行處理過程其實是用的機器的intel處理器,而ipad上是用的arm處理器,部分程式碼在兩個除錯平臺會有些區別,具體什麼區別現在我還沒有接觸到。而真機除錯需要去蘋果官方申請開發者許可權,下載ios開發的私鑰和許可證才行。私鑰其實就相當於現在各大開放平臺的appID和secretKey,而許可證就相當於官方註冊你要除錯機器的韌體編號,對其分發一個和私鑰配對的解密字串。 搞了半天下載的私鑰都出不來,最後還是直接從同事的另一臺mac上把私鑰拷貝出來,環境這才算搭好。

題注:雖然很多問題要自己想辦法解決,但是同事以前做過的話,過去喊一聲,總比你在那裡研究半天的強;在給你解決問題的過程中還能對你言傳身教的,可以讓你學到不少東西,至少可以少走彎路,更快的完成老闆交代的任務。

(3)總算可以閒下來寫程式碼了,ipad和iphone的開發沒有什麼區別,雖然以前自己也折騰過幾個小例項,看過那個所有初學者都會看的objective-c基礎教程,但真正自己寫程式碼還真沒有怎麼接觸過。所以就當邊學邊練了。

技術問題

討論一些工作工程中花比較多時間解決的問題:

(1)iOS記憶體分配和釋放的問題

開始在window的appDaleget中寫了一些UIAlertview,除錯程式碼的時候執行到Alertview show的時候在主執行緒裡就提示SIGBART錯誤,一開始以為是Daleget中不能顯示AlertVIew,以前看書是說最好不要在dale get中去顯示介面的東西,就有這樣的誤會。 不過在同事確認可以後,將問題確認為程式碼漏洞。於是將Alertview 置於函式入口,直接return,發現在Daleget是可以顯示的,於是將alertview 一步步的後移,終於確定了出問題的函式,進入函式之後,在一部分一部分的程式碼註釋,最後終於確定了問題程式碼行:

 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults release];

問題就出在release 上,defaults的初始化並沒有呼叫init分配記憶體。剛寫程式碼,所以一直謹記ios開發的init release原則,所以每遇到一個*物件的時候就去release它,結果這樣是不對的。後面又遇到這樣的記憶體釋放問題,由於謹記上一次教訓,開始以為alloc就要release,其實也不是這樣的,只有init 你才需release;以後遇到這樣的問題,寧願少release,也不要多release。因為有些物件你不release是不會出問題的。

(2)iOS聯機除錯的檔案拷貝問題

iOS聯機除錯時需要將程式中的資原始檔先拷貝到除錯機器裡,我一開始不知道,將檔案放到xcode的機器上,檢查檔案是否存在時發現路徑是機器的路徑。這下就滑稽了,不知道怎麼拷貝一個檔案到ipad的應用目錄下去,mac下也查不到ipad的磁碟,最後詢問同事才知道拷貝資原始檔到程式是在程式裡做的,給我的版本是隻要目錄存在就不拷貝,我的個去呀,原來時這樣的。所以只能在機器上先把原有的應用刪掉,然後command+R,拷貝檔案,OK!

(3) iOS判斷是否有網路的程式

在判斷網路是否存在的情況下,我開始的做法時send一個登入百度的http請求,如果請求的返回碼是200-300之間,我就判定有網,而pc版的做法也是開一個ping百度的執行緒,看是否能夠ping成功,可是ipad上沒法用ping呀。所以只有另尋辦法,在網上找了找,發現下面這段程式碼能用,嘿,貼下來,供以後使用。

//check if net OK
-(BOOL) checkNet {
    bool ans = NO;    
    // Create zero addy
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;
    
    // Recover reachability flags
    SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
    SCNetworkReachabilityFlags flags;
    
    BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
    CFRelease(defaultRouteReachability);
    
    if (!didRetrieveFlags)
    {
        NSLog(@"Error. Could not recover network reachability flags");
        ans = NO;
    }
    
    BOOL isReachable = flags & kSCNetworkFlagsReachable;
    BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
    ans = (isReachable && !needsConnection) ? YES : NO;
    
    return ans;
}

(4) iOS xml解析問題

這個問題困擾了我很久,ios有一個解析XML的標準庫,叫NSXMLParser,網上還說,解析XML的方法很多,各種各樣的辦法很多,我不想包含第三方的庫,所以我還是選擇自帶的NSXMLParser。這個庫是用了xml 代理的方式,具體解釋一下這個代理,這個代理就是自己新建一個xml解析的類,在這個類裡建立一個NSXMLParser的物件,然後將代理設為這個新建的xml類。另外新建的xml解析類必須繼承NSXMLParserDelegate協議,然後在新建的xml類中自己實現這些協議的方法。具體看程式碼吧。

首先自己定義存放解析資料的資料結構:

//儲存使用者名稱和密碼的資料結構
@interface  UserPwd: NSObject {
    NSString *_username;
    NSString *_passwd;
}
@property (nonatomic, retain) NSString *username;
@property (nonatomic, retain) NSString *passwd;
@end

@implementation UserPwd
@synthesize username = _username;
@synthesize passwd = _passwd;

-(void) dealloc{
    [_username release];
    [_passwd release];
}

@end

然後實現自己建立的xml解析類:
@implementation XMLParser
- (void) parseXMLFile:(NSString *)xmlFilename{
    NSData *data = [[NSData alloc] initWithContentsOfFile:xmlFilename];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; 
//    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[[NSURL alloc] initWithString:xmlFilename]];
    [parser setShouldProcessNamespaces:NO];    
    [parser setShouldReportNamespacePrefixes:NO];
    [parser setShouldResolveExternalEntities:NO];
    [parser setDelegate:self];
    [parser parse];
}


 -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict{
     NSLog(@"111begin");
     if([elementName isEqualToString:@"user"]) {
         NSLog(@"User");
         s_UserPwd = [[UserPwd alloc] init];
        //讀每個elemet的屬性值 
[s_UserPwd setUsername:[attributeDict objectForKey:@"username"]];
         [s_UserPwd setPasswd:[attributeDict objectForKey:@"password"]];
         
         NSLog(@"%@", [s_UserPwd username]);
     }
     }
}

//解析每個element的value
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    NSLog(@"%@", string);
}

//element結束時的動作
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    NSLog(@"111end");
}

//報告解析的結束
-(void)parserDidEndDocument:(NSXMLParser*)parser{
    
}
//報告不可恢復的解析錯誤
-(void)paser:parserErrorOccured{
    
}
@end

(5)iOS UIWebview獲取螢幕touch按鈕事件問題的解決

這個問題給困擾了我一天,最後投機用了一個方法才搞定,問題是這樣的,在一個UIWebview中載入了一個登入頁,頁面中有一個登入按鈕(html元素),問題就是我如何在viewcontroller中獲取這個頁面元素的的點選事件,網上說ios裡的webview中無法獲取touch事件,這可怎麼半?

查資料發現,IOS可以和javascript互動,你在ios中可以獲取頁面中的標題、元素、提交表單,插入js,但是js中元素的onclick事件中卻無法插入ios的程式碼。而ios的UIWebview中只能截獲url跳轉事件,開始的登入按鈕沒有onclick事件,是為了安全性的考慮,參考同事安桌板的做法,給登入按鈕設定了一個onclick事件,讓其調轉到一個有指定標記(登入標記)的url,然後再在UIwebview中截獲
這個事件,就OK了。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    //獲取當前調轉頁面的URL
    NSString *_loginURL = [[request URL] absoluteString];
    //如果當前URL有當前登入標記,則進行登入驗證
    if([[_loginURL lowercaseString] rangeOfString:@"login標記"].length > 0)
    {
        userName = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('loginId')[0].value"];
        passWd = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('passwd')[0].value"];
        [self iPADLogin];
    }
     return YES;
}