runtime详解

iOS Runtime理解和应用场景 为了关联属性和其对应的存取方法,需要定义一个全局唯一的键值。 阅读详情

转:http://www.jianshu.com/p/46dd81402f63

一:基本概念

Runtime基本是用C和汇编写的,可见苹果为了动态系统的高效而作出的努力。你可以在这里下到苹果维护的开源代码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。Objective-C 从三种不同的层级上与 Runtime 系统进行交互,分别是通过 Objective-C 源代码,通过 Foundation 框架的NSObject类定义的方法,通过对 runtime 函数的直接调用。大部分情况下你就只管写你的Objc代码就行,runtime 系统自动在幕后辛勤劳作着。

  • RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。
  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。
  • OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。
  • 只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

二:runtime的具体实现

我们写的oc代码,它在运行的时候也是转换成了runtime方式运行的,更好的理解runtime,也能帮我们更深的掌握oc语言。
每一个oc的方法,底层必然有一个与之对应的runtime方法。


image
  • 当我们用OC写下这样一段代码
    [tableView cellForRowAtIndexPath:indexPath];

  • 在编译时RunTime会将上述代码转化成[发送消息]
    objc_msgSend(tableView, @selector(cellForRowAtIndexPath:),indexPath);

三:常见方法

unsigned int count;

  • 获取属性列表

    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
     for (unsigned int i=0; i<count; i++) {
         const char *propertyName = property_getName(propertyList[i]);
         NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
     }
  • 获取方法列表

     Method *methodList = class_copyMethodList([self class], &count);
     for (unsigned int i; i<count; i++) {
         Method method = methodList[i];
         NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
     }
  • 获取成员变量列表

    Ivar *ivarList = class_copyIvarList([self class], &count);
      for (unsigned int i; i<count; i++) {
          Ivar myIvar = ivarList[i];
          const char *ivarName = ivar_getName(myIvar);
          NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
      }
  • 获取协议列表

    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
      for (unsigned int i; i<count; i++) {
          Protocol *myProtocal = protocolList[i];
          const char *protocolName = protocol_getName(myProtocal);
          NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
      }

现在有一个Person类,和person创建的xiaoming对象,有test1和test2两个方法

  • 获得类方法
Class PersonClass = object_getClass([Person class]);
SEL oriSEL = @selector(test1);
Method oriMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
  • 获得实例方法
Class PersonClass = object_getClass([xiaoming class]);
SEL oriSEL = @selector(test2);
Method cusMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
  • 添加方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
  • 替换原方法实现
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
  • 交换两个方法
method_exchangeImplementations(oriMethod, cusMethod);

四:常见作用

  • 动态的添加对象的成员变量和方法
  • 动态交换两个方法的实现
  • 拦截并替换方法
  • 在方法上增加额外功能
  • 实现NSCoding的自动归档和解档
  • 实现字典转模型的自动转换

五:代码实现

要使用runtime,要先引入头文件#import <objc/runtime.h>
这些代码的实例有浅入深逐步讲解,最后附上一个我在公司项目中遇到的一个实际问题。

1. 动态变量控制

在程序中,xiaoming的age是10,后来被runtime变成了20,来看看runtime是怎么做到的。

1.动态获取XiaoMing类中的所有属性[当然包括私有] 

Ivar *ivar = class_copyIvarList([self.xiaoming class], &count);

2.遍历属性找到对应name字段 

const char *varName = ivar_getName(var);

3.修改对应的字段值成20 

object_setIvar(self.xiaoMing, var, @"20");

4.代码参考

-(void)answer{
     unsigned int count = 0;
     Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
     for (int i = 0; i<count; i++) {
         Ivar var = ivar[i];
         const char *varName = ivar_getName(var);
         NSString *name = [NSString stringWithUTF8String:varName];
         if ([name isEqualToString:@"_age"]) {
             object_setIvar(self.xiaoMing, var, @"20");
             break;
         }
     }
     NSLog(@"XiaoMing's age is %@",self.xiaoMing.age);
 }
2.动态添加方法

在程序当中,假设XiaoMing的中没有guess这个方法,后来被Runtime添加一个名字叫guess的方法,最终再调用guess方法做出相应。那么,Runtime是如何做到的呢? 

1.动态给XiaoMing类中添加guess方法: 

 class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:");

这里参数地方说明一下:

(IMP)guessAnswer 意思是guessAnswer的地址指针;
"v@:" 意思是,v代表无返回值void,如果是i则代表int;@代表 id sel; : 代表 SEL _cmd;
“v@:@@” 意思是,两个参数的没有返回值。 

2.调用guess方法响应事件: 

[self.xiaoMing performSelector:@selector(guess)];

3.编写guessAnswer的实现: 

void guessAnswer(id self,SEL _cmd){
NSLog(@"i am from beijing");
} 

这个有两个地方留意一下:

  • void的前面没有+、-号,因为只是C的代码。
  • 必须有两个指定参数(id self,SEL _cmd) 

4.代码参考

 -(void)answer{
     class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:");
     if ([self.xiaoMing respondsToSelector:@selector(guess)]) {

         [self.xiaoMing performSelector:@selector(guess)];

     } else{
         NSLog(@"Sorry,I don't know");
     }
 }

 void guessAnswer(id self,SEL _cmd){

     NSLog(@"i am from beijing");

 }
3:动态交换两个方法的实现

在程序当中,假设XiaoMing的中有test1 和 test2这两个方法,后来被Runtime交换方法后,每次调动test1 的时候就会去执行test2,调动test2 的时候就会去执行test1, 。那么,Runtime是如何做到的呢?

  1. 获取这个类中的两个方法并交换
Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(test1));
    Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(test2));
    method_exchangeImplementations(m1, m2);

交换方法之后,以后每次调用这两个方法都会交换方法的实现

4:拦截并替换方法

在程序当中,假设XiaoMing的中有test1这个方法,但是由于某种原因,我们要改变这个方法的实现,但是又不能去动它的源代码(正如一些开源库出现问题的时候),这个时候runtime就派上用场了。

我们先增加一个tool类,然后写一个我们自己实现的方法-change,
通过runtime把test1替换成change。

Class PersionClass = object_getClass([Person class]);
Class toolClass = object_getClass([tool class]);

    ////源方法的SEL和Method

    SEL oriSEL = @selector(test1);
    Method oriMethod = class_getInstanceMethod(PersionClass, oriSEL);

    ////交换方法的SEL和Method

    SEL cusSEL = @selector(change);
    Method cusMethod = class_getInstanceMethod(toolClass, cusSEL);

    ////先尝试給源方法添加实现,这里是为了避免源方法没有实现的情况

    BOOL addSucc = class_addMethod(PersionClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
    if (addSucc) {
          // 添加成功:将源方法的实现替换到交换方法的实现     
        class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));

    }else {
    //添加失败:说明源方法已经有实现,直接将两个方法的实现交换即
method_exchangeImplementations(oriMethod, cusMethod);  
  }
5:在方法上增加额外功能

有这样一个场景,出于某些需求,我们需要跟踪记录APP中按钮的点击次数和频率等数据,怎么解决?当然通过继承按钮类或者通过类别实现是一个办法,但是带来其他问题比如别人不一定会去实例化你写的子类,或者其他类别也实现了点击方法导致不确定会调用哪一个,runtime可以这样解决:

@implementation UIButton (Hook)

+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        Class selfClass = [self class];

        SEL oriSEL = @selector(sendAction:to:forEvent:);
        Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);

        SEL cusSEL = @selector(mySendAction:to:forEvent:);
        Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);

        BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
        if (addSucc) {
            class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        }else {
            method_exchangeImplementations(oriMethod, cusMethod);
        }

    });
}

- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    [CountTool addClickCount];
    [self mySendAction:action to:target forEvent:event];
}

@end

load方法会在类第一次加载的时候被调用,调用的时间比较靠前,适合在这个方法里做方法交换,方法交换应该被保证,在程序中只会执行一次。

6.实现NSCoding的自动归档和解档

如果你实现过自定义模型数据持久化的过程,那么你也肯定明白,如果一个模型有许多个属性,那么我们需要对每个属性都实现一遍encodeObject 和 decodeObjectForKey方法,如果这样的模型又有很多个,这还真的是一个十分麻烦的事情。下面来看看简单的实现方式。
假设现在有一个Movie类,有3个属性,它的h文件这这样的

#import <Foundation/Foundation.h>

//1. 如果想要当前类可以实现归档与反归档,需要遵守一个协议NSCoding
@interface Movie : NSObject<NSCoding>

@property (nonatomic, copy) NSString *movieId;
@property (nonatomic, copy) NSString *movieName;
@property (nonatomic, copy) NSString *pic_url;

@end

如果是正常写法, m文件应该是这样的:

#import "Movie.h"
@implementation Movie

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_movieId forKey:@"id"];
    [aCoder encodeObject:_movieName forKey:@"name"];
    [aCoder encodeObject:_pic_url forKey:@"url"];

}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.movieId = [aDecoder decodeObjectForKey:@"id"];
        self.movieName = [aDecoder decodeObjectForKey:@"name"];
        self.pic_url = [aDecoder decodeObjectForKey:@"url"];
    }
    return self;
}
@end

如果这里有100个属性,那么我们也只能把100个属性都给写一遍。
不过你会使用runtime后,这里就有更简便的方法。
下面看看runtime的实现方式:

#import "Movie.h"
#import <objc/runtime.h>
@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie class], &count);

    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie class], &count);
        for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
       // 归档
       NSString *key = [NSString stringWithUTF8String:name];
      id value = [decoder decodeObjectForKey:key];
       // 设置到成员变量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

这样的方式实现,不管有多少个属性,写这几行代码就搞定了。怎么,还嫌麻烦,下面看看更加简便的方法:两句代码搞定。
我们把encodeWithCoder 和 initWithCoder这两个方法抽成宏

#import "Movie.h"
#import <objc/runtime.h>

#define encodeRuntime(A) \
\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [self valueForKey:key];\
[encoder encodeObject:value forKey:key];\
}\
free(ivars);\
\

#define initCoderRuntime(A) \
\
if (self = [super init]) {\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [decoder decodeObjectForKey:key];\
[self setValue:value forKey:key];\
}\
free(ivars);\
}\
return self;\
\

@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    encodeRuntime(Movie)
}

- (id)initWithCoder:(NSCoder *)decoder
{
    initCoderRuntime(Movie)
}
@end

我们可以把这两个宏单独放到一个文件里面,这里以后需要进行数据持久化的模型都可以直接使用这两个宏。

7.实现字典转模型的自动转换

字典转模型的应用可以说是每个app必然会使用的场景,虽然实现的方式略有不同,但是原理都是一致的:遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。
像几个出名的开源库:JSONModel,MJExtension等都是通过这种方式实现的。

  • 先实现最外层的属性转换
   // 创建对应模型对象
    id objc = [[self alloc] init];

    unsigned int count = 0;

    // 1.获取成员属性数组
    Ivar *ivarList = class_copyIvarList(self, &count);

    // 2.遍历所有的成员属性名,一个一个去字典中取出对应的value给模型属性赋值
    for (int i = 0; i < count; i++) {

        // 2.1 获取成员属性
        Ivar ivar = ivarList[i];

        // 2.2 获取成员属性名 C -> OC 字符串
       NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];

        // 2.3 _成员属性名 => 字典key
        NSString *key = [ivarName substringFromIndex:1];

        // 2.4 去字典中取出对应value给模型属性赋值
        id value = dict[key];

        // 获取成员属性类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        }

如果模型比较简单,只有NSString,NSNumber等,这样就可以搞定了。但是如果模型含有NSArray,或者NSDictionary等,那么我们还需要进行第二步转换。

  • 内层数组,字典的转换
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType containsString:@"NS"]) { 

             //  是字典对象,并且属性名对应类型是自定义类型
            // 处理类型字符串 @\"User\" -> User
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            // 自定义对象,并且值是字典
            // value:user字典 -> User模型
            // 获取模型(user)类对象
            Class modalClass = NSClassFromString(ivarType);

            // 字典转模型
            if (modalClass) {
                // 字典转模型 user
                value = [modalClass objectWithDict:value];
            }

        }

        if ([value isKindOfClass:[NSArray class]]) {
            // 判断对应类有没有实现字典数组转模型数组的协议
            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {

                // 转换成id类型,就能调用任何对象的方法
                id idSelf = self;

                // 获取数组中字典对应的模型
                NSString *type =  [idSelf arrayContainModelClass][key];

                // 生成模型
                Class classModel = NSClassFromString(type);
                NSMutableArray *arrM = [NSMutableArray array];
                // 遍历字典数组,生成模型数组
                for (NSDictionary *dict in value) {
                    // 字典转模型
                    id model =  [classModel objectWithDict:dict];
                    [arrM addObject:model];
                }

                // 把模型数组赋值给value
                value = arrM;

            }
        }

我自己觉得系统自带的KVC模式字典转模型就挺好的,假设movie是一个模型对象,dict 是一个需要转化的 [movie setValuesForKeysWithDictionary:dict]; 这个是系统自带的字典转模型方法,个人感觉也还是挺好用的,不过使用这个方法的时候需要在模型里面再实现一个方法才行,
- (void)setValue:(id)value forUndefinedKey:(NSString *)key 重写这个方法为了实现两个目的:1. 模型中的属性和字典中的key不一致的情况,比如字典中有个id,我们需要把它赋值给uid属性;2. 字典中属性比模型的属性还多的情况。
如果出现以上两种情况而没有实现这个方法的话,程序就会崩溃。
这个方法的实现:

- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"id"]) {
        self.uid = value;
    }
}

六.几个参数概念

以上的几种方法应该算是runtime在实际场景中所应用的大部分的情况了,平常的编码中差不多足够用了。
如果从头仔细看到尾,相信你基本的用法应该会了,虽然会用是主要的目的,有几个基本的参数概念还是要了解一下的。

1.objc_msgSend
 /* Basic Messaging Primitives
 *
 * On some architectures, use objc_msgSend_stret for some struct return types.
 * On some architectures, use objc_msgSend_fpret for some float return types.
 * On some architectures, use objc_msgSend_fp2ret for some float return types.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */

这是官方的声明,从这个函数的注释可以看出来了,这是个最基本的用于发送消息的函数。另外,这个函数并不能发送所有类型的消息,只能发送基本的消息。比如,在一些处理器上,我们必须使用objc_msgSend_stret来发送返回值类型为结构体的消息,使用objc_msgSend_fpret来发送返回值类型为浮点类型的消息,而又在一些处理器上,还得使用objc_msgSend_fp2ret来发送返回值类型为浮点类型的消息。
最关键的一点:无论何时,要调用objc_msgSend函数,必须要将函数强制转换成合适的函数指针类型才能调用。
从objc_msgSend函数的声明来看,它应该是不带返回值的,但是我们在使用中却可以强制转换类型,以便接收返回值。另外,它的参数列表是可以任意多个的,前提也是要强制函数指针类型。
其实编译器会根据情况在objc_msgSend, objc_msgSend_stret, objc_msgSendSuper, 或 objc_msgSendSuper_stret四个方法中选择一个来调用。如果消息是传递给超类,那么会调用名字带有”Super”的函数;如果消息返回值是数据结构而不是简单值时,那么会调用名字带有”stret”的函数。

2.SEL

objc_msgSend函数第二个参数类型为SEL,它是selector在Objc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的 ID,而这个 ID 的数据结构是SEL:
typedef struct objc_selector *SEL;
其实它就是个映射到方法的C字符串,你可以用 Objc 编译器命令@selector()或者 Runtime 系统的sel_registerName函数来获得一个SEL类型的方法选择器。
不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器,于是 Objc 中方法命名有时会带上参数类型(NSNumber一堆抽象工厂方法),Cocoa 中有好多长长的方法哦。

3.id

objc_msgSend第一个参数类型为id,大家对它都不陌生,它是一个指向类实例的指针:
typedef struct objc_object *id;
那objc_object又是啥呢:
struct objc_object { Class isa; };
objc_object结构体包含一个isa指针,根据isa指针就可以顺藤摸瓜找到对象所属的类。
PS:isa指针不总是指向实例对象所属的类,不能依靠它来确定类型,而是应该用class方法来确定实例对象的类。因为KVO的实现机理就是将被观察对象的isa指针指向一个中间类而不是真实的类,这是一种叫做 isa-swizzling 的技术,详见官方文档.

4.Class

之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针:
typedef struct objc_class *Class;
objc_class里面的东西多着呢:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if  !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

可以看到运行时一个类还关联了它的超类指针,类名,成员变量,方法,缓存,还有附属的协议。
在objc_class结构体中:ivars是objc_ivar_list指针;methodLists是指向objc_method_list指针的指针。也就是说可以动态修改 *methodLists 的值来添加成员方法,这也是Category实现的原理.

上面讲到的所有东西都在Demo里,如果你觉得不错,还请为我的Demo star一个。

demo下载
文献计量VOSviewer分析知网文献基本操作及实用技巧 本教程旨在为读者提供一个关于VOS viewer的初级使用指南。VOS viewer是一个强大的科学研究可视化工具,主要用于构建和可视化科学地图。它能够处理大规模的科研数据,包括文献引用、共引、共词、共作者等信息。无论你是科研人员,还是数据分析师,甚至是研究生,都能在这个教程中找到使用VOS viewer的有效方法。我们将一步步教你如何使用这个软件,以及如何利用它进行科研数据的可视化。 阅读详情

相关推荐

ISO18000-6B/6C(EPC C1G2)两个射频识别(RFID)标准协议的技术特点

目前读写器基本上是采用IMPINJ的R2000芯片和E710芯片开发,因为ISO18000-6B标准的现在应用很少,E710芯片已经不再支持ISO18000-6B标准,选择上就只有采用R2000芯片开发的UR6258超高频读写器以及手持机,这个在项目应用上需要留意。尽管ISO18000-6B标准具有上述优点,但也存在一些不足之处,特别是多标签阅读性能远远满足不了现在的应用需求,近年来发展停滞,绝大部分应用已经被ISO18000-6C(EPC C1G2)取代;#RFID# #物联网# #标准协议#

RFID设备与应用介绍 2155

iOS底层 Runtime深入理解

前言 Runtime是近年来面试遇到的一个高频方向,也是我们平时开发中或多或少接触的一个领域,那么什么是runtime呢?它又可以用来做什么呢? 什么是Runtime?平时项目中有用过么? OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行 OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动 态性相关的函数 平时编写的OC代码,底层都是转换成了Runtime API进行调用 具体应用 利用关联对象(AssociatedObject)给分类

Rey的技术博客 2487

Cesium实战:从DEM数据到3D地形可视化的全流程解析

本文详细解析了从DEM数据到3D地形可视化的全流程,重点介绍了Cesium和Cesiumlab在数字高程模型(DEM)处理与地形数据转换中的应用。通过实战案例和工具对比,帮助开发者高效实现地形可视化,并分享性能优化与高级功能开发经验。

weixin_29079743的博客 336

【iOS】——Runtime学习

iOS的Runtime,通常称为Objective-C Runtime,是一个C语言库,包含了很多底层的纯C语言API。,它是Objective-C语言动态特性的基石。这个系统在程序运行时提供了一系列强大的功能,允许我们在应用运行过程中动态地操作类和对象,执行诸如检查和改变对象、交换方法实现、动态添加方法或属性等操作。高级编程语言想要成为可执行文件需要先编译为汇编语言再汇编为机器语言,机器语言也是计算机能够识别的唯一语言,但是OC并不能直接编译为汇编语言,而是要先转写为纯C语言再进行编译和汇编的操作,

m0_73974920的博客 2735

PLC编程标准之争:IEC61131与IEC61499核心区别与选型指南

在工业自动化领域,PLC编程长期依赖IEC61131标准,它定义了梯形图、结构化文本等语言,通过周期扫描机制保证确定性执行。然而,随着工业4.0和分布式控制场景的兴起,IEC61499以事件驱动模型打破传统范式,让功能块能跨设备协同,实现更灵活的柔性产线和模块化系统。理解两者在程序结构、数据交互、部署方式和实时性上的差异,是选型与工程落地的关键。本文从基础概念出发,剖析执行模型与架构演进,结合一线调试经验,帮助工程师在传统PLC与下一代标准之间做出理性决策。

weixin_27014595的博客 267

智慧社区:安防系统解决方案

智慧社区:安防系统解决方案

s13166803785的博客 431

iOS runtime 详解和使用场景(最详细的使用教程)

一、Runtime介绍 OC是对C语言的扩展,加入了面向对象和消息发送机制,Runtime是OC的一个核心,是用C语言和汇编语言编写。OC是动态运行时语言,在运行时确定一个对象的类型、调用哪个对象的方法,因此需要Runtime来做类和对象的动态创建,消息传递和消息转发等。OC代码最终会转换成Runtime库中对应的函数结构体。任何语言最终都会被编译为汇编语言,再汇编为机器语言。OC到可执行文件编译过程: OC->Runtime->C->汇编->可执行文件。 Ru...

天微微亮 8455

基于PCL的电力线点云提取与分割实现

本文基于PCL库实现电力线点云提取与分割,包含6个核心处理阶段:文件读取与结果文件夹创建、高程滤波分割候选点云、欧式聚类去噪、电力线分段提取、球形邻域搜索补全点云、最终分割与可视化保存。通过PCA计算点云线性特征(阈值0.91)筛选电力线点,采用欧式聚类(容忍度3.0)去除噪点,并针对不同规模聚类差异化设置球形搜索半径(4.0/6.0)补全点云。最终通过欧式聚类分割并可视化带颜色点云。算法对高程滤波阈值、聚类参数等具有敏感性,需根据点云特性调整优化。

weixin_45196569的博客 192

iOS开发之Runloop和Runtime

RunLoop 对于一个iOS开发者,如果你的水平只是停留在会用API的级别,那说明你与大神还是慢慢长路,本文章大家一块学习一些深层次的东西,RunLoop和Runtime。Runloop官方文档 你肯定写过一个按钮点击事件,点击界面上的一个按钮,这个时候就会有对应的按钮响应事件发生。给我们的感觉就像应用一直处于随时待命的状态,在没人操作的时候它一直在休息,在让它干活的时候,它就能立刻响应。其实

逝水无情的博客 3233

在Qt中集成VTK和PCL:实现数据可视化的完整指南

在现代软件开发中,数据可视化至关重要。将VTK(Visualization Toolkit)和PCL(Point Cloud Library)集成到Qt环境里,能为开发者提供强大的可视化解决方案。本文将深入讲解如何在Qt中读取PCL文件,展示VTK模型和PCL数据,适合想要在Qt项目中实现数据可视化的开发者。

轩宇的博客 916

使用Microsoft Print to PDF进行编程:创建PDF文件的简单方法

Microsoft Print to PDF是一种内置于Windows操作系统中的功能,它允许我们通过打印操作将任何可打印的内容保存为PDF文件。然后,我们使用Microsoft Print to PDF打印机进行打印操作,并将输出文件保存为指定的PDF文件。使用Microsoft Print to PDF功能是一种方便且简单的方法来创建PDF文件,无论是在个人使用还是在企业开发中,它都可以提供良好的支持和灵活性。请注意,如果你使用的是其他编程语言,你可以根据相应的打印机接口或库来实现类似的功能。

TechSavant的博客 1635

java Runtime.exec方法详解

在做一个项目时用到了Runtime.exec(String command),但是网上的很多博客讲解的都很不详细明白,也不全,干脆我自己来做一个讲解。 我们先来看看api的解释: 从字面上理解的意思就是在单独打开一条线程执行指定的命令。当然,这个方法有多个重载,如下: 下面会讲到其它的重载方法,不急。 想到api的解释我第一时间想到的就是cmd命令行!因为确实有些相似,但是转念一想又不对,“在单独的进程中执行指定的字符串命令”不就是这玩意儿吗! 然后我分别测试了这几个命令“..

loveLifeLoveCoding的博客 2389

rosbag分析传感器数据的时间同步情况方法

ROS传感器时间同步分析流程:通过rosbaginfo初步检查bag文件信息,使用rqt_bag可视化时间线分布,采用rostopicecho或Python脚本提取各传感器时间戳并计算时间差统计值(均值/最大差/标准差),最后分析接收与采样时间延迟。量化评估同步精度,识别固定偏移或随机抖动问题,确保多传感器数据对齐,为后续融合处理提供可靠数据基础。

start_up_go的博客 201

iOS Runtime详解

Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制。而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库。它是 Objective-C 面向对象和动态机制的基石。Objective-C 是一个动态语言,这意味着它不仅需要一个编译器,也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。

irainsa的博客 753

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Runtime详解

Runtime 简介 Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同 Objective-C的动态性是由Runtime API来支撑的 Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写 基础知识 这是在读取Apple底层的源码的基础知识,如果能够理解透彻对于研读源码会带来很大帮助。 如果想取出来二进制中某位的值,则将那一位的值置为1,其余位置为0,然后按位与运算即可。如某值1010,想取出来第二位的值(从右往左),则采用0010进行与运算

canoejun的博客 1178

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

runTime详解一

RunTime是OC语言中一个重要的概念,是深入理解OC这门语言的钥匙,关于RunTime有自己的一些心得体会,写这篇文章主要是为了梳理自己的知识,或许也能启发他人,有不足之处,请大家指正。 所谓RunTime表面意思是运行时,所谓运行时就是尽可能地把决定从编译器推迟到运行期, 就是尽可能地做到动态. 只是在运行的时候才会去确定对象的类型和方法的. 因此利用Runtime机制

xiaowenqiang_1的专栏 309

Runtime类详解

前言:Java的类库日益庞大,所包含的类和接口也不计其数。但其中有一些非常重要的类和接口,是Java类库中的核心部分。常见的有String、Object、Class、Collection、ClassLoader、Runtime、Process...,熟悉这些类是学好Java的基础。而这些类一般...

chuangmoliao4054的博客 356

ios RunTime机制详解

一:基本概念Runtime基本是用C和汇编写的,可见苹果为了动态系统的高效而作出的努力。你可以在这里下到苹果维护的开源代码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。Objective-C 从三种不同的层级上与 Runtime 系统进行交互,分别是通过 Objective-C 源代码,通过 Foundation 框架的NSObject类定义的方法,通过对 ...

吴凯强的博客 8854

RunTime类详解

1.该类主要代表了应用程序的运行环境。一个RunTime就代表一个运行环境。2.RunTime类常用的方法:(1) getRuntime():该方法用于返回当前应用程序的运行环境对象。(2) exec(String command):该方法用于根据指定的路径执行对应的可执行文件。①实例:public class Demo7 { public static void main(String[] a...

大同小后生伟的博客 1万+

[转]runtime 详解

公司项目用到一个三方开源库,里面有个bug,不能改动源码,我想来想去,只能通过runtime这个万能的手段来解决。但是runtime 并不怎么会用,怎么办,马上学习呗。说到runtime,它是Objective-c里面最核心的技术,被人们传呼的神乎其神,但是感觉有一层神秘的面纱笼罩其上,毕竟使用场景不多,相信大多数开发者都不会熟练的运用。而网络上也有无数的文章来讲解runtime,但是真...

176

Java Runtime 类详解

Java Runtime 类详解 Java Runtime 类使用 Java Runtime.exec 方法的使用 Java Runtime.getRuntime().exec() 的使用 Java Runtime.exec 详解

Cloud-Future的博客 8175

iOS Runtime 详解

什么是runtime依照苹果文档的说法,runtime是: The Objective-C language defers as many decisions as it can from compile time and link time to runtime. (尽量将决定放到运行的时候,而不是在编译和链接过程) 如何理解这段话呢,我们首先要知道,一段代码从写完到最终执行的过程中发

njafei的博客 2228

iOS runtime详解

因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时。也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的代码。这就是 Objective-C Runtime 系统存在的意义,它是整个Objc运行框架的一块基石。 Runtime消息传递 一个对象的方法像这样[obj foo],编译器转成消息发送objc_msgSend(obj, foo),Runtime时执行的流程是这样的: 首先,通过obj的isa指针找到它的 class ; 在

qq_30356665的博客 1418

runtime 详解

rumtime基础知识 Objective-C提供了编译运行时,只要有可能,它都可以动态地运行,这意味着不仅需要编译器,还需要运行时系统执行编译的代码,运行时系统充当Objective-C语言的操作系统,有了它才能运行。 运行时系统所提供功能是非常强大的,在实际开发中是经常使用到的。比如,苹果不允许我们给Category追加扩展属性,是因为它不会自动生成成员变量,那么我们通过运行时就可以很好的

liupinghui的专栏 3732
上一篇: FMDB - 官方使用文档
下一篇: RuntimeSummary
直隶码农
博客等级 码龄19年 95粉丝 103原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值