慎用公共變量

謹~~慎

前言

  • 在開發(fā)過程中饶套,避免不了會使用公共變量垒探,記錄共享對象狀態(tài)、數據最簡單的方式就是創(chuàng)建創(chuàng)建公共變量蛤克;
  • 當業(yè)務邏輯變多构挤,還采用這種思想就會變得危險,代碼邏輯變得不清晰筋现,慢慢就有一種代碼壞味道矾飞。
  • 具體總結如下:
1、過多邏輯分支豹绪,不夠清晰申眼,公共變量不利于系統(tǒng)維護和項目拓展括尸;
2、安全性收到威脅钓辆,過多地方共享變量肴焊,變量的寫入和讀取在多線程下是危險的功戚;
3啸臀、業(yè)務邏輯交叉過多時,很難保證數據-邏輯的一致性豌注;

如何解決呢灯萍?

  • 出現問題旦棉,解決問題药薯,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;

舉個栗子
  • 在開發(fā)工程中芭碍,給UIView添加單擊手勢是非常常見的需求。假定孽尽,現在我們就要動態(tài)地將一個Tap手勢操作連接到任何UIView中窖壕,并且根據需要指定點擊后的實際操作;
  • 這時候我們就可以將一個手勢對象及操作的Block對象關聯到我們的UIView對象中杉女。這項任務分為一下兩部分瞻讽。
  • 首先,如果需要熏挎,我們要創(chuàng)建一個手勢識別對象并將它及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);
}
  • 這段代碼檢測了手勢識別的關聯對象。如果沒有坎拐,則創(chuàng)建并建立關聯關系烦磁。同時,將傳入的塊對象連接到指定的key上哼勇。注意Block對象的關聯內存管理策略-Copy;
  • 然后都伪,處理單擊事件,具體實現如下:
- (void) JL_handleActionForTapGesture:(UITapGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateRecognized)
    {
        void(^action)(void) = objc_getAssociatedObject(self, kJLActionHandlerTapBlockKey);
 
        if (action)
        {
            action();
        }
    }
}
  • 我們需要檢測手勢識別對象的狀態(tài)猴蹂,因為我們只需要在點擊手勢被識別出來時才執(zhí)行操作院溺。
  • 通過上面可以看到,關聯對象實現起來也不是很復雜磅轻,而且還可以動態(tài)的增強類現有的功能。
優(yōu)化完善
  • 但是逐虚,還是有一點不太完美聋溜,代碼過于松散,按照上述的方式去應用到項目中叭爱,會寫不少重復代碼撮躁,我們需要封裝一下,并不暴露#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類的參數就可以了,不需要const void * _Nonnull key杨帽,接口方法變得更優(yōu)雅簡潔一些。

  • 用封裝的方法重寫上述方法:
//定義綁定對象的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();
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末嗤军,一起剝皮案震驚了整個濱河市注盈,隨后出現的幾起案子,更是在濱河造成了極大的恐慌叙赚,老刑警劉巖老客,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異震叮,居然都是意外死亡胧砰,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門苇瓣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尉间,“玉大人,你說我怎么就攤上這事击罪≌艹埃” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵外邓,是天一觀的道長撤蚊。 經常有香客問我,道長损话,這世上最難降的妖魔是什么侦啸? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮丧枪,結果婚禮上光涂,老公的妹妹穿的比我還像新娘。我一直安慰自己拧烦,他們只是感情好忘闻,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恋博,像睡著了一般齐佳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上债沮,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天炼吴,我揣著相機與錄音,去河邊找鬼疫衩。 笑死硅蹦,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播童芹,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼涮瞻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了假褪?” 一聲冷哼從身側響起署咽,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗜价,沒想到半個月后艇抠,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡久锥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年家淤,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑟由。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡絮重,死狀恐怖,靈堂內的尸體忽然破棺而出歹苦,到底是詐尸還是另有隱情青伤,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布殴瘦,位于F島的核電站狠角,受9級特大地震影響,放射性物質發(fā)生泄漏蚪腋。R本人自食惡果不足惜丰歌,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屉凯。 院中可真熱鬧立帖,春花似錦、人聲如沸悠砚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灌旧。三九已至绑咱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枢泰,已是汗流浹背羡玛。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宗苍,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像讳窟,于是被迫代替她去往敵國和親让歼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容