美文网首页
1.动态加载可选包

1.动态加载可选包

作者: tokened | 来源:发表于2018-04-12 13:45 被阅读0次

1.动态加载可选包 创建一个协议和一个遵守该协议的类用于测试: Greeter协议: #import @protocol Greeter -(NSString *)greeting:(NSString *)salutation; @end BasicGreeter类: #import #import "Greeter.h" @interface BasicGreeter : NSObject @end #import "BasicGreeter.h" @implementation BasicGreeter -(NSString *) greeting:(NSString *)salutation{ return [NSString stringWithFormat:@"%@,World!",salutation]; } @end 2.创建可选包 新建工程中选择OS X->Framework&Library->Bundle: 先将Greeter.h添加到可选包中。再创建一个遵守Greeter协议的类 CustomGreeter类: #import #import "Greeter.h" @interface CustomGreeter : NSObject @end #import "CustomGreeter.h" @implementation CustomGreeter -(NSString *)greeting:(NSString *)salutation{ return [NSString stringWithFormat:@"%@,Universe!", salutation]; } @end 3.传入包路径 接下需要将可选包的路径作为参数传入命令行程序的main函数中: 选择工程中的bundle文件(记得先编译一次,不然bundle文件是红色的,无法使用),然后将图中标记的bundle文件完整路径复制下来: 20160708111636469.png 回到命令行程序,点击下图中标记处,选择Edit Scheme: 20160708111655048.png 在弹出窗口的下图标记位置粘贴刚刚复制的bundle文件路径: 20160708144125723.png 4.使用可选包 最后在main.m中进行测试: #import #import "BasicGreeter.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 声明一个类型为id并且遵循Greeter协议的变量并进行测试 id greeter = [[BasicGreeter alloc] init]; NSLog(@"%@",[greeter greeting:@"Hello"]); // 在指定路径(通过输入参数获得)创建一个包 NSString *bundlePath; // argc设置了程序的参数数量,而argv[]数组存储了参数值,数组第一个参数存储了程序的名称, // 因此argc大于或等于1,这里传递了一个参数(包的路径),所以argc应该等于2. if (argc != 2) { // 没有获得包路径,退出 NSLog(@"Please provide a path for the bundle"); } else{ // 获取包的路径并创建一个NSBundle对象 bundlePath = [NSString stringWithUTF8String:argv[1]]; NSBundle *greeterBundle = [NSBundle bundleWithPath:bundlePath]; if (greeterBundle == nil) { NSLog(@"Bundle not found at path"); } else{ // 以动态方式加载包 NSError *error; BOOL isLoaded = [greeterBundle loadAndReturnError:&error]; if (!isLoaded) { NSLog(@"Error = %@",[error localizedDescription]); } else{ // 加载包后,使用该包创建一个对象,并向这个对象发送一条消息 Class greeterClass = [greeterBundle classNamed:@"CustomGreeter"]; greeter = [[greeterClass alloc] init]; NSLog(@"%@",[greeter greeting:@"Hello"]); // 使用完以动态方式加载的包后进行卸载 // 先释放所有用包中的类创建的对象! greeter = nil; BOOL isUnloaded = [greeterBundle unload]; if (!isUnloaded) { NSLog(@"Couldn't unload bundle"); } } } } } return 0; } 运行结果: 2016-07-08 14:10:26.774 DynaLoader[12966:141636] Hello,World! 2016-07-08 14:10:26.802 DynaLoader[12966:141636] Hello,Universe! 可以看到动态加载的可选包中类可以跟工程中自带的类同样地工作。 5.运行时系统API 下面示例代码是对运行时系统API的使用: 定义了一个方法的实现函数,创建并注册了一个类,创建了一个类实例,最后以动态方式向该实例添加了一个变量; #import #import #import // 用于显示选择器的方法实现函数 static void display(id self, SEL _cmd){ NSLog(@"Invoking method with selector %@ on %@ instance", NSStringFromSelector(_cmd), [self className]); } int main(int argc, const char * argv[]) { @autoreleasepool { // 创建一个类对(类与元类) Class WidgetClass = objc_allocateClassPair([NSObject class], "Widget", 0); // 向这个类添加一个方法 const char *types = "v@:"; // class_addMethod的4个参数分别添加方法的目标类、新方法的选择器、函数的地址(数据类型为IMP)和描述方法参数的数据类型的字符串(字符串里的内容分别依次为函数返回值类型和每个参数类型,在这里v表示void,@表示selector,:表示IMP) class_addMethod(WidgetClass, @selector(display), (IMP)display, types); // 向这个类添加一个实例变量 const char *height = "height"; class_addIvar(WidgetClass, height, sizeof(id), rint(log2(sizeof(id))), @encode(id)); // 注册这个类 objc_registerClassPair(WidgetClass); // 创建一个widget实例,并设置实例变量的值 id widget = [[WidgetClass alloc] init]; id value = [NSNumber numberWithInt:15]; [widget setValue:value forKey:[NSString stringWithUTF8String:height]]; // @encode(type)返回的是type的类型(用C语言 char *的表示) NSLog(@"Widget instance height = %@", [widget valueForKey:[NSString stringWithUTF8String:height]]); // 向widget实例发送一条消息 objc_msgSend(widget, NSSelectorFromString(@"display")); // 以动态方式向widget实例添加一个变量(关联对象) // 由于Objective-C中不能向对象添加实例变量,使用关联对象来模拟该功能 NSNumber *width = [NSNumber numberWithInt:10]; objc_setAssociatedObject(widget, @"width", width, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // 获取该变量的值并显示它 id result = objc_getAssociatedObject(widget, @"width"); NSLog(@"Widget instance width = %@", result); } return 0; } 注意:在目前版本的Xcode中,需要将 Apple LLVM X.X(版本号) - Preprocessing 中的 Enable Strict Checking of objc_msgSend Calls 设置为No,不然编译会失败。 6.动态代理 下面是一个动态代理的实现示例。使用Foundation框架中的NSInvocation和NSProxy类实现动态的标准转发功能(另一种消息转发方式为快速转发,在[精通Objective-C]对象和消息传递中有具体实现过程)。 将标准转发功能与NSProxy类一起使用,可以在Objective-C中获得不错的面向切面编程(AOP)机制,AOP是一种编程范式,其目的是通过横切将依赖或影响程序其他部分的功能和程序中的其他组成部分分割开,提高程序的模块化程度。 7.创建实现横切功能的协议和类 Invoker协议: #import @protocol Invoker @required // 必要方法(必须实现) -(void)preInvoke:(NSInvocation *)inv withTarget:(id) target; @optional // 可选方法 -(void)postInvoke:(NSInvocation *)inv withTarget:(id) target; @end AuditingInvoker类: #import #import "Invoker.h" @interface AuditingInvoker : NSObject @end #import "AuditingInvoker.h" @implementation AuditingInvoker -(void) preInvoke:(NSInvocation *)inv withTarget:(id)target{ NSLog(@"Creating audit log before sending message with selector %@ to %@ object",NSStringFromSelector([inv selector]),[target className]); } -(void) postInvoke:(NSInvocation *)inv withTarget:(id)target{ NSLog(@"Creating audit log after sending message with selector %@ to %@ object",NSStringFromSelector([inv selector]),[target className]); } @end 8.编写代理类 AspectProxy类: #import #import "Invoker.h" @interface AspectProxy : NSProxy @property(strong) id proxyTarget; // 代理的对象 @property(strong) id invoker; // 实现横切的对象 @property(readonly) NSMutableArray *selectors; // 已注册选择器的集合 -(id)initWithObject:(id)object andInvoker:(id)invoker; -(id)initWithObject:(id)object selectors:(NSArray *)selectors andInvoker:(id)invoker; -(void)registerSelector:(SEL)selector; @end #import "AspectProxy.h" @implementation AspectProxy // 父类为NSProxy,是一个基类,所以方法开头没有调用[super init] -(id)initWithObject:(id)object selectors:(NSArray *)selectors andInvoker:(id)invoker{ _proxyTarget = object; _invoker = invoker; _selectors = [selectors mutableCopy]; return self; } -(id)initWithObject:(id)object andInvoker:(id)invoker{ return [self initWithObject:object selectors:nil andInvoker:invoker]; } // 为目标对象中被调用的方法返回一个NSMethodSignature对象。运行时系统要求在执行标准转发时实现这个方法。 -(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [self.proxyTarget methodSignatureForSelector:sel]; } -(void)forwardInvocation:(NSInvocation *)invocation{ // 在调用目标方法前执行横切功能 if ([self.invoker respondsToSelector:@selector(preInvoke:withTarget:)]) { if (self.selectors != nil) { SEL methodSel = [invocation selector]; for (NSValue *selValue in self.selectors) { if (methodSel == [selValue pointerValue]) { [[self invoker] preInvoke:invocation withTarget:self.proxyTarget]; break; } } } else{ [[self invoker] preInvoke:invocation withTarget:self.proxyTarget]; } } // 调用目标方法 [invocation invokeWithTarget:self.proxyTarget]; // 在调用目标方法后执行横切功能 if ([self.invoker respondsToSelector:@selector(postInvoke:withTarget:)]) { if (self.selectors != nil) { SEL methodSel = [invocation selector]; for (NSValue *selValue in self.selectors) { if (methodSel == [selValue pointerValue]) { [[self invoker] postInvoke:invocation withTarget:self.proxyTarget]; } } } else{ [[self invoker] postInvoke:invocation withTarget:self.proxyTarget]; } } } // 向已注册选择器的集合添加一个选择器 -(void)registerSelector:(SEL)selector{ NSValue *selValue = [NSValue valueWithPointer:selector]; [self.selectors addObject:selValue]; } @end 9.测试动态代理程序 #import #import "AspectProxy.h" #import "AuditingInvoker.h" #import "Calculator.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 创建Calculator对象 id calculator = [[Calculator alloc] init]; NSNumber *addend1 = [NSNumber numberWithInteger:-25]; NSNumber *addend2 = [NSNumber numberWithInteger:10]; // 为该对象创建代理 NSValue* selValue = [NSValue valueWithPointer:@selector(sumAddend1:addend2:)]; NSArray *selvalues = @[selValue]; AuditingInvoker *invoker = [[AuditingInvoker alloc] init]; id calculatorProxy = [[AspectProxy alloc] initWithObject:calculator selectors:selvalues andInvoker:invoker]; // 使用指定的选择器向该代理发送消息 [calculatorProxy sumAddend1:addend1 addend2:addend2]; // 使用没有特殊处理要求的其他选择器向这个代理发送消息 [calculatorProxy sumAddend1:addend1 :addend2]; // 为这个代理注册另一个选择器并再次向其发送消息 [calculatorProxy registerSelector:@selector(sumAddend1::)]; [calculatorProxy sumAddend1:addend1 :addend2]; } return 0; } 运行结果: 2016-07-08 17:37:40.146 AspectProxy[23532:243497] Creating audit log before sending message with selector sumAddend1:addend2: to Calculator object 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Invoking method on Calculator object with selector sumAddend1:addend2: 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Creating audit log after sending message with selector sumAddend1:addend2: to Calculator object 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Invoking method on Calculator object with selector sumAddend1:: 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Creating audit log before sending message with selector sumAddend1:: to Calculator object 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Invoking method on Calculator object with selector sumAddend1:: 2016-07-08 17:37:40.147 AspectProxy[23532:243497] Creating audit log after sending message with selector sumAddend1:: to Calculator object 分析结果可以看到,调用Calculator对象的代理中的sumAddend1:addend2:方法,会使该代理调用AuditingInvoker中的两个方法和真正目标(Calculator对象)中的sumAddend1:addend2:方法。因为该方法是通过Calculator对象的代理注册的。然后调用Calculator对象中的sumAddend1::方法,因为该方法还没有通过这个代理注册,所以程序仅会将调用该方法的消息转发给Calculator对象,而不会调用AuditingInvoker方法。最后对sumAddend1::方法进行注册之后,然后程序再次调用了它,程序就调用了AuditingInvoker对象中的AOP方法和真正目标(Calculator对象)中的sumAddend1::方法。 作者:控心KX 链接:https://www.jianshu.com/p/622f671f1949 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章

  • 1.动态加载可选包

    1.动态加载可选包创建一个协议和一个遵守该协议的类用于测试:Greeter协议:#import @protocol...

  • Objective运行时系统API

    1.动态加载可选包 创建一个协议和一个遵守该协议的类用于测试: Greeter协议: BasicGreeter类:...

  • element参数管理

    1.参数管理 A.展示动态参数可选项 动态参数可选项展示及操作在获取动态参数的方法中进行处理。 B.添加/删除可选...

  • 前端学习笔记三十三-电商项目实战(五)

    1.参数管理 A.展示动态参数可选项 动态参数可选项展示及操作在获取动态参数的方法中进行处理。 B.添加/删除可选...

  • 动态加载jar包

    概述 前段时间在开发外设驱动程序时,涉及到了动态加载jar包的知识,于是开始学习了下。这里主要是完成客户端驱动ja...

  • R可视化——地图绘制及数据的添加

    加载所需R包 数据获取与加载 1、地图数据——从阿里云DataV可视化网站(可选择其他平台)下载格式为.json的...

  • Android主题换肤框架 无缝切换

    Android-Skin-Loader 一个通过动态加载本地皮肤包进行换肤的皮肤框架 工程目录介绍 用法 1. 在...

  • 安装包大小优化

    1.常规的安装包优化 图片压缩 资源动态加载 Lint工具 极限压缩 Proguard 混淆 2.资源文件再压缩 ...

  • Android - Fragment加载方式

    首先介绍Fragment有几种加载方式 1.静态加载2.动态加载3.通过FragmentAdapter加载 静态加...

  • DYLD的初步防护

    1. DYLD源码 1.1 DYLD中加载动态库的部分,在dyld.cpp的_main函数中 1.2 在加载动态...

网友评论

      本文标题:1.动态加载可选包

      本文链接:https://www.haomeiwen.com/subject/rkbxkftx.html