一、iOS开发底层原理
1:类也是一个对象,所以它必须是另一个类的实例,这个类就是元类(metaclass)。
CE4585C9-10C0-481A-AEA6-6A00B6AC200B.png我们可以从图中看出:
(1)NSObject的类中定义了实例方法。
(2)NSObject的元类中定义了类方法。
(3)NSObject的元类继承自NSObject类,所以NSObject类是所 有类的根,因此NSObject中定义的实例放阿飞可以被多有对象调用。
(4)NSObject的元类的isa指向自己。
2、因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化,所以无法再运行时动态的给对象增加成员变量。
相应的,对象的方法都定义保存在类的可变区域中。Objective-C 2.0并未在头文件中将实现暴露出来,但在Objective-1.0,中,我们可以看到方法的定义列表是为一个名为methoLists的指针的指针,这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。
3、动态的创建对象
我们可以使用Objective-C语言提供的与runtime相关的函数,动态地创建一个新的类,并且通过相关的方法来获得isa的值,从而了解对象的内部结构。
动态创建类的代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//创建一个新类,名为LRGCustomView,是UIView的子类
Class newClass=objc_allocateClassPair([UIView class], "LRGCustomView", 0);
//为该类增加一个名为report的方法
class_addMethod(newClass, @selector(report), (IMP)ReportFunction,"v@:");
//注册该类
objc_registerClassPair(newClass);
//创建一个LRGCustomView的实例
id instanceOfNewClass=[[newClass alloc] init];
//调用report方法
[instanceOfNewClass performSelector:@selector(report)];
}
void ReportFunction(id self,SEL _cmd)
{
NSLog(@"This object is %p.",self);
NSLog(@"Class is %@,and super is %@ ",[self class],[self superclass]);
Class currentClass=[self class];
for(int i=1;i<5;i++){
NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
currentClass=object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p",[NSObject class]);
NSLog(@"NSObject's class is %p"),object_getClass([NSObject class]);
}
该代码中重要的地方:
54DE3BAA-58BE-4AD7-8778-5533EAA468F4.png1、import runtime相关的头文件:objc/runtime.h。
2、使用objc_allocateClassPair方法创建新的类。
3、使用class_addMethod方法来给类增加新的方法。
4、使用objc_registerClassPair来注册新的类。
5、使用object_getClass方法来获得对象的isa指针所指向的对象。
分析:
03358EF1-1150-4797-B54A-9A7147F2F744.png1、从LRGCustomView对象开始,在我们连续取了3次isa指针所指向的对象后,isa指针所指的地址变成了NSObject元类的地址,第四次起取isa指针所指的对象时的结果说明了NSObject元类的isa指针指向了他自己。
2、作为对比,再代码最后取NSObject类的isa指针地址,我们可以看到其值与其他元类对象的isa只针对值一样,都是指向NSObject元类的。
isa swizzling的应用
系统提供的KVO的实现,就利用了动态的修改isa指针的值的技术。
Method Swizzling API说明
Objective-C提供了以下API来动态替换类方法或者实例方法的实现:
024B7BF8-6319-45C6-A6FD-7D9769B8BE0B.pngclass_replaceMethod替换类方法的定义。当该类中没有想替换的原方法时,该方法会调用class_addMethod来为该类增加换一个新方法。
使用场景:当需要替换的方法可能不存在时,可以考虑使用该方法。
method_exchangeImplementations交换两个方法的实现。
使用场景:当需要交换两个方法的实现时使用。
method_setImplementation设置一个方法的实现。
使用场景:是最简单的用法,当仅仅需要为一个方法设置其实现方式时使用。
一下是唐巧的案例:
用clang分析block实现
clang提供一个命令,可以将Objective-C的源码改写成C语言,借此可以研究具体的源码实现方式。该命令是:
clang -rewrite-objc block.c
NSConcreteGlobalBlock类型的block的实现
先新建一个名为“block.c”的源文件:
#include<stdio.h>
int main(){
^{printf("Hello,World\n");}();
return 0;
}
再命令行中输入 clang -rewrite-objc block.c,即可在目录中看到clang输出了一个名为"block1.cpp"的文件,该文件就是block在C语言中的实现
GCD
Grand Central Dispatch(GCD)比NSThread、NSOperationQueue、NSInvocationOperation等技术用起来更加方便。
//后台执行:
dispatch_async(dispatch_get_global_queue(0,0),^{ });
//主线程执行:
dispatch_async(dispatch_get_main_queue(),^{ });
//一次性执行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{ });
//延迟2秒执行:
double delayInSeconds = 2.0;
dispatch_time_t popTime=dispatch_time(DISPATCH_TIME_NOW,delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime,dispatch_get_main_queue(),^(void){ });
dispatch_queue_t也可以自己定义,如要自定义queue,可以用dispatch_queue_create方法示例如下:
dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com",NULL);
dispatch_async(urls_queue,^{ };
dispatch_release(urls_queue);
另外,GCD还有一些高级用法,例如让后台两个线程并行执行,然后等两个线程都结合素后,再汇总执行结果。这个可以用dispatch_group、dispatch_group_async和dispatch_group_notify来实现,示例如下:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(0,0),^{ //并行执行的线程一});
dispatch_group_async(group,dispatch_get_global_queue(0,0),^{ //并行执行的线程二});
dispatch_group_notify(group,dispatch_get_global_queue(0,0),^{ //汇总结果});
后台运行
< 使用block的另一个用处是可以让程序再后台较长久地运行。以前当按Home键退出后,应用仅有最多5秒钟的时间做一些保存或者清理资源的工作。但是应该可以调用UIApplication的beginBackgroundTaskWithExpirationHandler方法,让应用最多有十分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存、发送统计数据等工作。但是应用可以调用UIApplication的beginBackgroundTaskWithExpirationHandler方法,让应用最多有十分钟的时间在后台长久运行。
让程序再后台长久运行的示例代码如下:
//AppDelegate.h文件
@property (assign,nonatomic) UIBackgroundTaskidentifier backgroundUpdateTask;
//AppDelegate.m文件
-(void)applicationDidenterBackground:(UIApplication *)application
{
[self beginBackgroundUpdateTask];
//在这里加上你需要长久运行的代码
[self beginBackgroundUpdateTask];
}
-(void)beingBackgroundUpdateTask{
self.backgroundUpdateTask=[[UIApplication shareApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
-(void)endBackgroundUpadateTask{
[[UIApplication shareApplication] endBackgroundTask:self.backgroundUpdateTask];
self.backgroundUpdateTask=UIBackgroundTaskInvalid;
}
UIWindow使用
通常有两种方法给UIWindow增加UIview
1.通过调用addsubView方法。UIWindow是UIView的子类,可以用addSubView的方法给自己增加子UIView。
2.通过设置其特有的rootViewController属性。通过设置该属性为要添加view对应的UIViewController,UIWindow将会自动将其view添加到当前window中,同事负责维护ViewController和view的生命周期。
UIWindow有一个类型为"UIWindowLevel"的属性,该属性定义了UIwindow的层级,系统定义的WindowLevel一共有三种取值,如下所示:
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar;
UIWindow的层级是UIWindowLevelNormal,当系统需要在其上面覆盖UIAlertView时,就会创建一个层级是UIWindowLevelAlter的UIWindow,因为其WindowLevel值更高,所以就覆盖在上面了。
手工创建UIWindow
UIWindow一旦被创建,它就自动被添加到整个界面上了
如果我们创建的UIWidnow需要处理键盘事件,那就需要将其试着为keyWindow。keyWindow是被系统设计用来接收键盘和其他非触摸事件的UIWindow。通过makeKeyWindow和resignKeyWindow方法来将自己创建的UIWindow实例设置成keyWindow.
网友评论