在本文中,我们将给您介绍关于如何确定阻塞期间associatedObjectId代表什么对象?的详细内容,并且为您解答阻塞状态的相关问题,此外,我们还将为您提供关于adifferentobjectwi
在本文中,我们将给您介绍关于如何确定阻塞期间associatedObjectId代表什么对象?的详细内容,并且为您解答阻塞状态的相关问题,此外,我们还将为您提供关于a different object with the same identifier value was already associated with the session、angular – 无法解析AuthenticationService的所有参数:([object Object],?,[object Object])、Associated Objects、AssociatedObject关联对象原理实现的知识。
本文目录一览:- 如何确定阻塞期间associatedObjectId代表什么对象?(阻塞状态)
- a different object with the same identifier value was already associated with the session
- angular – 无法解析AuthenticationService的所有参数:([object Object],?,[object Object])
- Associated Objects
- AssociatedObject关联对象原理实现
如何确定阻塞期间associatedObjectId代表什么对象?(阻塞状态)
我从另一个团队那里得到了有关在SQL Server中进行阻止的报告。看结果
Exec sp_who2
并从Glenn Berry的博客中查询
SELECT blocking.session_id AS blocking_session_id ,blocked.session_id AS blocked_session_id ,waitstats.wait_type AS blocking_resource ,waitstats.wait_duration_ms ,waitstats.resource_description ,blocked_cache.text AS blocked_text ,blocking_cache.text AS blocking_text FROM sys.dm_exec_connections AS blocking INNER JOIN sys.dm_exec_requests blocked ON blocking.session_id = blocked.blocking_session_id CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle) blocked_cache CROSS APPLY sys.dm_exec_sql_text(blocking.most_recent_sql_handle) blocking_cache INNER JOIN sys.dm_os_waiting_tasks waitstats ON waitstats.session_id = blocked.session_id
我希望不能发现任何被阻止的东西。多次运行,我开始发现有些东西出现了,但是下次运行查询时,blcoking消失了。
我创建了临时表 SELECT INTO
,blocking_cache.text AS blocking_text INTO #TempBlockingTable FROM sys.dm_exec_connections AS blocking
之后,将查询修改为INSERT INTO SELECT
。现在,我可以运行查询多次,而不必担心结果会消失。
我一直运行查询大约10秒钟,直到最终得到一些结果。
SELECT * FROM #TempBlockingTable
看着resource_description
柱,从sys.dm_os_waiting_tasks
我发现,数据显示在下面的格式。
<type-specific-description> id=lock<lock-hex-address> mode=<mode> associatedObjectId=<associated-obj-id>
sys.dm_os_waiting_tasks
http://technet.microsoft.com/zh-
cn/library/ms188743.aspx上的Microsoft文档没有针对的定义associatedObjectId
答案1
小编典典答案实际上来自Aaron
Bertrand在Google网上论坛中找到的答案。要获得成功OBJECT_NAME
,associatedObjectId
您需要运行以下查询
SELECT OBJECT_NAME([object_id]) FROM sys.partitions WHERE partition_id = 456489945132196
此数字456489945132196
表示associatedObjectId
fromresource_description
列中from的值sys.dm_os_waiting_tasks
a different object with the same identifier value was already associated with the session
最近遇到一个很棘手的错误,请大牛们帮我看看。。。先谢谢各位了
项目采用spring来管理hibernate,我执行的操作是更新一条bean的信息到数据库(getHibernateTemplate().update(entity);
),然后就报了这个错误。
下面是错误的日志:
org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [infoair.data.model.FlightInformation#284722]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [infoair.data.model.FlightInformation#284722] at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:679) at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412) at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:411) at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374) at org.springframework.orm.hibernate3.HibernateTemplate.update(HibernateTemplate.java:705) at org.springframework.orm.hibernate3.HibernateTemplate.update(HibernateTemplate.java:701) at infoair.data.dao.impl.CommonDao.update(CommonDao.java:165) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196) at $Proxy78.update(Unknown Source) at infoair.data.service.impl.CommonService.basicUpdate(CommonService.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:108) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy79.basicUpdate(Unknown Source) at infoair.core.flight.event.impl.FlightArrive.doProcess(FlightArrive.java:52) at infoair.core.flight.event.impl.FlightArrive.execute(FlightArrive.java:38) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:108) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy83.execute(Unknown Source) at infoair.core.run.impl.FlightEventThread.run(FlightEventThread.java:72) at infoair.core.run.impl.FlightEventThreadTask.run(FlightEventThreadTask.java:41) at java.util.TimerThread.mainLoop(Timer.java:512) at java.util.TimerThread.run(Timer.java:462) Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [infoair.data.model.FlightInformation#284722] at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:613) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:307) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246) at org.hibernate.event.def.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:56) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) at org.hibernate.impl.SessionImpl.fireUpdate(SessionImpl.java:592) at org.hibernate.impl.SessionImpl.update(SessionImpl.java:580) at org.hibernate.impl.SessionImpl.update(SessionImpl.java:572) at org.springframework.orm.hibernate3.HibernateTemplate$14.doInHibernate(HibernateTemplate.java:708) at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406) ... 40 more
angular – 无法解析AuthenticationService的所有参数:([object Object],?,[object Object])
Can’t resolve all parameters for AuthenticationService: ([object Object],?,[object Object])
我已经检查了几乎每个主题,并尝试了多种方法来解决它,但仍然无法在第二天击败它.
我试图像这样在appService中注入第一个authService但是得到了同样的错误
@Inject(forwardRef(() => AuthenticationService)) public authService: AuthenticationService
我检查了所有DI和服务内部的导入顺序,在我看来一切都是正确的
如果有人可以帮我处理它,我很感激.
Angular 4.0.0
AuthService
import { Injectable } from '@angular/core'; import {Http,Headers,Response} from '@angular/http'; import 'rxjs/add/operator/toPromise'; import {Observable} from 'rxjs/Rx'; import {AppServices} from "../../app.services"; import {Router} from "@angular/router"; @Injectable() export class AuthenticationService { public token: any; constructor( private http: Http,private appService: AppServices,private router: Router ) { this.token = localStorage.getItem('token'); } login(username: string,password: string): Observable<boolean> { let headers = new Headers(); let body = null; headers.append("Authorization",("Basic " + btoa(username + ':' + password))); return this.http.post(this.appService.api + '/login',body,{headers: headers}) .map((response: Response) => { let token = response.json() && response.json().token; if (token) { this.token = token; localStorage.setItem('Conform_token',token); return true; } else { return false; } }); } logout(): void { this.token = null; localStorage.removeItem('Conform_token'); this.router.navigate(['/login']); } }
应用服务
import {Injectable} from '@angular/core'; import {Headers,Http,RequestOptions} from '@angular/http'; import {Router} from "@angular/router"; import {AuthenticationService} from "./auth/auth.service"; import 'rxjs/add/operator/toPromise'; import {Observable} from 'rxjs/Rx'; @Injectable() export class AppServices { api = '//endpoint/'; public options: any; constructor( private http: Http,private router: Router,public authService: AuthenticationService // doesn't work // @Inject(forwardRef(() => AuthenticationService)) public authService: AuthenticationService // doesn't work either ) { let head = new Headers({ 'Authorization': 'Bearer ' + this.authService.token,"Content-Type": "application/json; charset=utf8" }); this.options = new RequestOptions({headers: head}); } // ==================== // data services // ==================== getData(): Promise<any> { return this.http .get(this.api + "/data",this.options) .toPromise() .then(response => response.json() as Array<Object>) .catch((err)=>{this.handleError(err);}) }
应用模块
import { browserModule } from '@angular/platform-browser'; import { browserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import {BaseRequestOptions,HttpModule} from '@angular/http'; import { MaterialModule} from '@angular/material'; import {FlexLayoutModule} from "@angular/flex-layout"; import 'hammerjs'; import { routing,appRoutingProviders } from './app.routing'; import { AppServices } from './app.services'; import {AuthGuard} from "./auth/auth.guard"; import {AuthenticationService} from "./auth/auth.service"; import {AppComponent} from './app.component'; import {AuthComponent} from './auth/auth.component'; import {NotFoundComponent} from './404/not-found.component'; import { HomeComponent } from './home/home.component'; @NgModule({ declarations: [ AppComponent,AuthComponent,NotFoundComponent,HomeComponent ],imports: [ browserModule,browserAnimationsModule,FormsModule,HttpModule,routing,MaterialModule,FlexLayoutModule ],providers: [AppServices,AuthGuard,AuthenticationService],bootstrap: [AppComponent] }) export class AppModule { }
解决方法
你可以使用
export class AuthenticationService { public token: any; appService: AppServices; constructor( private http: Http,// private appService: AppServices,injector:Injector; private router: Router ) { setTimeout(() => this.appService = injector.get(AppServices)); this.token = localStorage.getItem('token'); }
另见DI with cyclic dependency with custom HTTP and ConfigService
要避免使用setTimeout,您还可以从AppService的构造函数中设置AuthenticationService.appService(或者相反)
Associated Objects
Written by Mattt Thompson on Feb 10th, 2014
#import <objc/runtime.h>
Objective-C developers are conditioned to be wary of whatever follows this ominous incantation. And for good reason: messing with the Objective-C runtime changes the very fabric of reality for all of the code that runs on it.
In the right hands, the functions of <objc/runtime.h>
have the potential to add powerful new behavior to an application or framework, in ways that would otherwise not be possible. In the wrong hands, it drains the proverbial sanity meter of the code, and everything it may interact with (withterrifying side-effects).
Therefore, it is with great trepidation that we consider this Faustian bargain, and look at one of the subjects most-often requested by NSHipster readers: associated objects.
Associated Objects—or Associative References, as they were originally known—are a feature of the Objective-C 2.0 runtime, introduced in Mac OS X 10.6 Snow Leopard (available in iOS 4). The term refers to the following three C functions declared in <objc/runtime.h>
, which allow objects to associate arbitrary values for keys at runtime:
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
Why is this useful? It allows developers to add custom properties to existing classes in categories, which is an otherwise notable shortcoming for Objective-C.
NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject) @property (nonatomic, strong) id associatedObject; @end
NSObject+AssociatedObject.m
@implementation NSObject (AssociatedObject) @dynamic associatedObject; - (void)setAssociatedObject:(id)object { objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id)associatedObject { return objc_getAssociatedObject(self, @selector(associatedObject)); }
It is often recommended that they key be a static char
—or better yet, the pointer to one. Basically, an arbitrary value that is guaranteed to be constant, unique, and scoped for use within getters and setters:
static char kAssociatedObjectKey; objc_getAssociatedObject(self, &kAssociatedObjectKey);
However, a much simpler solution exists: just use a selector.
Since SELs are guaranteed to be unique and constant, you can use _cmd as the key forobjc_setAssociatedObject(). #objective-c #snowleopard
— Bill Bumgarner (@bbum) August 28, 2009
Associative Object Behaviors
Values can be associated onto objects according to the behaviors defined by the enumerated typeobjc_AssociationPolicy
:
Behavior | @property Equivalent | Description |
---|---|---|
OBJC_ASSOCIATION_ASSIGN | @property (assign)or @property (unsafe_unretained) | Specifies a weak reference to the associated object. |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | @property (nonatomic, strong) | Specifies a strong reference to the associated object, and that the association is not made atomically. |
OBJC_ASSOCIATION_COPY_NONATOMIC | @property (nonatomic, copy) | Specifies that the associated object is copied, and that the association is not made atomically. |
OBJC_ASSOCIATION_RETAIN | @property (atomic, strong) | Specifies a strong reference to the associated object, and that the association is made atomically. |
OBJC_ASSOCIATION_COPY | @property (atomic, copy) | Specifies that the associated object is copied, and that the association is made atomically. |
Weak associations to objects made with OBJC_ASSOCIATION_ASSIGN
are not zero weak
references, but rather follow a behavior similar to unsafe_unretained
, which means that one should be cautious when accessing weakly associated objects within an implementation.
According to the Deallocation Timeline described in WWDC 2011, Session 322 (~36:00), associated objects are erased surprisingly late in the object lifecycle, in
object_dispose()
, which is invoked byNSObject -dealloc
.
Removing Values
One may be tempted to call objc_removeAssociatedObjects()
at some point in their foray into associated objects. However, as described in the documentation, it''s unlikely that you would have an occasion to invoke it yourself:
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 objc_setAssociatedObject with a nil value to clear an association.
Patterns
Adding private variables to facilitate implementation details. When extending the behavior of a built-in class, it may be necessary to keep track of additional state. This is the textbook use case for associated objects. For example, AFNetworking uses associated objects on its
UIImageView
category to store a request operation object, used to asynchronously fetch a remote image at a particular URL.Adding public properties to configure category behavior. Sometimes, it makes more sense to make category behavior more flexible with a property, than in a method parameter. In these situations, a public-facing property is an acceptable situation to use associated objects. To go back to the previous example of AFNetworking, its category on
UIImageView
, itsimageResponseSerializer
allows image views to optionally apply a filter, or otherwise change the rendering of a remote image before it is set and cached to disk.Creating an associated observer for KVO. When using KVO in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.
Anti-Patterns
Storing an associated object, when the value is not needed. A common pattern for views is to create a convenience method that populates fields and attributes based on a model object or compound value. If that value does not need to be recalled later, it is acceptable, and indeed preferable, not to associate with that object.
Storing an associated object, when the value can be inferred. For example, one might be tempted to store a reference to a custom accessory view''s containing
UITableViewCell
, for use intableView:accessoryButtonTappedForRowWithIndexPath:
, when this can retrieved by callingcellForRowAtIndexPath:
.Using associated objects instead of X, where X is any one the following:
Subclassing for when inheritance is a more reasonable fit than composition.
Target-Action for adding interaction events to responders.
Gesture Recognizers for any situations when target-action doesn''t suffice.
Delegation when behavior can be delegated to another object.
NSNotification & NSNotificationCenter for communicating events across a system in a loosely-coupled way.
Associated objects should be seen as a method of last resort, rather than a solution in search of a problem (and really, categories themselves really shouldn''t be at the top of the toolchain to begin with).
Like any clever trick, hack, or workaround, there is a natural tendency for one to actively seek out occasions to use it—especially just after learning about it. Do your best to understand and appreciate when it''s the right solution, and save yourself the embarrassment of being scornfully asked "why in the name of $DEITY" you decided to go with that solution.
TweetAssociatedObject关联对象原理实现
介绍
关联对象(AssociatedObject)是Objective-C 2.0运行时的一个特性,允许开发者对已经存在的类在扩展中添加自定义的属性。在实际生产过程中,比较常用的方式是给分类(Category)添加成员变量。
例子
#import <objc/runtime.h>
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id property;
@end
@implementation NSObject (AssociatedObject)
@dynamic property;
- (id)property {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setProperty:(NSString *)property {
objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
通过实现代码可以稍微分析下,objc_getAssociatedObject
拿着不变的指针地址(示例传入selector作为参数,实际是void*),从实例中获取需要的对象。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
相当于一个 weak reference
,但其实等于 assign/unsafe_unretained
。
对于与weak
的区别不在本文讨论范围内,浅显的区别在于变量释放后,weak
会把引用置空,unsafe_unretained
会保留内存地址,一旦获取可能会野指针闪退。
总结
我们知道,如果类要添加变量,只有在objc_allocateClassPair
与objc_registerClassPair
之间addIvar
。等类注册后,变量结构就不允许再被改变,这是为了防止两个相同类的实例拥有不同变量导致运行困惑。
那么在runtime时给实例添加变量,又不改变类内部变量结构,关联对象就是一个比较好的做法。
关联对象的实现
外部方法
//Sets an associated value for a given object using a given key and association policy.
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.
id objc_getAssociatedObject(id object, const void * key);
//Removes all associations for a given object.
void objc_removeAssociatedObjects(id object);
相比刚刚例子中的用法,多了一个objc_removeAssociatedObjects
,那么可不可以用这个方法来删除不用的关联对象呢?
苹果的文档中解释说这个方法主要用来还原对象到类初始的状态,会移除所有的关联,包括其他模块添加的,因此应该用 objc_setAssociatedObject(..,nil,..)
的方式去卸载。
Setter实现
objc_setAssociatedObject
实际调用的是_object_set_associative_reference
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
内存管理
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
return objc_release(value);
}
}
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
old_association = ...
}
if (old_association.hasValue()) ReleaseValue()(old_association);
我们摘出与对象内存相关的代码仔细分析下,首先把新传入的对象,根据协议进行retain/copy
,在赋值的过程中获取旧值,在方法结束前release
。
赋值
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
//需要赋值
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
//找到了这个对象的关联表
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
//找到了这个key的关联对象
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
//没找到,新增一个关联
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
//没找到,创建一个新的关联表
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
}
先了解一下AssociationsManager
与AssociationsHashMap
class AssociationsManager {
static AssociationsHashMap *_map;
public:
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator>;
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>;
AssociationsManager
通过一个以指针地址为主键,值为关联表的哈希表,来管理应用内所有的关联对象。
首先以对象的指针地址去寻找关联表,再通过指定的键值查找关联关系,从而获取关联对象。
删除
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
和修改方法类似,找到关联关系后,执行哈希表的erase
方法删除。
Getter实现
objc_getAssociatedObject
实际调用的是_object_get_associative_reference
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
查找哈希表的方法和Setter一样,区别在于如果策略中需要retain和autorelease的话,都需要处理。那么是怎么约定这些策略呢?
enum {
OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
OBJC_ASSOCIATION_SETTER_RETAIN = 1,
OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
};
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
OBJC_ASSOCIATION_RETAIN = 01401
,其中01401
开头是0
,所以是八进制数字,翻译为二进制就是0000 0011 0000 0001
,取位判断就是OBJC_ASSOCIATION_SETTER_RETAIN
OBJC_ASSOCIATION_GETTER_RETAIN
OBJC_ASSOCIATION_GETTER_AUTORELEASE
。
在保存的时候,需要retain
,在获取的时候,需要先retain
增加引用计数,再执行autorelease
等待释放,从而实现原子性。
Remove实现
objc_removeAssociatedObjects
会判断对象是否存在关联,然后再执行_object_set_associative_reference
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
实现方式也可以看出为什么在介绍里不推荐使用,因为会遍历所有的关联对象,并且全部释放,可能会造成别的模块功能缺陷。
判断关联对象
比较有意思的是判断对象是否有关联对象的实现。
inline bool objc_object::hasAssociatedObjects()
{
if (isTaggedPointer()) return true;
if (isa.nonpointer) return isa.has_assoc;
return true;
}
inline void objc_object::setHasAssociatedObjects()
{
if (isTaggedPointer()) return;
retry:
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
if (!newisa.nonpointer || newisa.has_assoc) {
ClearExclusive(&isa.bits);
return;
}
newisa.has_assoc = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
默认返回的结果都是true
,只有在64位系统下,才保存一个标记位。这么处理我推测是为了加快释放周期速度,在析构对象时,会根据这个方法判断是否需要释放关联对象。试想如果每次都查询哈希表,执行效率必定会降低,不如都先通过,之后再做处理。
关于
nonpointer
不在本文介绍范围内,简单描述为在64位系统下,指针地址保存不仅仅为内存地址,还存有其他标记信息,包括本文涉及的has_assoc
。
taggedPointer
是一种优化策略,把简单的数字或字符串信息直接保存在指针地址中,从而不申请额外内存加快运行效率。
总结
关联对象的实现不复杂,保存的方式为一个全局的哈希表,存取都通过查询表找到关联来执行。哈希表的特点就是牺牲空间换取时间,所以执行速度也可以保证。
问答
关联对象有什么应用?
关联对象可以在运行时给指定对象绑定一个有生命周期的变量。
1.由于不改变原类的实现,所以可以给原生类或者是打包的库进行扩展,一般配合Category实现完整的功能。
2.ObjC类定义的变量,由于runtime的特性,都会暴露到外部,使用关联对象可以隐藏关键变量,保证安全。
3.可以用于KVO,使用关联对象作为观察者,可以避免观察自身导致循环。
系统如何管理关联对象?
系统通过管理一个全局哈希表,通过对象指针地址和传递的固定参数地址来获取关联对象。根据setter
传入的参数协议,来管理对象的生命周期。
其被释放的时候需要手动将其指针置空么?
当对象被释放时,如果设置的协议是OBJC_ASSOCIATION_ASSIGN
,那么他的关联对象不会减少引用计数,其他的协议都会减少从而释放关联对象。
unsafe_unretain
一般认为外部有对象控制,所以对象不用处理,因此不管什么协议,对象释放时都无需手动讲关联对象置空。
关于如何确定阻塞期间associatedObjectId代表什么对象?和阻塞状态的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于a different object with the same identifier value was already associated with the session、angular – 无法解析AuthenticationService的所有参数:([object Object],?,[object Object])、Associated Objects、AssociatedObject关联对象原理实现的相关信息,请在本站寻找。
本文标签: