Objective - 新闻资讯 - 云南小程序开发|云南软件开发|云南网站建设-昆明葵宇信息科技有限公司

159-8711-8523

云南网建设/小程序开发/软件开发

知识

不管是网站,软件还是小程序,都要直接或间接能为您产生价值,我们在追求其视觉表现的同时,更侧重于功能的便捷,营销的便利,运营的高效,让网站成为营销工具,让软件能切实提升企业内部管理水平和效率。优秀的程序为后期升级提供便捷的支持!

您当前位置>首页 » 新闻资讯 » 技术分享 >

Objective

发表时间:2020-10-19

发布人:葵宇科技

浏览次数:30


以前的几年中出现了大年夜量的Objective-C开辟者。有些是大年夜动态说话转过来的,比如Ruby或Python,有些是大年夜强类型说话转过来的,如Java或C#,当然也有直接以Objective-C作为入门说话的。也就是说有很大年夜一部分开辟者都没有应用Objective-C太长时光。当你接触一门新说话时,更多地会存眷基本常识,如语法和特点等。但平日有一些更高等的,更鲜为人知又有强大年夜功能的特点等待你去开辟。
The Runtime
Objective-C是一门简单的说话,95%是C。只是在说话层面上加了些关键字和语法。真正让Objective-C如斯强大年夜的是它的运行时。它很小但却很强大年夜。它的核心是消息分发。
 
Messages
如不雅你是大年夜动态说话如Ruby或Python转过来的,可能知道什么是消息,可以直接跳过进入下一节。那些大年夜其他说话转过来的,持续看。
履行一个办法,有些说话,编译器会履行一些额外的优化和缺点检查,因为调用关系很直接也很明显。但对于消息分发来说,就不那么明显了。在发消息前不必知道某个对象是否可以或许处理消息。你把消息发给它,它可能会处理,也可能转给其他的Object来处理。一个消息不必对应一个办法,一个对象可能实现一个办法来处理多条消息。
在Objective-C中,消息是经由过程objc_msgSend()这个runtime办法及邻近的办法来实现的。这个办法须要一个target,selector,还有一些参数。理论上来说,编译器只是把消息分发变成objc_msgSend来履行。比如下面这两行代码是等价的。
 
[array insertObject:foo atIndex:5];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

大年夜多半面向对象的说话里有 classes 和 objects 的概念。Objects经由过程Classes生成。然则在Objective-C中,classes本身也是objects(译者注:这点跟python很像),也可以处理消息,这也是为什么会有类办法和实例办法。具体来说,Objective-C中的Object是一个构造体(struct),第一个成员是isa,指向本身的class。这是在objc/objc.h中定义的。
 
typedef struct objc_object {
    Class isa;
} *id;

object的class保存了办法列表,还有指向父类的指针。但classes也是objects,也会有isa变量,那么它又指向哪儿呢?这里就引出了第三个类型: metaclasses。一个 metaclass被指向class,class被指向object。它保存了所有实现的办法列表,以及父类的metaclass。如不雅想更清跋扈地懂得objects,classes以及metaclasses是若何一路工作地,可以浏览这篇文┞仿。
 
Methods, Selectors and IMPs
我们知道了运行时会发消息给对象。我们也知道一个对象的class保存了办法列表。那么这些消息是若何映射到办法的,这些办法又是若何被履行的呢?
第一个问题的谜底很简单。class的办法列表其实是一个字典,key为selectors,IMPs为value。一个IMP是指向办法在内存中的实现。很重要的一点是,selector和IMP之间的关系是在运行时才决定的,而不是编译时。如许我们就能玩出些花样。
IMP平日是指向办法的指针,第一个参数是self,类型为id,第二个参数是_cmd,类型为SEL,余下的是办法的参数。这也是self和_cmd被定义的处所。下面演示了Method和IMP
- (id)doSomethingWithInt:(int)aInt{}
 
id doSomethingWithInt(id self, SEL _cmd, int aInt){}

其他运行时的办法
 
如今我们知道了objects,classes,selectors,IMPs以及消息分发,那么运行时到底能做什么呢?重要有两个感化:
1.创建、修改、自省classes和objects
2.消息分发
之前已经提过消息分发,不过这只是一小部分功能。所有的运行时办法都有特定的前缀。下面是一些有意思的办法:
 
class
class开首的办法是用来修改和自省classes。办法如 class_addIvar、class_addMethod、 class_addProperty和class_addProtocol 许可重建classes。
 
class_copyIvarList、class_copyMethodList、class_copyProtocolList和class_copyPropertyList能拿到一个class的所有内容。而class_getClassMethod、class_getClassVariable、 class_getInstanceMethod, class_getInstanceVariable、class_getMethodImplementation 和 class_getProperty 返回单个内容。
 
也有一些通用的自省办法,如class_conformsToProtocol、class_respondsToSelector、 class_getSuperclass。最后,你可以应用class_createInstance来创建一个object。
 
ivar
这些办法能让你获得名字,内存地址和Objective-C type encoding。
 
method
这些办法重要用来自省,比如method_getName, method_getImplementation, method_getReturnType等等。也有一些修改的办法,包含method_setImplementation和method_exchangeImplementations,这些我们后面会讲到。
 
objc
一旦拿到了object,你就可以对它做一些自省和修改。你可以get/set ivar, 应用object_copy和object_dispose来copy和free object的内存。最NB的不仅是拿到一个class,而是可以应用object_setClass来改变一个object的class。待会就能看到应用处景。
 
property
属性保存了很大年夜一部分信息。除了拿到名字,你还可以应用property_getAttributes来发明property的更多信息,如返回值、是否为atomic、getter/setter名字、是否为dynamic、背后应用的ivar名字、是否为弱引用。
 
protocol
Protocols有点像classes,然则精简版的,运行时的办法是一样的。你可以获取method, property, protocol列表, 检查是否实现了其他的protocol。
 
sel
最后我们有一些办法可以处理 selectors,比如获取名字,注册一个selector等等。
 
如今我们对Objective-C的运行时有了大年夜概的懂得,来看看它们能做哪些有趣的工作。
 
Classes And Selectors From Strings
 
比较基本的一个动态特点是经由过程String来生成Classes和Selectors。Cocoa供给了NSClassFromString和NSSelectorFromString办法,应用起来很简单:
Class stringclass = NSClassFromString(@"NSString");

于是我们就获得了一个string class。接下来:
 
NSString *myString = [stringclass stringWithString:@"Hello World"];

为什么要这么做呢?直接应用Class不是更便利?平日情况下是,但有些场景下这个办法会很有效。起首,可以得知是否存在某个class,NSClassFromString 会返回nil,如不雅运行时不存在该class的话。比如可以检查NSClassFromString(@"NSRegularExpression")是否为nil来断定是否为iOS4.0+。
 
另一个应用处景是根据不合的输入返回不合的class或method。比如你在解析一些数据,每个数据项都有要解析的字符串以及自身的类型(String,Number,Array)。你可以在一个办法里搞定则些,也可以应用多个办法。个一一个办法是获取type,然后应用if来调用匹配的办法。另一种是根据type来生成一个selector,然后调用之。以下是两种实现方法:
 
- (void)par搜刮引擎优化bject:(id)object {
    for (id data in object) {
        if ([[data type] isEqualToString:@"String"]) {
            [self parseString:[data value]]; 
        } else if ([[data type] isEqualToString:@"Number"]) {
            [self parseNumber:[data value]];
        } else if ([[data type] isEqualToString:@"Array"]) {
            [self parseArray:[data value]];
        }
    }
}
- (void)par搜刮引擎优化bjectDynamic:(id)object {
    for (id data in object) {
        [self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]];
    }
}
- (void)parseString:(NSString *)aString {}
- (void)parseNumber:(NSString *)aNumber {}
- (void)parseArray:(NSString *)aArray {}

可一看到,你可以把7行带if的代码变成1行。将来如不雅有新的类型,只需增长实现办法即可,而不消再去添加新的 else if。
 
Method Swizzling
 
之前我们讲过,办法由两个部分构成。Selector相当于一个办法的id;IMP是办法的实现。如许分开的一个便利之处是selector和IMP之间的对应关系可以被改变。比如一个 IMP 可以有多个 selectors 指向它。
 
而 Method Swizzling 可以交换两个办法的实现。或许你会问“什么情况下会须要这个呢?”。我们先来看下Objective-C中,两种扩大class的门路。起首是 subclassing。你可以重写某个办法,调用父类的实现,这也意味着你必须应用这个subclass的实例,但如不雅持续了某个Cocoa class,而Cocoa又返回了本来的class(比如 NSArray)。这种情况下,你会想添加一个办法到NSArray,也就是应用Category。99%的情况下这是OK的,但如不雅你重写了某个办法,就没有机会再调用本来的实现了。
 
Method Swizzling 可以搞定则个问题。你可以重写某个办法而不消持续,同时还可声调用本来的实现。平日的做法是在category中添加一个办法(当然也可所以一个全新的class)。可以经由过程method_exchangeImplementations这个运行时办法来交换实现。来看一个demo,这个demo演示了若何重写addObject:办法来记载每一个新添加的对象。
#import  <objc/runtime.h>
 
@interface NSMutableArray (LoggingAddObject)
- (void)logAddObject:(id)aObject;
@end
 
@implementation NSMutableArray (LoggingAddObject)
 
+ (void)load {
    Method addobject = class_getInstanceMethod(self, @selector(addObject:));
    Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:));
    method_exchangeImplementations(addObject, logAddObject);
}
 
- (void)logAddObject:(id)aobject {
    [self logAddObject:aObject];
    NSLog(@"Added object %@ to array %@", aObject, self);
}
 
@end

我们把办法交换放到了load中,这个办法只会被调用一次,并且是运行时载入。如不雅指向临时用一下,可以放到其余处所。留意到一个很明显的递归调用logAddObject:。这也是Method Swizzling轻易把我们搞混的处所,因为我们已经交换了办法的实现,所以其实调用的是addObject:
 [img]http://img.blog.csdn.net/20150104185959656
动态持续、交换
我们可以在运行时创建新的class,这个特点用得不多,但其实它照样很强大年夜的。你能经由过程它创建新的子类,并添加新的办法。
但如许的一个子类有什么竽暌姑呢?别忘了Objective-C的一个关键点:object内部有一个叫做isa的变量指向它的class。这个变量可以被改变,而不须要从新创建。然后就可以添加新的ivar和办法了。可以经由过程以下敕令来修改一个object的class
object_setClass(myObject, [MySubclass class]);

这可以用在Key Value Observing。当你开端observing an object时,Cocoa会创建这个object的class的subclass,然后将这个object的isa指向新创建的subclass。点击这里查看更具体的解释。
 
动态办法处理
今朝为止,我们评论辩论了办法交换,以及已有办法的处理。那么当你发送了一个object无法处理的消息时会产生什么呢?很明显,”it breaks”。大年夜多半情况下确切如斯,但Cocoa和runtime也供给了一些应对办法。
起首是动态办法处理。平日来说,处理一个办法,运行时寻找匹配的selector然后履行之。有时,你只想在运行时才创建某个办法,比如有些信息只有在运行时才能获得。要实现这个效不雅,你须要重写+resolveInstanceMethod:和/或 +resolveClassMethod:。如不雅确实袈漩加了一个办法,记得返回YES。
+ (BOOL)resolveInstanceMethod:(SEL)aSelector {
    if (aSelector == @selector(myDynamicMethod)) {
        class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSelector];
}

那Cocoa在什么场景下会应用这些办法呢?Core Data用得很多。NSManagedObjects有很多在运行时添加的属性用来处理get/set属性和关系。那如不雅Model在运行时被改变了呢?
 
消息转发
如不雅 resolve method 返回NO,运行时就进入下一步调:消息转发。有两种常见用例。1) 将消息转发到另一个可以处理该消息的object。2) 将多个消息转发到同一个办法。
消息转发分两步。起首,运行时调用-forwardingTargetForSelector:,如不雅只是想把消息发送到另一个object,那么就应用这个办法,因为更高效。如不雅想要修改消息,那么就要应用-forwardInvocation:,运行时将消息打包成NSInvocation,然后返回给你处理。处理完之后,调用invokeWithTarget:。
Cocoa有几处处所用到了消息转发,重要的两个处所是代劳(Proxies)和响应链(Responder Chain)。NSProxy是一个轻量级的class,它的感化就是转发消息到另一个object。如不雅想要惰性加载object的某个属性会很有效。NSUndoManager也有效到,不过是朝长进步消息,之后再履行,而不是转发到其他的处所。
响应链是关于Cocoa若何处理与发送事宜与行动到对应的对象。比如说,应用Cmd+C履行了copy敕令,会发送-copy:到响应链。起首是First Responder,平日是当前的UI。如不雅没有处理该消息,则转发到下一-nextResponder。这么一向下去直到找到可以或许处理该消息的object,或者没有找到,报错。
 
应用Block作为Method IMP
iOS 4.3带来了很多新的runtime办法。除了对properties和protocols的加强,还带老鏖新的以 imp 开首的办法。平日一个 IMP 是一个指向办法实现的指针,头两个参数为 object(self)和selector(_cmd)。iOS 4.0和Mac OS X 10.6 带来了block,imp_implementationWithBlock() 能让我们应用block作为 IMP,下面这个代码片段展示了若何应用block来添加新的办法。
IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {
    NSLog(@"Hello %@", string);
});
class_addMethod([MYclass class], @selector(sayHello:), myIMP, "v@:@");

如不雅想知道这是若何实现的,可以查看这篇文┞仿
 
可以看到,Objective-C 外面看起来挺简单,但照样很灵活的,可以带来很多可能性。动态说话的优势在于在不扩大说话本身的情况下做很多很灵活的工作。比如Key Value Observing,供给了优雅的API可以与已有的代码无缝结合,而不须要新增说话级其余特点。我们知道,Objective-C开辟iOS app,不得不质疑的是app安然,关于iOS app安然若何进行保护,可以点击这里查看
 

相关案例查看更多