前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >慎用公共变量~~

慎用公共变量~~

作者头像
Jacklin
发布2018-05-15 17:19:32
7530
发布2018-05-15 17:19:32
举报

在开发过程中,避免不了会使用公共变量,记录共享对象状态、数据最简单的方式就是创建创建公共变量。

当业务逻辑变多,还采用这种思想就会变得危险,代码逻辑变得不清晰,慢慢就有一种代码坏味道。具体总结为一下几点:

  1. 过多逻辑分支,不够清晰,公共变量不利于系统维护和项目拓展;
  2. 安全性收到威胁,过多地方共享变量,变量的写入和读取在多线程下是危险的;
  3. 业务逻辑交叉过多时,很难保证数据-逻辑的一致性;

出现问题解决问题,Objective-C针对上述问题,提供了一个解决方案:即关联对象(Associated Object)。

我们可以把关联对象想象成一个Objective-C对象(如字典),这个对象通过给定的key连接到类的一个实例上。不过由于使用的是C接口,所以key是一个void指针(const void *)。我们还需要指定一个内存管理策略,以告诉Runtime如何管理这个对象的内存。

这个内存管理的策略可以由以下值指定:

  • OBJC_ASSOCIATION_ASSIGN /**< Specifies a weak reference to the associated object. */
  • OBJC_ASSOCIATION_RETAIN_NONATOMIC/**< Specifies a strong reference to the associated object. * The association is not made atomically. */
  • OBJC_ASSOCIATION_COPY_NONATOMIC /**< Specifies that the associated object is copied.* The association is not made atomically. */
  • OBJC_ASSOCIATION_RETAIN /**< Specifies a strong reference to the associated object. * The association is made atomically. */
  • OBJC_ASSOCIATION_COPY /**< Specifies that the associated object is copied.* The association is made atomically. */

当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。

如果指定的策略是OBJC_ASSOCIATION_ASSIGN,则宿主释放时,关联对象不会被释放;

而如果指定的是Retain或者是Copy,则宿主释放时,关联对象会被释放。

我们甚至可以选择是否是自动Retain/Copy。当我们需要在多个线程中处理访问关联对象的多线程代码时,这就非常有用了,实现线程和逻辑绑定。

1、我们将一个对象连接到其它对象所需要做的就是下面两行代码:

static char anObjectKey;
 
objc_setAssociatedObject(self, &anObjectKey, anObject, OBJC_ASSOCIATION_RETAIN)

2、使用下面一行代码获取绑定的对象:

id anObject = objc_getAssociatedObject(self, &anObjectKey);

在这种情况下,Self对象将获取一个新的关联的对象anObject,且内存管理策略是自动Retain关联对象,当Self对象释放时,会自动Release关联对象。

另外,如果我们使用同一个key来关联另外一个对象时,也会自动释放之前关联的对象,这种情况下,先前的关联对象会被妥善地处理掉,并且新的对象会使用它的内存。

3、移除关联对象:

 objc_removeAssociatedObjects(anObject);

或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil。

4、举个栗子:

在开发工程中,给UIView添加单击手势是非常常见的需求。假定,现在我们就要动态地将一个Tap手势操作连接到任何UIView中,并且根据需要指定点击后的实际操作。

这时候我们就可以将一个手势对象及操作的block对象关联到我们的UIView对象中。这项任务分为两部分。

  • 首先,如果需要,我们要创建一个手势识别对象并将它及block做为关联对象。具体实现如下:
- (void)setTapActionWithBlock:(void (^)(void))block
{
    UITapGestureRecognizer *tapGR = objc_getAssociatedObject(self, &kJLActionHandlerTapGestureKey);
 
    if (!tapGR)
    {
        tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(JL_handleActionForTapGesture:)];
        [self addGestureRecognizer: tapGR];
        objc_setAssociatedObject(self, & kJLActionHandlerTapGestureKey, tapGR, OBJC_ASSOCIATION_RETAIN);
    }
 
    objc_setAssociatedObject(self, & kJLActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}

这段代码检测了手势识别的关联对象。如果没有,则创建并建立关联关系。同时,将传入的块对象连接到指定的key上。注意block对象的关联内存管理策略-Copy。

  • 然后,处理单击事件,具体实现如下:
- (void) JL_handleActionForTapGesture:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateRecognized)
    {
        void(^action)(void) = objc_getAssociatedObject(self, kJLActionHandlerTapBlockKey);
 
        if (action)
        {
            action();
        }
    }
}

我们需要检测手势识别对象的状态,因为我们只需要在点击手势被识别出来时才执行操作。

通过上面可以看到,关联对象实现起来也不是很复杂,而且还可以动态的增强类现有的功能。

5、但是,还是有一点不太完美,代码过于松散,按照上述的方式去应用到项目中,会写不少重复代码,我们需要封装一下,并不暴露#import <objc/runtime.h>引用,具体实现如下:

重新定义一套表征内存策略的枚举:

typedef NS_ENUM(NSInteger, JLAssociationPolicy) {
    
    /**
     OBJC_ASSOCIATION_ASSIGN < Specifies a weak reference to the associated object>
     */
    JLAssociationPolicyAssign = 1,
    
    /**
     OBJC_ASSOCIATION_RETAIN_NONATOMIC <Specifies a strong reference to the associated object.
     *   The association is not made atomically>
     */
    JLAssociationPolicyRetainNonatomic = 2,
    
    /**
     OBJC_ASSOCIATION_COPY_NONATOMIC < Specifies that the associated object is copied.
     *   The association is not made atomically.>
     */
    JLAssociationPolicyCopyNonatomic = 3,
    
    /**
     OBJC_ASSOCIATION_RETAIN < Specifies a strong reference to the associated object.
     *   The association is made atomically.>
     */
    JLAssociationPolicyRetain = 4,
    
    /**
     OBJC_ASSOCIATION_COPY < Specifies that the associated object is copied.
     *   The association is made atomically.>
     */
    JLAssociationPolicyCopy = 5
};

声明方法:

/**
 Set AssociatedObject
 
 @param object Be Associated Object
 @param key associted Key
 @param value associated value or object
 @param policy policy
 */+ (void)JL_setAssociatedObject:(id _Nonnull)object key:(NSString *_Nullable)key value:(id _Nullable)value policy:(JLAssociationPolicy)policy;/**
 Get AssociatedObject
 
 @param object Be Associated Object
 @param key associted Key
 @return associated value or object
 */+ (id _Nullable)JL_getAssociatedObject:(id _Nonnull)object key:(NSString *_Nullable)key;/**
 Remove AssociatedObject
 
 @param object associated value or object
 */+ (void)JL_removeAsociatedObject:(id _Nonnull)object;

Key,在使用的时候只需要传入NSString类的参数就可以了,接口方法变得更优雅简洁一些。

6、用封装的方法重写上述方法:

//定义绑定对象的Key
static NSString *const kJLActionHandlerTapGestureKey = @"JLActionHandlerTapGestureKey";
static NSString *const kJLActionHandlerTapBlockKey = @"JLActionHandlerTapBlocKey";
- (void)setTapActionWithBlock:(void (^)(void))block
{
    UITapGestureRecognizer *tapGR = [JLAssociatedObjectUtils JL_getAssociatedObject:self key:kJLActionHandlerTapGestureKey];
    
    if (!tapGR)
    {
        tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(JL_handleActionForTapGesture:)];
        [self addGestureRecognizer: tapGR];
        [JLAssociatedObjectUtils JL_setAssociatedObject:self key:kJLActionHandlerTapGestureKey value:tapGR policy:JLAssociationPolicyRetain];
    }

     [JLAssociatedObjectUtils JL_setAssociatedObject:self key:kJLActionHandlerTapBlockKey value:tapGR policy:JLAssociationPolicyCopy];
}
- (void) JL_handleActionForTapGesture:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateRecognized)
    {
        void(^action)(void) = [JLAssociatedObjectUtils JL_getAssociatedObject:self key:kJLActionHandlerTapBlockKey];
        
        if (action)
        {
            action();
        }
    }
}

小结

今天,讲到这,要分享的内容就结束了。我将上述封装方法整理成了工具类,已经上传至Github(https://github.com/123sunxiaolin/JLPractice/tree/master/JLAssociatedObjectUtilsDemo),大家有需要的可以自行下载,感觉好的话,欢迎赏赐一个Star~~~ 谢谢~~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 攻城狮的动态 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档