runtime关联属性

原文出自:标哥的技术博客

前言



在开发中经常需要给已有的类扩展添加方法和属性,但是Objective-C是不允许给已有类扩展属性的,因为类扩展是不会自动生成成员变量的。但是,苹果提供了runtime,我们可以通过runtime使用关联API就可以做到了。

关联API介绍



我们先看看关联API,只有这三个API,使用也是非常简单的:

/**
 * Sets an associated value for a given object using a given key and association policy.
 *
 * @param object The source object for the association.
 * @param key The key for the association.
 * @param value The value to associate with the key key for object. Pass nil to clear an existing association.
 * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
 *
 * @see objc_setAssociatedObject
 * @see objc_removeAssociatedObjects
 */
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

/**
 * Returns the value associated with a given object for a given key.
 *
 * @param object The source object for the association.
 * @param key The key for the association.
 *
 * @return The value associated with the key \e key for \e object.
 *
 * @see objc_setAssociatedObject
 */
id objc_getAssociatedObject(id object, const void *key)

/**
 * Removes all associations for a given object.
 *
 * @param object An object that maintains associated objects.
 *
 * @note The main purpose of this function is to make it easy to return an object
 *  to a "pristine state”. You should not use this function for general removal of
 *  associations from objects, since it also removes associations that other clients
 *  may have added to the object. Typically you should use \c objc_setAssociatedObject
 *  with a nil value to clear an association.
 *
 * @see objc_setAssociatedObject
 * @see objc_getAssociatedObject
 */
void objc_removeAssociatedObjects(id object)

实际上,我们几乎不会使用到objc_removeAssociatedObjects函数,这个函数的功能是移除指定的对象上所有的关联。

设置关联值

对于设置关联,我们需要使用下面的API关联起来:

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

参数说明:

  • object:与谁关联,通常是传self
  • key:唯一键,在获取值时通过该键获取,通常是使用static const void *来声明
  • value:关联所设置的值
  • policy:内存管理策略,比如使用copy

获取关联值

如果我们要获取所关联的值,需要通过key来获取,调用如下函数:

id objc_getAssociatedObject(id object, const void *key)

参数说明:

  • object:与谁关联,通常是传self,在设置关联时所指定的与哪个对象关联的那个对象
  • key:唯一键,在设置关联时所指定的键

关联策略

我们先看看设置关联时所指定的policy,它是一个枚举类型,看官方说明:

/**
 * Policies related to associative references.
 * These are options to objc_setAssociatedObject()
 */
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

我们说明一下各个值的作用:

  • OBJC_ASSOCIATION_ASSIGN:表示弱引用关联,通常是基本数据类型,如intfloat
  • OBJC_ASSOCIATION_RETAIN_NONATOMIC:表示强(strong)引用关联对象
  • OBJC_ASSOCIATION_COPY_NONATOMIC:表示关联对象copy
  • OBJC_ASSOCIATION_RETAIN:表示强(strong)引用关联对象,但不是线程安全的
  • OBJC_ASSOCIATION_COPY:表示关联对象copy,但不是线程安全的

扩展属性



我们来写一个例子,扩展UIControl添加Block版本的TouchUpInside事件。

扩展头文件声明:

#import <UIKit/UIKit.h>

typedef void (^HYBTouchUpBlock)(id sender);

@interface UIControl (HYBBlock)

@property (nonatomic, copy) HYBTouchUpBlock hyb_touchUpBlock;

@end

扩展实现文件:

#import "UIControl+HYBBlock.h"
#import <objc/runtime.h>

static const void *sHYBUIControlTouchUpEventBlockKey = "sHYBUIControlTouchUpEventBlockKey";

@implementation UIControl (HYBBlock)

- (void)setHyb_touchUpBlock:(HYBTouchUpBlock)hyb_touchUpBlock {
  objc_setAssociatedObject(self,
                           sHYBUIControlTouchUpEventBlockKey,
                           hyb_touchUpBlock,
                           OBJC_ASSOCIATION_COPY);

  [self removeTarget:self
              action:@selector(hybOnTouchUp:)
    forControlEvents:UIControlEventTouchUpInside];

  if (hyb_touchUpBlock) {
    [self addTarget:self
             action:@selector(hybOnTouchUp:)
   forControlEvents:UIControlEventTouchUpInside];
  }
}

- (HYBTouchUpBlock)hyb_touchUpBlock {
  return objc_getAssociatedObject(self, sHYBUIControlTouchUpEventBlockKey);
}

- (void)hybOnTouchUp:(UIButton *)sender {
  HYBTouchUpBlock touchUp = self.hyb_touchUpBlock;

  if (touchUp) {
    touchUp(sender);
  }
}

@end

使用起来很简单吧!!!

写在最后



本文章是专门介绍通过runtime如何给已有类添加扩展属性,如果文章中出现有疑问的地方,请在评论中评论,笔者会在第一时间回复您的!

且看且珍惜!!!

关注我



如果在使用过程中遇到问题,或者想要与我交流,可加入有问必答QQ群:324400294

关注微信公众号:iOSDevShares

关注新浪微博账号:标哥Jacky

支持并捐助



如果您觉得文章对您很有帮忙,希望得到您的支持。您的捐肋将会给予我最大的鼓励,感谢您的支持!

支付宝捐助 微信捐助
时间: 2024-11-10 01:11:24

runtime关联属性的相关文章

JavaBean 设置关联属性

在一个Bean中设置关联属性的基本步骤如下: 调入java.beans 包,以便访问该包中所定义的一些方便类.MyButton中的import语句的使用方法如下: import java.beans.*; 实例化java.beans.PropertyChangeSupport类. private PropertyChangeSupport changes = new PropertyChangeSupport(this); MyButton创建了一个名为changes的新对象,它是Propert

WPF/Silverlight深度解决方案:(一)解锁被Storyboard束缚的关联属性

原文 WPF/Silverlight深度解决方案:(一)解锁被Storyboard束缚的关联属性 如果您在使用WPF/Silverlight进行相关动画开发中使用了Storyboard,并对关联属性进行了动画修改,那么您是否有注意到这些关联属性常常无法再通过直接赋值的形式去修改,尽管它的值已经被更改,但却始终无法在界面上得以体现.例如,在我的C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial)教程里大量运用了Storyboard对角色的X,Y坐标关联属性以及角色

iOS运行时(Runtime)总结

声明 本博客中文章不会在此处再更新,只会在微信公众号中更新,请关注微信公众号,以获取最新的学习资源和更多学习资源.本博文末尾有微信公众号二维码,扫一扫添加关注. 原文出自:微信公众号iOSDevShares的文章 引言 相信很多同学都听过运行时,但是我相信还是有很多同学不了解什么是运行时,到底在项目开发中怎么用?什么时候适合使用?想想我们的项目中,到底在哪里使用过运行时呢?还能想起来吗?另外,在面试的时候,是否经常有笔试中要求运用运行时或者在面试时面试官会问是否使用过运行时,又是如何使用的? 回

iOS - OC RunTime 运行时

1.运行时 运行时是苹果提供的纯 C 语言的开发库,是一种非常牛逼.开发中经常用到的底层技术. 1.1 运行时原理 Objective-C 是一门简单的语言,95% 是 C,只是在语言层面上加了些关键字和语法,真正让 Objective-C 如此强大的是它的运行时.它很小但却很强大,它的核心是消息分发. 1.Messages 执行一个方法,有些语言,编译器会执行一些额外的优化和错误检查,因为调用关系很直接也很明显.但对于消息分发来说,就不那么明显了.在发消息前不必知道某个对象是否能够处理消息.你

hibernate annoation (八 关联映射)

onetoone:单向 1,主键关联: 在关联放使用@OneToOne sql语句:(类代码见同前面的代码) Java代码 create table A (id integer not null auto_increment, aname varchar(255), b_id integer, primary key (id)) create table B (id integer not null auto_increment, bname varchar(255), primary key

hibernate5(9)注解映射[1]多对一单向关联

在博客网站中,我们可能需要从某一篇文章找到其所关联的作者,这就需要从文章方建立起对用户的关联,即是多对一的映射关系. 现在先看一个配置实例:我们的文章实体类 package com.zeng.model; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue;

通过runtime打印出对象所有属性的值

通过runtime打印出对象所有属性的值 今天给给大家提供的关于NSObject的category,通过runtime打印属性的值,相当有用哦,以后你再也不用每个对象都通过NSLog来逐个打印属性值了. 源码: NSObject+Properties.h 与 NSObject+Properties.m // // NSObject+Properties.h // // Created by YouXianMing on 14-9-4. // Copyright (c) 2014年 YouXian

《Ember.js实战》——2.3 计算属性

2.3 计算属性 计算属性是一个函数,其返回一个从其他变量或表达式(也可以是其他计算属性)获取的值.计算属性与普通JavaScript函数之间的区别在于,Ember.js将计算属性看作其真正的属性.因此,就可以在计算属性上调用get()和set()等`方法,以及绑定/观察它们(观察者概念在本章稍后介绍).通常,在模型对象中定义计算属性,并在控制器和视图中使用它. 目前的记事本应用程序还没用上计算属性,但如果你想增强应用程序功能,在界面左边的事项列表中显示每条事项前20个字符的内容,那么,就请忘掉

当EF禁用延迟加载及代理时,即使采用Include导航属性仍为null,求解!

问题描述 当EF禁用延迟加载及代理时,即使采用Include导航属性仍为null,求解!假设如下代码:context.Configuration.LazyLoadingEnabled=false;context.Configuration.ProxyCreationEnabled=false;context.set(TEntity).Include(t=>t.Property).ToList();最终TEntity.Property是null,求解!我想实现一次性加载所有内容包含关联属性的值,且