你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 寒哥教你學 iOS

寒哥教你學 iOS

編輯:IOS開發基礎

lecture-hall-347316_640.jpg

本篇文章主要講解 4個問題

  1. load妙用

  2. aop面向切面編程

  3. NSNumber Or Int

  4. @()適配64位

1 讓appDelegate 減少負擔

經過漫長時間的學習 你終於掌握了iOS大法 你找到了份iOS開發的工作 信誓旦旦的要開始你的coding生涯 老板對你非常器重 然後告訴你 我覺得你的技術 是非常刁的 那這個項目就你自己來搞吧 啊哦這就意味著這個項目你就從頭到尾處理 從軟件的架構 到頁面的展示 都交給你喽 ??

用著自己的半吊子水平 papapa的 coding 決心一定要把代碼封裝好 寫的漂亮 (其實是聽大神說 封裝 其實自己不太懂)

項目到了尾聲 老板告訴你我們的app 我們的app 將來得來個分享到朋友圈的功能吧 不然怎麼體現我產品的牛逼

然後你聽說友盟比較好使(有廣告的嫌疑) 你去友盟看了他們的文檔 他告訴你你要在 appdelegate didFinishLaunch方法裡面寫了這個東西

 [UMSocialData setAppKey:@"XX"];
    //     注冊微信

 [UMSocialWechatHandler setWXAppId:@"XXX"  appSecret:@"XX" url:@""];
    //    注冊QQ

 [UMSocialQQHandler setQQWithAppId:@"XXX" appKey:@"XXX" url:@""];

過了幾天 老板又說 我們需要統計下我頁面的信息 你接入了友盟的統計 在appdelegate didFinishLaunch又 多了行代碼

需求是無窮無盡 我需要bug統計(fir hud) 提醒用戶評分系統(iRate) 推送(jPush 信鴿 個推。。)

當初你決心一定要把代碼封裝的完美 寫的漂亮的心早就被老板的需求徹底打敗了

別擔心 寒哥教你小技巧

不知道你們用過 IQKeyBoardManage和iRate這種智能庫沒

大牛的readme 寫了這段話

Key Features
1) CODELESS, Zero Line Of Code 不需要寫任何代碼
2) Works Automatically //自動工作
3) No More UIScrollView //不需要scrollview
4) No More Subclasses //不需要繼承父類
5) No More Manual Work //不需要配置
6) No More #imports //不需要導入

其實不神奇 只是大牛用了 + load這個方法

學習OC都知道這個代碼會在一個類被加載到運行庫中就會被自動調用 這不就實現了 自動調用

寫一個類繼承自NSObject

#import @interface ThirdPartService : NSObject@end



 #import "ThirdPartService.h"
 #import "UMSocial.h"
 #import "UMSocialWechatHandler.h"
 #import "UMSocialQQHandler.h"
 #import 
 #import @implementation ThirdPartService
 + (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{    //    TODO  這裡是我自己測試的  fir hud
    [FIR handleCrashWithKey:@"XX"];    //    友盟
    [UMSocialData setAppKey:@"XX"];    //     隱藏未安裝的平台
    [UMSocialConfig hiddenNotInstallPlatforms:@[UMShareToQQ,UMShareToQzone,UMShareToWechatSession,UMShareToWechatTimeline]];    //     注冊微信
    [UMSocialWechatHandler setWXAppId:@"XX" appSecret:@"XX" url:@""];    //    注冊QQ
    //    TODO   QQ的不是真的
    [UMSocialQQHandler setQQWithAppId:@"XX" appKey:@"XX" url:@""];    //    TODO    UM統計
    [MobClick startWithAppkey:@""];
    [MobClick setCrashReportEnabled:NO];    NSLog(@"第三方服務注冊完畢");
});
}@end

類似於定位也可以這樣寫

blob.png

模塊和服務完全拆開

但是有的服務 如APNS需要LaunchOption 那就只能寫在appdDelegate 不過這樣的話已經摘除很多代碼了 只剩下幾個固定的 到時候再修改appDelegate就會感覺非常清晰 了

2 ViewController繼承?

接著上面講 我們接入了友盟統計 友盟統計最基本的東西就是 統計頁面的pv

blob.png

友盟的這樣寫 對於新手的我們就覺得這不就so easy嗎

我打開了某個vc(HomeViewController)

在代碼裡面寫上了這句

-(void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];#ifndef DEBUG
   [MobClick beginLogPageView:NSStringFromClass([self class])];#endif}
-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];#ifndef DEBUG

  [MobClick endLogPageView:NSStringFromClass([self class])];#endif}

然後我一個項目中可能有幾十個 甚至上百個頁面需要統計pv 我總不能每個節目都這樣寫吧

聰明的我們想到了繼承

如MyBaseViewController:UIViewController

這樣就要做一件事 把我們項目中所有繼承自UIViewController的類全部改為繼承自MyBaseViewController 然而你真的覺得這樣好嗎 我們一個項目中有幾十個控制器 我就要把每個控制器改一遍

這種重復性的工作一是無聊 而是容易出錯 你復制著復制者就會遺漏掉某個類 重要的是 我們項目中很多類並不是直接繼承自UIViewController 有的可能是UITableViewController UICollectionViewContr0ller UINavigationController 甚至不常用的UISearchDisPlayController UIPopoverController ?UIPresentController 是不是突然覺得這麼多啊啊 ??

這也不是坑的 坑的是將來你混成了大牛 招了個小弟 你告訴他你所有的類都要繼承自我寫的各種父類 新手總是會不經意見犯錯誤 有些類忘記繼承了 後期查起來難度非常大 浪費時間 所以這種設計是不合理的

  • 寒哥再次教你黑魔法 Method swizzling

關於這個是干嘛的 自行百度

這裡有一篇來自NSHipster博主的文章 英文

中文翻譯

還有一篇解釋runtime的文章傳送門

實踐

用方法交叉 我們就可以攔截吸引的方法了 上代碼了

這樣就做到了面向切面編程(AOP)的思想

上代碼

#import @interface UIViewController (AOP)#warning  運行時 改變一下方法 做一些切面編程比如 統計 等等
 @end



 #import "UIViewController+AOP.h"
 #import #import 
 @implementation UIViewController (AOP)

 + (void)load {   static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{
    Class class = [self class];    // When swizzling a class method, use the following:
    // Class class = object_getClass((id)self);
    swizzleMethod(class, @selector(viewDidLoad), @selector(aop_viewDidLoad));
    swizzleMethod(class, @selector(viewDidAppear:), @selector(aop_viewDidAppear:));
    swizzleMethod(class, @selector(viewWillAppear:), @selector(aop_viewWillAppear:));
    swizzleMethod(class, @selector(viewWillDisappear:), @selector(aop_viewWillDisappear:));
});
} void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)   {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);BOOL didAddMethod =
class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));if (didAddMethod) {
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));
} else {
    method_exchangeImplementations(originalMethod, swizzledMethod);
}
 }
 - (void)aop_viewDidAppear:(BOOL)animated {
[self aop_viewDidAppear:animated];


}

 -(void)aop_viewWillAppear:(BOOL)animated {
[self aop_viewWillAppear:animated];#ifndef DEBUG
   [MobClick beginLogPageView:NSStringFromClass([self class])];#endif}
 -(void)aop_viewWillDisappear:(BOOL)animated {
    [self aop_viewWillDisappear:animated];#ifndef DEBUG

    [MobClick endLogPageView:NSStringFromClass([self class])];#endif}
 - (void)aop_viewDidLoad {
[self aop_viewDidLoad];if ([self isKindOfClass:[UINavigationController class]]) {    UINavigationController *nav = (UINavigationController *)self;
    nav.navigationBar.translucent = NO;
    nav.navigationBar.barTintColor = GLOBAL_NAVIGATION_BAR_TIN_COLOR;
    nav.navigationBar.tintColor = [UIColor whiteColor];    NSDictionary *titleAtt = @{NSForegroundColorAttributeName:[UIColor whiteColor]};
    [[UINavigationBar appearance] setTitleTextAttributes:titleAtt];
    [[UIBarButtonItem appearance]
     setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
     forBarMetrics:UIBarMetricsDefault];
}//    self.view.backgroundColor = [UIColor whiteColor];self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
 } @end

圖片代碼一份 方便觀看

blob.png

blob.png

我們充分利用了黑魔法達到了面向切面編程的好處

思想來源這裡http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html

黑魔法非毒藥 遵守一個規范寫出來的代碼是不會Crash的 只要能幫我們解決問題就是好東西

黑魔法性能 有瓶頸? 都到runtime的底層了 你還擔心有瓶頸 ?少年安心使用就好了 ? 不服 可以用Time Profiel測試

黑魔法也非萬能 ?像 我們在導航控制器要封裝手勢 統一管理左側返回按鈕 ?這些東西 還是繼承來得好

技術就是工具 黑貓,白貓,抓住老鼠就是好貓

3 網絡訪問參數到底用基本數據類型還是對象

下面看兩個方法

blob.png

 + (void)getDataAtPageNo:(NSNumber *)pageNo PageSize:(NSNumber *)pageSize 
complete:(CompleteBlock)complete {NSMutableDictionary *param = [NSMutableDictionary dictionary];    if (pageSize) {
        [param setObject:pageSize forKey:@"pageSize"];
   }
 [param setObject:pageNo forKey:@"pageNo"];// SendRequest}
 + (void)getData2AtPageNo:(long )pageNo PageSize:(long )pageSize 
 complete:(CompleteBlock)complete {     NSMutableDictionary *param = [NSMutableDictionary dictionary];

        [param setObject:@(pageSize) forKey:@"pageSize"];
        [param setObject:@(pageNo) forKey:@"pageNo"];// SendRequest
 }

在訪問網絡請求時 對於有參數的請求 設計一個方法 主流為以上兩種

  1. 使用對象當做參數

  2. 使用基本數據類型做參數

一般情況下 這並沒有什麼大的區別 但是寒哥給出的意見是Never出現基本數據類型

一般情況下 開發者可能覺得並沒有什麼區別 下面我給大家舉個例子

在設計一個分頁展示數據的時候: 在頁面上的邏輯就是 默認加載第一頁 每頁長度為10 (Server端的同學一般都很友好 默認情況下 不傳每頁的長度就是10個) 但是傳了就會覆蓋掉後台寫的默認參數 如傳了20 Server就吐20條數據

  1. 在第一中設計方案中: 可能在某個控制器中保留一個PageNo PageSize 的對象的成員變量 ,在下拉刷新或者上拉加載的時候 會傳遞對應的參數給請求方法 , 如果沒有特殊需求的話pageSize 對象可有可無 也就是有可能為nil ,那在對應的param可能就沒有這個參數傳遞給Server 。

    Server 就會交還給我們某頁的20條數據

  2. 在 第二種設計方案中 : 可能也在某個控制器中保留一個PageNo pageSize的基本數據類型的成員變量, 在訪問網絡請求時交給對應的方法 , 一般沒有特殊需求我們也不會對PageSize專門設值 但是 基本數據類型在OC 和C語言這種傳統的編程語言中是有默認值的 為0,雖然我們沒有給pageSize 賦值 但是默認系統默認給了0這個初始值 那麼傳遞到Server的時候 就會覆蓋掉Server 寫的默認pageSize=10 這樣的請求既不會報錯 也不會返回數據

超級難調試

所以在網絡訪問中 寒哥給出的意見就是Never出現基本數據類型

4 用NSNumber比基本數據類型的好處 ? 64位適配問題

我們一般都用來當做網絡請求的參數 緩存或者展示到頁面

  1. 對於網絡請求的參數 因為NSDictionary只能放對象 所以NSNumber最好的方式

  2. 緩存 無論緩存到plist 還是KeyArchive 都是需要對象的所以NSNumber也非常合適

3 展示到頁面

我見過這樣給頁面上賦值的朋友

blob.png

我們看到這樣貌似並沒有什麼不妥

但是我們把設備切換到iPhone5S以下 也就是32位的設備

blob.png

注意這裡有Warning

為什麼呢 我們來看下NSInteger的頭文件

blob.png

在32下設備是Int 在64位是long

我們都知道蘋果不允許不支持64位的app上架 但是貌似我們從來沒有為32位和64位做適配

其實不然 在printf 和NSLog 時 對應%d %zd %f 占位符是非常嚴格的 如果不對項目就會造成意外的結果

blob.png

blob.png

其實拿到一個NSNumber我們並不知道他到底是int long unsigned int Bool 直接針對某個類型轉換是有風險的 但是其實Clang 給我們提供了個非常好用的Macro @()

blob.png

NSNumber並不是一個簡單的類 它是cocoa 中 類簇的實現參考資料

http://www.cocoachina.com/ios/20140109/7681.html

http://www.cocoachina.com/ios/20150106/10848.html

http://www.cocoachina.com/ios/20141218/10688.html

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved