你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 神奇的load方法

神奇的load方法

編輯:IOS開發基礎

load方法說明

在Objective-C中,絕大多數類都繼承自NSObject這個根類,而該類有load方法,可以用來實現初始化操作。其原型如下:

+ (void)load

對於加入運行期系統中的每個類(class)及分類(category)來說,必定會調用此方法,而且僅調用一次。當包含類或分類的程序庫載入系統時,就會執行此方法(通常指應用程序啟動)。如程序是iOS平台設計的,則肯定會在此時執行。Mac OS X應用程序更自由一些,它們可以使用“動態加載”(dynamic loading)之類的特性,等應用程序啟動好之後再去加載程序庫。如果分類和其所屬的類都定義了load方法,則先調用類裡的,再調用分類裡的。

執行load方法時,運行期系統處於“脆弱狀態”。在執行子類的load方法之前,必定會先執行所有超類的load方法,而如果代碼還依賴了其他程序庫,那麼程序庫裡相關類的load方法也必定會先執行。

load方法的妙用

簡化AppDelegate類

隨著項目功能的不斷增加,我們有很多功能或者第三庫需要啟動項目時就加載,AppDelegate類就會越來越龐大。這樣結構既不夠清晰,而且耦合性比較強。

改進前:

//設置NUI配置
    [self setNUIConfig];
    //開啟統計
    [[KSStatisticalMgr sharedInstance] start];
    //初始化數據庫
    [KSDBUtils startInitDB];
    //注冊統計平台
    if (!TARGET_IPHONE_SIMULATOR)
    {
        [[SocialService sharedInstance] registerPlatforms];
    }
    //檢測服務器狀態
    [[KSServerMgr sharedInstance]  doGetServerStatus];
    //獲取用戶數據
    [USER_MGR updateUserAssets];
//啟動圖界面
    KSLaunchVC *splashVC = [[KSLaunchVC alloc] initWithNibName:@"KSLaunchVC" bundle:nil];
    UIWindow *keywindow = [UIApplication sharedApplication].keyWindow;
    [keywindow addSubview:splashVC.view];
    [keywindow bringSubviewToFront:splashVC.view];
    [self.window makeKeyAndVisible];
    //自適應屏幕鍵盤控件
    IQKeyboardManager * manager = [IQKeyboardManager sharedManager];
    manager.enable = YES;
    manager.shouldResignOnTouchOutside = YES;
    manager.shouldToolbarUsesTextFieldTintColor = YES;
    manager.enableAutoToolbar = YES;
    //設置首頁
    BYCircleListViewController *homePageVC = [[BYCircleListViewController alloc] init];    
    BYNavigationViewController *navVC = [[BYNavigationViewController alloc] initWithRootViewController:homePageVC];
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    self.window.rootViewController = navVC;   
    [self.window makeKeyAndVisible];

改進後

目錄結構如下:

1.jpg

改進後AppDelegate目錄

初始化第三方庫BYThirdPartService.m的代碼如下:

#import "BYThirdPartService.h"
@implementation BYThirdPartService
+ (void)load{
    static dispatch_once_t onceToken;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //設置NUI配置
        [self setNUIConfig];
        //開啟統計
        [self startStatistics];
        //鍵盤初始化
        [self  initKeyboard];
    });
}
//設置NUI配置
- (void)setNUIConfig{
    //判斷屏幕尺寸
    CGFloat scale = [UIScreen mainScreen].scale;
    int scaleInt = (int)scale;
    NSString *nuiStyleStartName = @"KSDefault";
    NSString *nuiStyleName = @"KSDefault.NUI";
    [NUISettings initWithStylesheet:nuiStyleName];
    if([NUISettings hasProperty:@"translucent" withClass:@"NavigationBar"])
    {
        [[UINavigationBar appearance] setTranslucent:[NUISettings getBoolean:@"translucent" withClass:@"NavigationBar"]];
    }
    if ([NUISettings hasProperty:@"tint-color" withClass:@"NavigationBar"]) {
        [[UINavigationBar appearance] setTintColor:[NUISettings getColor:@"tint-color" withClass:@"NavigationBar"]];
    }
}
//鍵盤初始化
- (void)initKeyboard{
    IQKeyboardManager * manager = [IQKeyboardManager sharedManager];
    manager.enable = YES;
    manager.shouldResignOnTouchOutside = YES;
    manager.shouldToolbarUsesTextFieldTintColor = YES;
    manager.enableAutoToolbar = YES;
}
//開始統計
- (void)startStatistics{
[MobClick startWithConfigure:UMConfigInstance];
}
初始化數據 BYInitData.m的代碼(思路,具體代碼根據自身項目的實際情況進行修改)
#import "BYInitData.h"
@implementation BYInitData
+ (void)load{
    static dispatch_once_t onceToken;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //初始化數據庫
        [self initDB];
        //檢測網絡狀態
        [self GetServerStatus];
        //獲取用戶信息
        [self  GetUserinfo];
    });
}
//初始化數據庫
- (void)initDB{
    [[KSDBHelper sharedInstance] startInitOrUpdate];
}
- (void)GetServerStatus{
   //檢測網絡狀態
...........
}
- (void)GetServerStatus{
   //獲取用戶信息
...........
}
@end

簡化後AppDelegate如下:

#import "AppDelegate.h"
#import "BYCircleListViewController.h"
#import "BYNavigationViewController.h"
//只需增加相應的兩個頭文件
#import "BYThirdPartService.h"
#import "BYInitData.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    BYCircleListViewController *homePageVC = [[BYCircleListViewController alloc] init];
    BYNavigationViewController *navVC = [[BYNavigationViewController alloc] initWithRootViewController:homePageVC];  
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];   
    self.window.backgroundColor = [UIColor whiteColor];   
    self.window.rootViewController = navVC;
    [self.window makeKeyAndVisible];
    return YES;
}

當類被引入項目時, runtime 會向每一個類對象發送 load 消息. 神奇的load 方法, 會在每一個類甚至分類被引入時僅調用一次, 調用的順序是父類優先於子類, 子類優先於分類. 而且 load 方法不會被類自動繼承, 每一個類中的 load 方法都不需要像 viewDidLoad 方法一樣調用父類的方法。

埋點統計

在iOS中,在運行時替換兩個方法的實現,達到“勾住”某個方法並注入代碼的目的。具體方法如下:

重載類的“+(void)load”方法,在程序加載到內存時利用Runtime的method_exchangeImplementations等接口將方法的實現互相交換。當方法M被調用時就會被勾住(Hook),執行我們的方法。

該技術稱為Method Swizzling,屬於面向切面編程(Aspect-Oriented Programming)的一種實現。

替換兩個方法的實現,代碼如下:

#import "BYStatistics.h"
#import
@implementation BYStatistics
+ (void)swizzlingClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{
    Class class = cls;
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL isExistMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (isExistMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }       
}
@end

BYStatistics統計類下文會用到。利用神奇的load方法統計兩個頁面的展示與離開次數

#import "UIViewController+Stastistics.h"
#import "BYStatistics.h"
@implementation UIViewController (Stastistics)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(swizzling_viewWillAppear:);
        [BYStatistics swizzlingClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector];
        SEL originalSelector2 = @selector(viewWillDisappear:);
        SEL swizzledSelector2 =  @selector(swizzling_viewWillDisappear:);
        [BYStatistics swizzlingClass:[self class] originalSelector:originalSelector2 swizzledSelector:swizzledSelector2];
    });
}
#pragma mark - Method Swizzling
- (void)swizzling_viewWillAppear:(BOOL)animated{
    //插入需要執行的代碼
    [self inject_viewWillAppear];
    [self swizzling_viewWillAppear:animated];
}
//利用hook,統計頁面的停留時間
- (void)inject_viewWillAppear{
    NSString *pageName = [self pageEventName:YES];
    if (pageName) {
        //統計代碼
    }
}
- (void)swizzling_viewWillDisappear:(BOOL)animated{
    [self inject_viewWillDisappear];
    [self swizzling_viewWillDisappear:animated];
}
- (void)inject_viewWillDisappear
{
    NSString *pageName = [self pageEventName:YES];
    if (pageName) {
        //統計代碼
    }
}
@end

load方法與initialize方法

NSObject的load方法和initialize方法都是用來實現初始化操作。

load方法

對於加入運行期系統中的每個類及分類來說,必定會調用此方法,而且近調用一次。當包含類或分類的程序庫載入系統時,就會執行此方法,而這通常就是指應用程序啟動的時候,若程序是為iOS平台設計的,則肯定會在此時執行。

如果分類和其所屬的類都定義了load方法,則先調用類裡的,在調用分類裡的。在執行子類的load方法之前,必定會先執行所有超類的load方法,而如果代碼還依賴了其他程序庫,那麼程序庫裡相關類的load方法也必定會先執行。

在整個應用程序執行load方法時都會阻塞

initialize方法

它是“惰性”調用的,也就是說,只有當程序用到了相關的類時,才會調用。因此,如果某個類一直都沒有使用,那麼其initialize方法就一直不會運行。這也就等於說,應用程序無須先把每個類的initialize都執行一遍

注意事項

  • 與其他方法不同,load方法不參與覆寫機制

  • load方法實現得精簡一些,有助於保持應用程序的響應能力,也能

如有寫的不對地方,請在評論區指出,謝謝!

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