When you say str = @"abcd", you're not using a code pattern that the compiler recognizes as returning a newly-allocated object, so you don't trigger the warning about a direct assignment of a new object to a __weak variable.
Furthermore, a string literal like @"abcd" is stored in your program's executable file. It's never deallocated. The retain and release operations don't actually change its retain count. Its retain count is set to a magic number indicating an immortal object. So your __weak variable str doesn't actually get set to nil, because the object it references doesn't get deallocated. That's why it prints abcd.
In fact, clang specifically suppresses a warning if you assign a string literal (as opposed to some other kind of literal like an array literal @[a, b, c]). See the comment in the clang source code:
static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
Expr *RHS, bool isProperty) {
// Check if RHS is an Objective-C object literal, which also can get
// immediately zapped in a weak reference. Note that we explicitly
// allow ObjCStringLiterals, since those are designed to never really die.
RHS = RHS->IgnoreParenImpCasts();
// This enum needs to match with the 'select' in
// warn_objc_arc_literal_assign (off-by-1).
Sema::ObjCLiteralKind Kind = S.CheckLiteralKind(RHS);
if (Kind == Sema::LK_String || Kind == Sema::LK_None)
return false;
S.Diag(Loc, diag::warn_arc_literal_assign)
<< (unsigned) Kind
<< (isProperty ? 0 : 1)
<< RHS->getSourceRange();
return true;
}
So if we change the type to NSArray and use an array literal, we get a warning:

Moving on… You get the warning when you say str = [[NSString alloc] init] because the compiler recognizes that [[NSString alloc] init] is a code pattern that typically returns a new object.
However, in the particular case of [[NSString alloc] init], you'll discover that str again doesn't get set to nil. That's because -[NSString init] is special-cased to return a global empty-string object. It doesn't actually make a new object on each call.
__weak NSString *str;
str = [[NSString alloc] init];
NSLog(@"%ld %p [%@]", CFGetRetainCount((__bridge CFTypeRef)str), str, str);
Output:
2018-01-24 01:00:22.963109-0600 test[3668:166594] 1152921504606846975 0x7fffe55b19c0 []
That 1152921504606846975 is the magic retain count indicating an immortal object.