흑마술은 용법용량을 지켜 올바르게 사용해 주세요
17516 단어 Objective-C검은 마술
소개
이제 Objective-C로 미안해.
Objective-C의 흑마술 「objc/runtime.h」
runtimeAPI는 메소드의 거동을 바꾸거나 매우 강력한 흑마술입니다.
흑마술에 대해서는 하기
[Objective-C] 런타임 API 메모
흑마술의 유효 활용
사용자 정보를 관리하는 User
라는 모델 클래스가 있다고 가정합니다.
User.hNS_ASSUME_NONNULL_BEGIN
@interface User : NSObject
@property (nonatomic, strong) NSString *userId;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, nullable, strong) NSString *address;
@property (nonatomic) NSInteger age;
- (instancetype)initWithUserData:(NSDictionary<NSString *, NSString *> *)userData;
@end
NS_ASSUME_NONNULL_END
User.m#import "User.h"
@implementation User
- (instancetype)initWithUserData:(NSDictionary<NSString *, NSString *> *)userData {
self = [super init];
if (self) {
_userId = userData[@"userId"];
_userName = userData[@"userName"];
_address = userData[@"address"];
_age = [userData[@"age"] integerValue];
}
return self;
}
@end
이것을 그대로 NSLog
로 표시하면 객체의 포인터가 표시됩니다.
- (void)createUser {
NSDictionary *tarouData = @{@"userId": @"001"
, @"userName": @"田中太郎"
, @"address": @"東京都"
, @"age": @25};
User *tarou = [[User alloc] initWithUserData:tarouData];
NSLog(@"%@", tarou);
NSDictionary *hanakoData = @{@"userId": @"002"
, @"userName": @"山田花子"
, @"age": @23};
User *hanako = [[User alloc] initWithUserData:hanakoData];
NSLog(@"%@", hanako);
}
포인터가 아니고, userName
이나 address
의 내용이 보고 싶을 때는 user.userName
라고 변수를 지정해 NSLog 를 내야 합니다.
조금 귀찮습니다.
그래서 objc/runtime.h
<objc/runtime.h>
를 가져오고 User
클래스의 description
메소드를 아래와 같이 기술합니다.
User.m#import <objc/runtime.h>
- (NSString *)description {
NSMutableString *description = [NSMutableString string];
[description appendString:@"{\n"];
unsigned int outCount, i;
// 自身が持つPropertyの一覧を取得
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
// 文字列変換
NSString *propertyName = [NSString stringWithUTF8String:name];
// KVCを使ってPropertyの中身を取得
NSString *propertyValue = [self valueForKey:propertyName];
[description appendFormat:@"\t%@: %@", propertyName, propertyValue];
[description appendString:@"\n"];
}
free(properties);
[description appendString:@"}\n"];
return description;
}
@end
그리고 이전 코드를 다시 실행하면 ,,
Property의 내용이 표시됩니다.
이것이라면 Property가 증가해도 자동으로 출력 항목이 증가하기 때문에 편리합니다.
여기에서 본제
저는 SES에서 Objective-C를 메인으로 개발하고 있습니다만, 지금의 현장에서 이런 코드를 만났습니다.
NSUSerDefaults+Category@interface NSUserDefaults(Category)
+(void)switchStringForKey;
@end
@implementation NSUserDefaults(Category)
+(void)switchStringForKey {
[self switchInstanceMethodFrom:@selector(stringForKey:) To:@selector(nonNilStringForKey:)];
}
- (NSString *)nonNilStringForKey:(NSString *)defaultName{
NSString *str = [self nonNilStringForKey:defaultName];
if (str) {
return str;
} else {
NSLog(@"String nil key: %@", defaultName);
return @"";
}
}
+(void)switchInstanceMethodFrom:(SEL)from To:(SEL)to {
Method fromMethod = class_getInstanceMethod(self,from);
Method toMethod = class_getInstanceMethod(self,to );
method_exchangeImplementations(fromMethod, toMethod);
}
무엇을 하고 있는가 하면(자) NSUserDefaults
의 stringForKey:
의 메소드를 옮겨놓아(Method Swizzling)
절대로 nil이 반환되지 않도록하고 있습니다. ← 여기 중요
왜 이런 코드가 있는지 궁금했지만, 코드를 읽을수록 이유가 밝혀졌습니다.
자세한 것은 걸리지 않습니다만, 앱내 사용하는 캐릭터 라인을 한 번 모두 NSUserDefaults에 세트 해 각 화면에서 NSUserDefaults로부터 캐릭터 라인을 취득해 묘화 하고 있었습니다.
꽤 똥 코드 독특한 코드 네요.
그러한 처리가 되어 있으므로, StringForKey:
로 취득할 수 없었을 때에, 화면에 (null)
라고 표시되는 것을 피하기 위한 Method Swizzling
입니다.
검은 마술이 폭발했습니다.
그런 가운데 9월의 끝 정도에, 어떤 팀의 분들이 시끄러웠습니다.
분명히 iOS10
에서 카메라 롤을 보면 앱이 떨어지는 것 같습니다.
저도 도움으로 원인 조사를 실시하고 있었습니다만, 카메라 롤을 표시할 때에 신경이 쓰이는 로그를 발견했습니다.
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ほげほげ
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ふがふが
2016-12-22 23:36:46.550 hogehoge[5750:242757] String nil key: com.apple.CoreData.ほげふが
※ 표시 내용을 일부 흐리게 하고 있습니다
어라? 이거 검은 마술의 부분이지?
물건은 시험과 Method Swizzling
의 처리를 제외한 곳에 카메라 롤이 떨어지지 않게 되지 않습니까! ! !
뭐가 문제였는지
Apple 문서
Returns nil if the default does not exist or is not a string or number value.
Method Swizzling
로 옮겨놓은 처리는 본래 존재하지 않는 Value에 액세스 하면(자), nil
를 돌려주는 것이 됩니다.
그리고 카메라 롤을 표시할 때 UserDefaultsにアクセスしてnilかどうかで処理を分岐している箇所があり、その部分でクラッシュしていた
로 예측됩니다.
어떻게 대책했는가
이번 예에서 말하면 기존의 처리를 바꾸어 버린 것이 직접적인 요인이었기 때문에 nonNilStringForKey
를 폐지해 원래대로 nil
를 돌려주도록 했습니다.
애초에 이번 사례는.
또 전체 화면에서 nonNilStringForKey
가 사용되고 있어 간단하게 치환을 할 수 없는 상태였으므로 나도 방치해 버렸습니다.
원래 StringForKey
의 사용법이 되어 있지 않다고 하는 곳은 있습니다만
끝에
NSUserDefaults
는 매우 강력한 함수이므로, 여러분 용법용량을 올바르게 지키고 흑마술을 사용합시다.
Reference
이 문제에 관하여(흑마술은 용법용량을 지켜 올바르게 사용해 주세요), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/sasho/items/318e28431ddd0338149d
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
runtimeAPI는 메소드의 거동을 바꾸거나 매우 강력한 흑마술입니다.
흑마술에 대해서는 하기
[Objective-C] 런타임 API 메모
흑마술의 유효 활용
사용자 정보를 관리하는
User
라는 모델 클래스가 있다고 가정합니다.User.h
NS_ASSUME_NONNULL_BEGIN
@interface User : NSObject
@property (nonatomic, strong) NSString *userId;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, nullable, strong) NSString *address;
@property (nonatomic) NSInteger age;
- (instancetype)initWithUserData:(NSDictionary<NSString *, NSString *> *)userData;
@end
NS_ASSUME_NONNULL_END
User.m
#import "User.h"
@implementation User
- (instancetype)initWithUserData:(NSDictionary<NSString *, NSString *> *)userData {
self = [super init];
if (self) {
_userId = userData[@"userId"];
_userName = userData[@"userName"];
_address = userData[@"address"];
_age = [userData[@"age"] integerValue];
}
return self;
}
@end
이것을 그대로
NSLog
로 표시하면 객체의 포인터가 표시됩니다.- (void)createUser {
NSDictionary *tarouData = @{@"userId": @"001"
, @"userName": @"田中太郎"
, @"address": @"東京都"
, @"age": @25};
User *tarou = [[User alloc] initWithUserData:tarouData];
NSLog(@"%@", tarou);
NSDictionary *hanakoData = @{@"userId": @"002"
, @"userName": @"山田花子"
, @"age": @23};
User *hanako = [[User alloc] initWithUserData:hanakoData];
NSLog(@"%@", hanako);
}
포인터가 아니고,
userName
이나 address
의 내용이 보고 싶을 때는 user.userName
라고 변수를 지정해 NSLog 를 내야 합니다.조금 귀찮습니다.
그래서 objc/runtime.h
<objc/runtime.h>
를 가져오고 User
클래스의 description
메소드를 아래와 같이 기술합니다.User.m
#import <objc/runtime.h>
- (NSString *)description {
NSMutableString *description = [NSMutableString string];
[description appendString:@"{\n"];
unsigned int outCount, i;
// 自身が持つPropertyの一覧を取得
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
// 文字列変換
NSString *propertyName = [NSString stringWithUTF8String:name];
// KVCを使ってPropertyの中身を取得
NSString *propertyValue = [self valueForKey:propertyName];
[description appendFormat:@"\t%@: %@", propertyName, propertyValue];
[description appendString:@"\n"];
}
free(properties);
[description appendString:@"}\n"];
return description;
}
@end
그리고 이전 코드를 다시 실행하면 ,,
Property의 내용이 표시됩니다.
이것이라면 Property가 증가해도 자동으로 출력 항목이 증가하기 때문에 편리합니다.
여기에서 본제
저는 SES에서 Objective-C를 메인으로 개발하고 있습니다만, 지금의 현장에서 이런 코드를 만났습니다.
NSUSerDefaults+Category@interface NSUserDefaults(Category)
+(void)switchStringForKey;
@end
@implementation NSUserDefaults(Category)
+(void)switchStringForKey {
[self switchInstanceMethodFrom:@selector(stringForKey:) To:@selector(nonNilStringForKey:)];
}
- (NSString *)nonNilStringForKey:(NSString *)defaultName{
NSString *str = [self nonNilStringForKey:defaultName];
if (str) {
return str;
} else {
NSLog(@"String nil key: %@", defaultName);
return @"";
}
}
+(void)switchInstanceMethodFrom:(SEL)from To:(SEL)to {
Method fromMethod = class_getInstanceMethod(self,from);
Method toMethod = class_getInstanceMethod(self,to );
method_exchangeImplementations(fromMethod, toMethod);
}
무엇을 하고 있는가 하면(자) NSUserDefaults
의 stringForKey:
의 메소드를 옮겨놓아(Method Swizzling)
절대로 nil이 반환되지 않도록하고 있습니다. ← 여기 중요
왜 이런 코드가 있는지 궁금했지만, 코드를 읽을수록 이유가 밝혀졌습니다.
자세한 것은 걸리지 않습니다만, 앱내 사용하는 캐릭터 라인을 한 번 모두 NSUserDefaults에 세트 해 각 화면에서 NSUserDefaults로부터 캐릭터 라인을 취득해 묘화 하고 있었습니다.
꽤 똥 코드 독특한 코드 네요.
그러한 처리가 되어 있으므로, StringForKey:
로 취득할 수 없었을 때에, 화면에 (null)
라고 표시되는 것을 피하기 위한 Method Swizzling
입니다.
검은 마술이 폭발했습니다.
그런 가운데 9월의 끝 정도에, 어떤 팀의 분들이 시끄러웠습니다.
분명히 iOS10
에서 카메라 롤을 보면 앱이 떨어지는 것 같습니다.
저도 도움으로 원인 조사를 실시하고 있었습니다만, 카메라 롤을 표시할 때에 신경이 쓰이는 로그를 발견했습니다.
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ほげほげ
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ふがふが
2016-12-22 23:36:46.550 hogehoge[5750:242757] String nil key: com.apple.CoreData.ほげふが
※ 표시 내용을 일부 흐리게 하고 있습니다
어라? 이거 검은 마술의 부분이지?
물건은 시험과 Method Swizzling
의 처리를 제외한 곳에 카메라 롤이 떨어지지 않게 되지 않습니까! ! !
뭐가 문제였는지
Apple 문서
Returns nil if the default does not exist or is not a string or number value.
Method Swizzling
로 옮겨놓은 처리는 본래 존재하지 않는 Value에 액세스 하면(자), nil
를 돌려주는 것이 됩니다.
그리고 카메라 롤을 표시할 때 UserDefaultsにアクセスしてnilかどうかで処理を分岐している箇所があり、その部分でクラッシュしていた
로 예측됩니다.
어떻게 대책했는가
이번 예에서 말하면 기존의 처리를 바꾸어 버린 것이 직접적인 요인이었기 때문에 nonNilStringForKey
를 폐지해 원래대로 nil
를 돌려주도록 했습니다.
애초에 이번 사례는.
또 전체 화면에서 nonNilStringForKey
가 사용되고 있어 간단하게 치환을 할 수 없는 상태였으므로 나도 방치해 버렸습니다.
원래 StringForKey
의 사용법이 되어 있지 않다고 하는 곳은 있습니다만
끝에
NSUserDefaults
는 매우 강력한 함수이므로, 여러분 용법용량을 올바르게 지키고 흑마술을 사용합시다.
Reference
이 문제에 관하여(흑마술은 용법용량을 지켜 올바르게 사용해 주세요), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/sasho/items/318e28431ddd0338149d
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
@interface NSUserDefaults(Category)
+(void)switchStringForKey;
@end
@implementation NSUserDefaults(Category)
+(void)switchStringForKey {
[self switchInstanceMethodFrom:@selector(stringForKey:) To:@selector(nonNilStringForKey:)];
}
- (NSString *)nonNilStringForKey:(NSString *)defaultName{
NSString *str = [self nonNilStringForKey:defaultName];
if (str) {
return str;
} else {
NSLog(@"String nil key: %@", defaultName);
return @"";
}
}
+(void)switchInstanceMethodFrom:(SEL)from To:(SEL)to {
Method fromMethod = class_getInstanceMethod(self,from);
Method toMethod = class_getInstanceMethod(self,to );
method_exchangeImplementations(fromMethod, toMethod);
}
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ほげほげ
2016-12-22 23:36:46.545 hogehoge[5750:242757] String nil key: com.apple.CoreData.Logging.ふがふが
2016-12-22 23:36:46.550 hogehoge[5750:242757] String nil key: com.apple.CoreData.ほげふが
NSUserDefaults
는 매우 강력한 함수이므로, 여러분 용법용량을 올바르게 지키고 흑마술을 사용합시다.
Reference
이 문제에 관하여(흑마술은 용법용량을 지켜 올바르게 사용해 주세요), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/sasho/items/318e28431ddd0338149d텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)