你見過哪些令你瞠目結舌的 Objective-C 代碼技巧?

看到程序員主題下面好幾個類似問題,竟然沒有iOS開發的,特此請教



用 Property() 這個 macro 在編譯時檢查一個 class 是否包含一個 property,並取到那個 property 的名字(一個 NSString),配合 Core Data 使用非常方便。

// NSObject+PropertyName.h

#define Property(Class, PropertyName) @(((void)(NO ((void)[Class nilObject].PropertyName, NO)), # PropertyName))

@interface NSObject (PropertyName)

+ (instancetype)nilObject;

@end

// NSObject+PropertyName.m

#import "NSObject+PropertyName.h"

@implementation NSObject (PGPropertyName)

+ (instancetype)nilObject
{
return nil;
}

@end

還有就是用 hack 的方式隱藏一個 class 真正的 superclass。

// Foo.h

@interface Foo : NSObject

- (void)foo;

@end

// Foo.m

#import "Boo.h"

@interface Foo : Boo

@end

@implementation Foo

- (void)foo
{
NSLog(@"Hello, world!");
}

@end

Foo 真實的 superclass 是 Boo,但對於其它 import 了 Foo.h 的 class 來說,就是 NSObject。


N年前第一次看到這段代碼的時候驚呆了= =


去掉UINavigationBar底部的分割線

//導航欄背景透明

[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];

//導航欄底部線清楚

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

self.navigationController.navigationBar.translucent = YES;

[self.navigationController.navigationBar setShadowImage:[UIImage new]];

加了沒有圖的圖上去。

這樣的話,可以直接讓navigationBar和下面的視圖渾然一體。


Method Swizzle: Objective-C的hook方案(一): Method Swizzling

SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(tracking_viewWillAppear:);

Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));

if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}


WeakifySelf.h

#define weakifySelf(BLOCK...)
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored "-Wshadow"")
rs_blockWithWeakifiedSelf(self, ^id(__typeof(self) __weak self) {
return (BLOCK);
})
_Pragma("clang diagnostic pop")

#define strongify(VAR)
id _strong_##VAR = VAR;
__typeof(VAR) __strong VAR = _strong_##VAR;

#define strongifyAndReturnIfNil(VAR)
strongify(VAR)
if (!(VAR)){ return;}

id rs_blockWithWeakifiedSelf(id self, id(^intermediateBlock)(id __weak self));

WeakifySelf.m

#import "WeakifySelf.h"

id rs_blockWithWeakifiedSelf(id self, id(^intermediateBlock)(id __weak self)){
id __weak weakSelf = self;
return intermediateBlock(weakSelf);
}

用起來是這樣的

- (void)someMethod {
self.block = weakifySelf(^{
// Self may be nil here
[self doSomeWork];
strongifyAndReturnIfNil(self);
// Self is strong and not nil.
// We can do ivars dereferencing
// and other stuff safely
self.XXX = XXX;
});
}



不如授人以漁?

UI控制項上有任何費解搞不定的,或者想屏蔽哪些功能的,或要改一下顏色的

打開reveal(強烈推薦)或者Xcode 看看view hierarchy 用kvc或者runtime基本都能改

method swizzling這種靠腦洞的就不說了

(逃


關於weakify 和strongify的裝B寫法

傳統寫法

#ifndef weakify
#if __has_feature(objc_arc)

#define weakify( x ) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
autoreleasepool{} __weak __typeof__(x) __weak_##x##__ = x; \
_Pragma("clang diagnostic pop")

#else

#define weakify( x ) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
autoreleasepool{} __block __typeof__(x) __block_##x##__ = x; \
_Pragma("clang diagnostic pop")

#endif
#endif

#ifndef strongify
#if __has_feature(objc_arc)

#define strongify( x ) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
try{} @finally{} __typeof__(x) x = __weak_##x##__; \
_Pragma("clang diagnostic pop")

#else

#define strongify( x ) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
try{} @finally{} __typeof__(x) x = __block_##x##__; \
_Pragma("clang diagnostic pop")

#endif
#endif

裝B寫法

#define weakify(...) \
autoreleasepool {} \
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

#define strongify(...) \
try {} @finally {} \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
_Pragma("clang diagnostic pop")


利用runTime獲取私有屬性。

首先導入runtime

然後獲取一下UIButton的所有屬性,不管是暴露還是未暴露的。

這是printIvar的具體實現,核心就是利用runtime中的 class_copyIvarList這個方法。

===================以下是控制台列印的結果============================


1.Runtime

2.OC中使用GCC語法

self.searchBar =
({

UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:({


CGRect frame = self.tableView.frame;


frame.size.height = 50.0f;


frame;


})];


searchBar.delegate = self;


searchBar;


});

3.帶提醒功能的宏,refer: https://github.com/jspahrsummers/libextobjc

#define keypath(OBJ, PATH)

(((void)(NO ((void)OBJ.PATH, NO)), # PATH))


與其說令人瞠目結舌的編碼技巧,倒不如說有令人瞠目結舌的組織思路


我覺得一些複雜的邏輯卻用一些巧妙的設計簡單的實現了功能,而且很容易擴展,這個超牛逼!


推薦閱讀:

APICloud是免費用的嗎?
android v7包里的Toolbar,怎麼定製圖標、字體居中的效果?
互聯網上的各種軟體是怎麼盈利的?
我有一些想法,但不會開發APP,我希望有人可以開發一個APP,專門搜集一些別人的想法創意意見的APP,現實嗎?

TAG:程序員 | iOS開發 | iOSvsAndroid | 移動開發 |