objective-cpropertiesobjective-c-blocksobjective-c-runtimeobjective-c-category

OC @property with block in category


excuse me. I want to use block as my property in category to change my code style as follows, but there is something wrong and I don't know why.
Here is my code :
```

typedef NSString* (^MethodreplacingRangeWithString)(NSRange range,NSString * string);
typedef NSString* (^MethodAppend)(NSString *) ;
@interface NSString (Speech)
@property(nonatomic ,copy)MethodreplacingRangeWithString replacingRangeWithString ;
@property(nonatomic, copy)MethodAppend append ;
+(void)speech:(NSString *)content;
@end

@implementation NSString (Speech)

//setter and getter
static NSString * a = @"replacingRangeWithString" ;
-(void)setReplacingRangeWithString:(MethodreplacingRangeWithString)replacingRangeWithString{
    objc_setAssociatedObject(self, @selector(replacingRangeWithString), replacingRangeWithString, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
-(MethodreplacingRangeWithString)replacingRangeWithString{
    return objc_getAssociatedObject(self, @selector(replacingRangeWithString)) ;
}

//setter and getter
static NSString * b = @"append" ;
-(void)setAppend:(MethodAppend)append{
    objc_setAssociatedObject(self, @selector(append), append,OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
-(MethodAppend)append{
    return objc_getAssociatedObject(self, @selector(append)) ;
}
//block
-(void)configureReplacingRangeWithStringProperty{
    __weak typeof (self) weakSelf = self ;
    self.replacingRangeWithString =  ^(NSRange range,NSString * str){
        return [weakSelf stringByReplacingCharactersInRange:range withString:str];
    };
}
-(void)configureAppend{
    __weak typeof (self)weakSelf = self ;
    self.append = ^(NSString *str){
        return [weakSelf stringByAppendingString:str] ;
    };
}

to change the style as follows :

NSString * str = @"hello world" ;
        [str configureAppend] ;
        [str configureReplacingRangeWithStringProperty] ;
        str = str.replacingRangeWithString(NSMakeRange(6, 5),@"iOS").append(@" hhhhh") ;

```

here is sth wrong with my configures and I don't know why


Solution

  • The cause of the actual crash you are getting is probably down to a misunderstanding of associated objects. When you associate an object you do so to a specific instance, not to all instances of the same type.

    Looking at your code:

    [str configureAppend] ;
    [str configureReplacingRangeWithStringProperty] ;
    

    At this point you have associated two objects with the particular instance that str is referring to. Now you try to do:

    str = str.replacingRangeWithString(NSMakeRange(6, 5),@"iOS").append(@" hhhhh") ;
    

    Breaking this up:

    str.replacingRangeWithString
    

    This invokes the property replacingRangeWithString on whatever object instance str is referencing. Let's call that object A. Now your previous two statements associated objects with A, so this works and you get your block reference back.

    str.replacingRangeWithString(NSMakeRange(6, 5),@"iOS")
    

    This invokes the block and returns a different string, call that object B.

    str.replacingRangeWithString(NSMakeRange(6, 5),@"iOS").append
    

    This invokes the property append on object B, you have associated no objects with object B so this returns a null reference.

    str.replacingRangeWithString(NSMakeRange(6, 5),@"iOS").append(@" ahhhh")
    

    You attempt to call your "block", but you have a null reference => memory fault.

    Update

    At this point I originally suggested that you need to rethink your design - you do - and that a solution might not be as simple as you might like - and since then it dawned on me that there might be a simple approach...

    Provided you are just trying to replace method calls with property accesses so you can neatly chain calls together; i.e. that you've no intention of calling your configure... methods with blocks which perform different operations; then you can dynamically create and return a block in your property. Here is the outline for append. First make the property read only:

    @property(nonatomic, readonly) MethodAppend append;
    

    and then define it using:

    -(MethodAppend)append
    {
       NSString *copied = self.copy;
       return ^(NSString *string){ return [copied stringByAppendingString:string]; };
    }
    

    This first copies the string in case the instance it is called on is an NSMutableString, you don't know how long after this property is invoked that the block will be called and the string value could have been changed by that point. Then it returns a block which accepts the required arguments and calls the intended method.

    That is all that is required, no setters or configure methods, and no associate objects.

    HTH